4. 数组参数、指针参数
在写代码时,有时候需要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
4.1 一维数组传参
#include <stdio.h>
void test(int arr[])//正确,传数组名,就是首元素地址,用数组接收,
{}
void test(int arr[10])//正确,这个是说明了接收数组的大小
{}
void test(int* arr)//正确,首元素地址就是指针,这个比较熟悉了
{}
void test2(int* arr[20])//正确,传的是指针数组,用指针数组接收
{}
void test2(int** arr)//正确。指针数组中存放的是一级指针,用二级指针来接收一级指针的地址
{}
int main()
{
int arr[10] = { 0 }; //整形指针
int* arr2[20] = { 0 }; //指针数组,存放int*的数组
test(arr);
test2(arr2);
}
4.2 二维数组传参
4.2.1 二维数组名和&二维数组名
二维数组名和&二维数组名

分析
&arr和arr,虽然值是一样的,但是意义不一样。
实际上:
arr表示的是第一行元素的地址,所以arr+1相对于arr的差值是5(一行5个元素)
&arr 表示的是整个二维数组的地址,数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40
4.2.2 二维数组名传参
- 二维数组数组名传参,传的虽然是第一行第一个元素的地址,但是表示的意义是第一行元素
分析
4.2.2 二维数组传参
二维数组传参,函数形参的设计只能省略第一个[ ]的数字。只能省略行不能省略列。
void test(int arr[3][5])//正确
{}
void test(int arr[][])//错误
{}
void test(int arr[][5])//正确
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
void test(int* arr)//错误,这是一个一级指针,不能接收二维数组第一行5个元素
{}
void test(int* arr[5])//错误 ,这是一个一维的整形数组(类型是int*), 不能接收二维数组的第一行的5个元素。要用数组接收的话必须是二维数组接收。
{}
void test(int(*arr)[5])//正确,这是一个指向一个int型的5个元素的数组指针,一个数组指针,
{}
void test(int** arr)//错误,这是一个二级指针(能接收的是一级指针的地址),不能接收传过来的二维数组的第一行的5个元素,
{}
int main()
{
int arr[3][5] = { 0 };//二维数组,3行5列。
test(arr);//二维数组名,表示的是二维数组第一行的元素
}
void test(int(*arr)[5]) 数组指针分析
4.2.3 小结
二维数组的数组名作为函数参数,能用二维数组接收和数组指针接收。
void test(int arr[3][5])//正确
{}
void test(int arr[][5])//正确
{}
void test(int(*arr)[5])
int main()
{
int arr[3][5] = { 0 };//二维数组,3行5列。
test(arr);//二维数组名,表示的是二维数组第一行的元素
}
4.3 一级指针传参
4.3.1 一级指针传参,用一级指针接收
#include <stdio.h>
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d\n", *(p + i));
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}
4.3.2 反向思考
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
void test1(int *p)
{}
//test1函数能接收什么参数?
#include <stdio.h>
void test1(int* p)
{}
int main()
{
int arr[1] = { 0 };
int a = 0;
int* p = &a;
tast1(&a); //可以传变量地址
test1(arr);//可以传一维数组数组名
test1(p);//可以传一级指针
}
变量地址
一维数组数组名
一级指针
4.4 二级指针传参
4.4.1 二级指针传参二级指针接收
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp); //可以传二级指针
test(&p); //可以传一级指针的地址
return 0;
}
4.4.1 反向思考
当函数的参数为二级指针的时候,可以接收什么参数?
void test(char **p)
{
}
int main()
{
char c = 'b';
char*pc = &c;
char**ppc = &pc;
char* arr[10];
test(&pc); //可以传一级指针的地址
test(ppc);//可以传二级指针
test(arr);//可以传指针数组的首地址
return 0;
}
一级指针的地址
二级指针
指针数组的首地址
5. 函数指针
5.1 函数是否有地址——是的
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}

5.2 保存函数地址的指针——函数指针
5.2.1 如何表示函数指针——类比数组指针
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n", &Add);
int arr[10];
int (*pa)[10] = &arr; //数组指针的写法是这样的
int (*pf)(int, int) = &Add; //类比写我们的函数指针
// *pf需要用()括起来,表示这是一个指针,然后后面紧跟的()
//表示这是一个函数指针,
//参数是int ,int 返回值是(*pf)之前的int
return 0;
}
5.2.2 函数名和&函数名表示意义一样没有区别
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n", &Add);
int arr[10];
int (*pa)[10] = &arr; //数组指针的写法是这样的
int (*pf)(int, int) = &Add; //函数指针&Add和Add表示的意义是完全一样的
int (* pf)(int, int) = Add; //取决于你想不想写。
return 0;
}
5.2.3 函数指针如何调用——类比函数调用
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n", &Add);
int arr[10];
int (*pa)[10] = &arr; //数组指针的写法是这样的
int (*pf)(int, int) = &Add;
//int ret = Add(2, 3);//函数调用是这样使用
int ret = pf(2, 3);//我们的函数指针和函数名表示的意义一样,类比,我们直接这样调用
printf("%d\n", ret);
return 0;
}

5.3 《C陷阱和缺陷》中的两个有趣的代码
5.3.1
(*(void (*)())0)();
//该代码是一次函数调用
//调用0地址处的一个函数
//首先代码中将0强制类型转换为类型为void (*)()的函数指针
//然后去调用0地址处的函数

5.3.2
void (*signal(int , void(*)(int)))(int);
//该代码是一次函数的声明
//声明的函数名字叫signal
//signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该函数指针能够指向的那个函数的参数是int
///返回类型是void
//signal函数的返回类型是一个函数指针,该函数指针能够指向的那个函数的参数是int,返回类型是void

代码看起来比较费劲,如何简化:
给函数指针重命名
typedef void(*pf_t)(int) ;//注意这里就是将void(*)(int)重命名为 *pf_t
重命名之后的代码:
pf_t *signal(int , pf_t);


1282

被折叠的 条评论
为什么被折叠?



