函数
数据类型 函数名(数据类型 形参1,数据类型,形参2)
函数的优势:提高代码的复用性,提高代码的可维护性
函数使用的三个步骤:函数定义、函数声明、函数调用
函数与函数之间是平级的关系;
定义的函数需要在使用前声明;
- 定义这个函数的主要实现什么事情(函数体-函数名);
- 函数实现这个功能,需要什么才能完成(是否需要形参);
- 函数功能实现了,调用处是否需要继续使用(是否需要返回,返回内容数据类型);
【注意事项】
- return下面不要写代码,永远执行不到,属于无效代码;
- 函数返回值类型为void,则不需要return;如果书写了,则在return 为空;
示例(计算两个数据的求和):
//求两个数据之和
int add(int a,int b)
{
return a+b;
}
//求两个数据之差
int sub(int a,int b)
{
return a-b;
}
//求两个数据的乘法
int mul(int a,int b)
{
return a*b;
}
//求两个数据的除法
int div(int a,int b)
{
return a/b;
}
数组
数组的在嵌入式中可以理解为一段连续的内存,内存中存放的数据内容;
是一种容器,可以用来储存同种数据类型的多个值;
数组是一个连续空间,数组一旦定义了,数组的长度是不可以变更的;
int arr[15] = {0};//int 默认为0,float double 默认为0.0
char arr_char[4+1] = {'t','e','s','t'};//4是数组内容长度,1是存储结束符使用'\0'
【备注说明】
长度省略:数据值的个数就是数组的长度
长度未省略:数据的个数<=长度
内存:软件在运行过程中,用来临时存储粗数据;
动作:读/写
内存地址:在内存中申请空间的编号
作用:快速管理和操作内存中的数据
内存的读和写都需要依赖于内存的地址:address,size,offset(偏移量)
内存地址的规则:32位操作系统:32字节的,最大可以支持内存:4GB
数组的元素访问:
1.使用for 取数组的下标(索引)读取数组中的内容,或进行修改;
2.使用指针读取数组中的内容,或进行修改
int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义一个int类型的数组,长度10;
int len = sizeof(arr)/sizeof(arr[0]);//获取一个数组的长度
int* arr1 = arr;//定义指针获取数组arr的首地址
int* arr2 = &arr[0];
printf("arr len is %d\n",len);
printf("arr first address is %p\n",arr1);
printf("arr first address is %p\n",arr2);
//使用指针访问数组
for(int j=0;j<len;j++)
{
printf(" %d",*arr1++);
}
printf("\narr data is:");
for(int i=0;i<len;i++)
{
printf(" %d",*arr2++);
}
//使用下标访问数组内容
for(int i;i<len;i++)
{
printf(" %d",arr1);
}
变量地址:说的是变量的首地址, &a 取变量a的地址(4字节) 以上基于32位操作系统
arr[0] :0说的内存的偏移量,偏移量大小和数据类型相关;数组长度=总长度/数据类型占用的字节数;
数组作为函数的参数
传递的是数组的首地址;常见使用*指针代替,很少传递数组;
传递数组的长度,先声明,传入形参;
其他说明
数组越界(指针越界)
最小索引:0
最大索引:数组的长度 - 1 这样可以保证数组索引不会越界;
特殊情况
sizeof运算的时候,不会退化,arr还是一个整体;
&arr 获取地址的时候,会退化,arr还是一个整体;
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int len = sizeof(arr)/sizeof(arr[0]);
//获取数组的首地址的两种方式
int* arr1 = arr;
int* arr2 = &arr[0];
printf("arr size is %d\n",sizeof(arr));
printf("arr first address is %p\n",arr);
printf("arr offset len is %p\n",&arr+1);//&arr的步长为数据类型*数组长度
}
二维数组
在C语言中,二维数组是一种数据结构,它包含多个一维数组。二维数组可以被看作是矩阵,其中每个元素都是一个固定大小的数组。二维数组在C语言中非常有用,尤其是在处理表格数据或矩阵运算时。
void twoDimensionalArray()
{
int arr[3][5]=//这样定义的是相同长度的二维数组
{
{1,2,3,4,5},
{11,22,33,44,55},
{111,222,333,444,55}
};
int (*p)[5] = arr;//数组指针,利用指针遍历二维数组
for(int i=0;i<3;i++)
{
for(int j=0;j<5;j++)
{
printf("%d ",*(*p+j));
}
printf("\n");
p++;
}
}
指针
指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
作用:查询(读取)和存储(写入)、参数传递、内存管理等
指针变量--存储的是内存地址;
数据类型 * 变量名;
指针变量占用的大小,和数据类型无关,和编译器相关的(32位:32字节,64位:64字节);
给指针变量赋值的时候,不能吧一个数值赋值给到指针变量;(指针对应的是内存的地址);
int a = 10;
int* p = &a;//p-指针变量名称,获取整型变量a,在内存中的地址;
printf("the a address is %p\n",p);//打印变量a在内存中的四肢
printf("the a value is %p\n",*p);//打印变量a的取值(通过指针)其中的*为解引用
指针(函数)中的作用
- 1.操作其他函数中的变量
- 2.函数返回值多个
- 3.函数的结果和计算状态分开
- 4.方便操作数据和函数
-
作用一:操作其他函数中的变量
-
void swap(int *num1,int *num2) { int temp = *num1; *num1 = *num2; *num2 = temp; } int main() { int a=10; int b=20; swap(&a,&b);//交换的是变量对应的地址 printf("调换后的数据为 a=%d,b=%d",a,b); }
补充说明
函数变量的生命周期和函数相关,函数结束了,变量也会消失;
此时在其他函数中,就无法通过指针使用了,
如果不想函数中的变量被收回,可以在变量的前面增加static 关键字进行修饰;
-
int* method(void) { static int a = 10; return &a ; } int main() { int *p = method(); printf("the a in method value is %d\n",*p); }
作用二:在函数中返回多个数值,方便开发者理解
-
void getArrMaxandMin(int*arr,int len,int *max,int* min) { *max = arr[0]; *min = arr[0]; for(int i = 1;i < len;i++) { //取最大值 if(arr[i] > *max) { *max = arr[i]; } //取最小值 if(arr[i] < *min) { *min = arr[i]; } } } int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int len = sizeof(arr)/sizeof(arr[0]); int max = 0; int min = 0; getArrMaxandMin(arr,len,&max,&min); }
作用三:利用指针将函数的结果和状态分开
-
int getRemainder(int a,int b,int *remainder) { if (b == 0) { return 1; } *remainder = a%b; return 0; } int main() { int a=10; int b=20; int res=0; int flag = getRemainder(a,b,&res); if (!flag) { printf("the res value is %d\n",res); } else { printf("the flag value is%d\n",flag); } }
作用四:方便操作数据和函数
-
指针高级阶段(指针的计算,二级指针和多级指针、数组和指针、函数和指针)
指针的运算
运算符:+、-
步长(偏移量):指针移动一次的字节数;偏移量大小与数据类型相关;
char(1字节) int(4字节)short(2字节) long(4字节) long long (8字节)
int a = 10;
int* p1 = &a;
printf("the a address id %p\n",p1);
printf("the p1+1 address id %p,the p1+1 value is %d \n",p1+1,*(p1+1));
printf("the P1+2 address id %p,the p1+1 value is %d\n",p1+2,*(p1+2));
有意义的操作:
指针跟正整数进行+、-操作(每次移动N个步长,偏移量);
指针和指针之间进行减操作(间隔步长,内存中空间偏移量有多大);
无意义操作
指针跟正整数进行乘除操作(指针指向不明确,引起野指针,指针越界)
指针和指针进行加、乘、除;
【备注说明】
野指针:指针指向的空间未分配;
悬空指针:指针指向的空间已经分配,但是已经被释放了;
Linux系统中遇到段错误,常见的原因都是由于指针使用不当引起的;
void 类型指针
void *p; 不可以进行指针的运算(+、-);
无法获取数据,无法计算,但是可以接受任何地址(优点);
不同类型的指针之间,是不能互相赋值的,void类型的指针打破了上面的观念,void没有任何类型;
优势:可以接受任意类型的指针记录的内存地址;
缺点:void类型的指针,无法获取变量中的数据,也不能进行指针的加减计算;
int main()
{
int abs = 10;
int abs1 = 20;
int* pabs = &abs;
int* pabs1 = &abs1;
printf("the abs address is %p\n",pabs);
printf("the abs1 address is %p\n",pabs1);
void* p3 =pabs;
void* p4 = pabs1;
printf("the p3 address is %p\n",p3);
printf("the p4 address is %p\n",p4);
}
使用void类型优化swap函数,使得swap_ex函数的通用型增加
void swap_ex(void* pnum,void* pnum2,int len)
{
char* p1 = pnum;
char* p2 = pnum2;
char temp = 0;
for(int i = 0;i < len;i++)
{
temp = *p1;
*p1 = *p2;
*p2 = temp;
p1++;
p2++;
}
}
int main()
{
int a=100;
int b=200;
swap_ex(&a,&b,sizeof(a));//不在受数据类型的限制,交换数据必须相同的数据类型
printf("调换后的数据为 a=%d,b=%d\n",a,b);
char c = 128;
char d = 255;
swap_ex(&c,&d,sizeof(c));//不在受数据类型的限制,交换数据必须相同的数据类型
printf("调换后的数据为 c=%d,d=%d\n",c,d);
}
二级指针
数据类型 *指针名
指针的数据类型:跟指向空间中的数据类型保持一致的;
示例:int ** p;
第一个*:数据类型,和int 数据变量是一个整体,表示内存存储数据的类型;
第二个*:定义指针的特殊标志;
作用:二级指针可以操作一级指针记录的地址
int main()
{
int a = 10;
int b = 20;
int* p5 = &a;
int** p6 = &p5;
//利用二级指针修改一级指针内存的地址
*p6 = &b;
//利用二级指针获取变量中记录的数据
printf("the a address is %p\n",&a);
printf("the b address is %p\n",&b);
printf("the p5 address is %p\n",p5);
printf("the p6 address is %d\n",**p6);//*解引用,一个*解一级指针引用
}
数组指针
概念:指向数组的指针,叫做数组指针(数组的指针);
作用:方便的操作数据中的各种数据
int* p = arr;
int (*p)[3] = arr;
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int len = sizeof(arr)/sizeof(arr[0]);
//获取数组的首地址的两种方式
int* arr1 = arr;
int* arr2 = &arr[0];
printf("arr first address is %p\n",arr1);
printf("arr first address is %p\n",arr2);
printf("arr len is %d,and arr data is:",len);
for(int j=0;j<len;j++)
{
printf(" %d",*arr1++);
}
printf("\narr data is:");
for(int i=0;i<len;i++)
{
printf(" %d",*arr2++);
}
}
指针数组
存放指针的数组
void twoDimensionalArray_Point()
{
//定义三个一维数组
int arr[7]={1,2,3,5,6};
int arr1[7]={0,9,8,7,6,5,4};
int arr2[7]={0};
//定义一个数组,把三个一维数组放到二维数组中
int *arr_twoDimensionalA[3]={arr,arr1,arr2};
//获取指针(二级指针)
int **p_arr = arr_twoDimensionalA;
for(int i=0;i<3;i++)
{
for(int j=0;j<7;j++)
{
printf("%d",*(*p_arr+j));
}
printf("\n");
//移动二维数组的指针,继续遍历下一个一维数组
p_arr++;
}
//把五个字符串的指针放到一个数组中,指针数组
char* p_arr[5] = {"hello","world","nihao","zhongguo","shijie"};
for(int i = 0;i < 5;i++)
{
printf("the string is %s\n",p_arr[i]);
}
}