目录
函数
函数三要素:功能、参数、返回值
格式:
[存储类型] 数据类型 函数名(形参列表)
{
函数体;
return 常量或变量或表达式;
}
常量或变量或表达式:数据类型要和定义函数的数据类型保持一致
当函数不需要返回值时,函数的数据类型是void,return语句可以没有
举例:
函数的声明
数据类型 函数名(形参列表)
函数的调用:函数名(实参)
1)不需要接收返回值和参数
2)不需要返回值,需要参数
3)需要返回值,不需要参数
4)需要参数,需要返回值
形参和实参区别:
形参是函数定义时,定义的形参变量。是形式上存在的参数,只有在电泳函数时才会开辟内存空间。
实参是调用函数时,实际传递的值。实际存在的值。
练习:
编写一个函数,函数的2个参数,第一个是一个字符,第二个是一个char *,返回字符串中该字符的个数。
参考:
返回字符串中某一字符在此字符串中首次出现的位置下标
函数传参
值传递
单向传递,把实参传递给形参使用,改变形参,实参不受影响
(把值复制一份传递过去,对复制的内容进行了修改,原内容不变)
地址传递
双向传递,在函数中修改形参,实参会一起改变
(把变量的地址传递过去,通过地址对原内容修改)
数组传递
和地址传递一样,参数中如果存在数组的定义,也会认为是指针
(本质也会认为是地址传递,传递的是数组的首地址(一维数组))
sizeof==4
开辟堆区空间
malloc
#include <stdlib.h>
void *malloc(size_t size);
功能:在堆区开辟空间
参数:开辟的空间大小
返回值:成功:返回开辟的堆区空间首地址
失败:NULL
void free(void *ptr);
功能:释放堆区空间
参数:堆区空间首地址
返回值:无
free(p);
p=NULL;
注意:
1.手动开辟堆区空间,要注意内存泄漏
2.使用完堆区空间后及时释放空间
思考:如下代码输出结果。
void fun(char *p)
{
p = (char *)malloc(32);
strcpy(p, "hello");
}
main()
{
char *m = NULL;
fun(m);
printf("%s\n", m);
}
代码出现段错误,原因?
思考下面代码能否正确运行,为什么?
1.
2
.
string函数族
strcpy
#include <string.h>
strlen
strcpy
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:字符串的复制
参数:把src指向的字符串复制到dest;复制的同时会复制'\0'
dest:目标字符串首地址
src:原字符串首地址
返回值:目标字符串首地址
strncpy
char *strncpy(char *dest, const char *src, size_t n);
功能:字符串的复制
参数:把src指向的字符串复制到dest;复制的同时会复制'\0'
dest:目标字符串首地址
src:原字符串首地址
n:字符个数
返回值:目标字符串首地址
补充面试题:
strcat
#include <string.h>
char *strcat(char *dest, const char *src);
功能:字符串拼接 把dest后边的'\0'去掉后拼接src
参数:dest拼接的字符串存放位置
src:要拼接的字符串
返回值:拼接后的字符串首地址
char *strncat(char *dest, const char *src, size_t n); //拼接src的前n个字符
strcmp
#include <string.h>
int strcmp(const char *s1, const char *s2);
功能:字符串的比较
参数:要比较的两个字符串的首地址
返回值:从字符串首个字符开始进行比较ASCII码值,如果相等会继续向下判断
1 s1>s2
-1 s1<s2
0 s1=s2
递归函数
自己调用自己
1.执行过程分为两个阶段
1)递推阶段:从原问题出发,按递归公式从未知到已知,最终到达递归终止条件(只需了解)
2)回归阶段:按递归的终止条件求出结果,逆向逐步带入递归公式,回到原问题求解
3.递归的两个必要条件
(1)存在限制条件,当满足这个限制条件的时候,递归便不再继续.
(2)每次递归调用之后越来越接近这个限制条件
举个栗子:
结构体
用户自定义的数据类型,在结构体里可以包含若干个不同的数据类型的成员变量(也可以相同),
格式:
struct 结构体名
{
数据类型 成员变量1;
数据类型 成员变量2;
数据类型 成员变量3;
};
例:
struct star //数据类型
{
int id;
char name[33];
float height;
};
成员变量类型:基本数据类型(6个)、指针、数组、结构体等,但是不能是函数
结构体变量
通过结构体数据类型定义的变量
struct 结构体名 变量名;
1.先定义结构体,再定义结构体变量
struct 结构体名
{
数据类型 成员变量1;
数据类型 成员变量2;
数据类型 成员变量3;
};
struct 结构体名 变量名;
举个栗子:
1.
2.定义结构体同时,定义结构体变量
3.缺省结构体名
赋值
1.定义的同时直接{}赋值
2.定义变量时未初始化,然后对变量单独赋值
3.点等法
访问
结构体变量名.成员变量名
重定义typedef
typedef int size_t;
int a; //size_t a;
1)定义结构体的同时重定义
2)先定义结构体,再重定义
typedef struct star ST;//ST数据类型
ST s;
ST *p=&s;
练习:创建一个名为student的结构体,包含姓名,学号,班级,分数,(数据类型自己定义),从终端输入学生的信息并打印。
参考:
扩展:
结构体数组
结构体类型相同的变量组成的数组
格式:
1)定义结构体同时定义结构体数组
2)先定义结构体,再定义结构体数组
初始化
1)定义结构体数组同时直接赋值
2)先定义,再赋值
练习:
创建一个名为student的结构体数组,包含学号,姓名,分数,(数据类型自己定义),从终端输入学生的信息并打印分数及格的学生信息(输入3人即可)
结构体指针
指向结构体变量的指针
格式:
struct 结构体名 *结构体指针名;
举个栗子:
赋值:
格式:指针变量名->成员变量名
p->id=1;
p->height=184;
(*p).id=2;
strcpy(p->name,"kun");
大小
本质是指针,大小是4
总结:
1.不能把结构体类型变量作为整体引用,只能对结构体类型变量中的各个成员变量分别引用
2. 如果成员变量本身属于另一种结构体类型,用若干个成员运算符一级级找到最低级的成员
举个栗子:
练习:
创建一个结构体数组,数组名为book,
结构体成员包含编号,书名,售价(数据类型自己设定)。写一个函数,包含两个形参,分别接收结构体数组的首
结构体大小
字节对齐原则 ---- 对齐8字节 比8字节小按照8字节开辟空间
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct demo
{
short a;// 2 开辟8字节
int b; //4
double c;//8 开辟8字节
};
int main()
{
printf("%d\n", sizeof(struct demo)); // 16
return 0;
}
补充:
结构体开辟空间示意图
1.在32位系统下,默认的value值为4字节,判断结构体中类型最大成员的字节大小,和默认的value值进行比较,按小的数进行对齐
2.结构体成员进行对齐时遵循地址偏移量是成员类型大小的整数倍,double类型数据存放在4字节的整数倍上
3.结构体成员按顺序进行存储
为什么要字节对齐?
1) 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。(提高程序的移植性)
2)性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
3)内存原因:在设计结构体时,通过对齐规则尽可能优化结构体的空间大小至最小空间\
结构体地址的连续性问题:
问题:为什么short的首地址不是0x7ffd30ea8c65?
结构体补充:
共用体
不同类型的成员变量共用同一块地址空间
格式:
union 共用体名
{
成员列表;
};
定义共用体变量
union 共用体名 变量名;
1) 共用体成员共用同一块地址空间
2) 赋值顺序以最后一次赋值为准
3) 共用体的大小为成员中类型最大的数据的大小
用共用体测大小端:
#include<stdio.h>
union val
{
int a;
char b;
};
union val v;
int main(int argc, char const *argv[])
{
v.a=0x12345678;
if(v.b==0x78)
printf("小端\n");
else
printf(" da端\n");
return 0;
}
枚举
用户自定义的数据类型,可以用来声明一组常数
格式:
enum 枚举名
{
value1,
value2,
value3,
...
};
未赋值时,常数默认从0开始,依次加1;
练习:
存储类型
auto static extern register
auto
修饰变量,一般省略
static修饰变量和函数
修饰变量
1.变量存放位置在静态区;未初始化,在.bss区;已初始化,在.data区
2.生命周期为整个程序
修饰局部变量,和普通局部变量的作用域没有区别,但是生命周期被延长至整个程序
修饰全局变量,限制在本文件中使用
3.被static修饰的变量只会初始化依次,初值赋值为0
#include<stdio.h>
void fun()
{
static int a=5;
a++;
printf("%d\n",a);
}
int main(int argc, char const *argv[])
{
fun(); //6
fun(); //7
return 0;
}