- 前言
- 4、数组参数、指针参数
- 4.1 一维数组传参
- 4.2 二维数组传参
- 4.3 一级指针传参
- 4.4 二级指针传参
- 5、函数指针
- 6、函数指针数组
- 总结
前言
本文接着上文,继续学习指针进阶的知识点:
- 数组参数、指针参数
- 函数指针
- 函数指针数组
4、数组参数、指针参数
首先对上一篇所学知识进行回顾复习,将知识点,画在一张图上就能清楚的对比相同点和不同点:
编程时,有时要把数组或者指针传给函数,函数的参数该如何设计?下面举例说明:
4.1 一维数组传参一维数组接收参数的几种方式
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 arr1[10] = { 0 };//一维数组
int* arr2[20] = { 0 };//指针数组
test(arr1);
test2(arr2);
}
4.2 二维数组传参
二维数组接收参数的几种方式
void test(int arr[3][5])//形参与实参的定义相同,定义数组收受参数
{}
void test(int arr[][5])//形参与实参的定义相同。
第1个[]内数字可以不写
{}
void test(int arr[][])//错误的,第2个[]内数字必须写
{}
void test(int *arr)//错误的,*arr对地址解引用,
{} //arr是二维数组名,第一行地址,第一行数组的首元素地址,获得第一个元素0
void test(int* arr[5])//错误的,这是一个数组,每一个元素是指针,就是地址
{}
void test2(int(*arr)[5])//指针指向的数组,是一个一维数组,数组有5个元素
{}
void test2(int** arr)//错误的,二级指针,存放一级指针的地址
{}
//二维数组传参
int main()
{
int arr[3][5] = { 0 };//二维数组
二维数组在传参数组名时,传递的是首元素a[0],即第一行数组的地址
test(arr);
}
4.3 一级指针传参
void test(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *arr++);
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = arr;//直接把数组名给指针即可
int sz = sizeof(arr) / sizeof(arr[0]);
test(pa, sz);
return 0;
}
下面反过来考虑:
当知道一个函数的形参部分为一级指针的时候,在主函数中调用时,应该传什么参数?
void test(int* p)
{}
int main()
{
int a = 10;
int* p = &a;
int arr[10];
//传参的几种形式,都是可以的
test(&a);
test(p);
test(arr);
return 0;
}
4.4 二级指针传参
//举例 1
void test(int** p)//二极指针,地址的地址
{}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
test(pp);
test(&p);
}
//举例 2
void test(char** ch)
{}
int main()
{
char ch = 'w';
char* pc = &ch;
char** ppc = &pc;
char* arr[5];
char arr1[3][5];
test(arr);
test(ppc);
test(&pc);
test(arr1);//错误的,二维数组穿的是第一行的地址,就是第一行数组的地址
//应该char (*p)[5],参数应该用数组指针传参,指向数组的指针
}
//举例 3
void test1(int(*p)[5])//数组指针,指针指向数组,这个数组有5个元素,每个元素是int类型
{}
void test2(int(*p)[3][5])//指针指向整个二维数组
{
*p//解引用后代表首行地址
}
int main()
{
int arr[3][5];
test1(arr);//二维数组传递的是第一行地址
test2(&arr);//传递的是整个二维数组的地址,一般不会这样用
}
5、函数指针
通过与前面所学的知识进行类比,就能发现定义指针的奥秘了:
- 数组指针——指向数组的指针
- 函数指针——指向函数的指针
//举例1
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);//函数名就是地址
printf("%p\n", &test);
return 0;
}
//举例2
int add(int x, int y)
{
return x + y;
}
int test(char* ch)
{}
int main()
{
//int arr[10];
//arr
//&arr
//int* p = arr;
//int(*p)[10] = &arr;
int(*pa)(int, int) = add;//就是函数指针变量,将函数的地址保存起来
int ret = (*pa)(2, 3);//此处的*不起作用,有无都行
/*int ret = add(2, 3);
int ret = pa(2, 3);*/
printf("%d\n", ret);
//int(*pc)(char*) = test;
return 0;
}
int main()
{
void(*p)();//这个p的类型函数指针类型,去掉p后,剩下的是void(*)(),指针p指向的地址是一个参数是空,返回类型是空的函数
//代码1
(*(void(*)())0)();
解释说明:
void(*)()是函数指针类型
(void(*)())0,将0强制转换成函数指针类型,则指针0指向的地址里有一个函数
*(void(*)())0 地址解引用,就是函数名了
(*(void(*)())0)(),调用0地址处放的函数*/
//代码2
void(*signal(int, void(*)()(int)))(int);
解释说明:
signal(int, void(*)()(int)) 函数两个参数,一个int,一个函数指针
void(*)()(int)是一个函数指针类型,指向的函数的参数是int,返回值是空
//如同函数声明 int add(int, int);
//去掉函数名和参数后,剩下的int就是函数的返回类型
void(*signal(int, void(*)()(int)))(int)中去除函数名 signal(int, void(*)()(int))后,
只剩下 void(* )(int), 它就是函数的返回类型,返回的是函数指针类型
//代码2也可以简化:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
}
6、函数指针数组
通过类比学习定义:
指针数组:
int* arr[10]; //数组的每个元素是int*
函数指针数组:
int(*parr1[5])();
上面去掉数组名parr[5]后,剩下int(*)(),就是函数指针类型,这个数组有5个元素,每个元素都是int(*)()类型
parr1 先和 [] 结合,说明 parr1是数组,数组的内容:是 int (*)() 类型的函数指针
即数组里的每个元素都是指针,即地址,每个地址都存有一个函数,对指针解引用就能调用函数
函数指针数组的用处:
一个简易计算器的函数:
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
void menu()
{
printf("**************************\n");
printf("**** 1.add 2.sub ****\n");
printf("**** 3.mul 4.div ****\n");
printf("**** 0.exit ****\n");
printf("**************************\n");
}
可以发现,当出现多个函数时,调用过程时相似的,但是代码重复较多,是冗余的:
//常规的写法非常繁琐
int main()
{
int input = 0;
int x = 0;
int y = 0;
int res = 0;
do
{
menu();
printf("请选择 ==> ");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入两个 *** 作数 ==> ");
scanf("%d %d", &x, &y);
res = add(x, y);
printf("ret=%d\n", res);
break;
case 2:
printf("请输入2个 *** 作数:>");
scanf("%d %d", &x, &y);
res = sub(x, y);
printf("ret=%d\n", res);
break;
case 3:
printf("请输入2个 *** 作数:>");
scanf("%d %d", &x, &y);
res = mul(x, y);
printf("ret = %d\n", res);
break;
case 4:
printf("请输入2个 *** 作数:>");
scanf("%d %d", &x, &y);
res = div(x, y);
printf("ret = %d\n", res);
break;
case 0:
printf("退出计算器\n");
break;
default:
break;
}
} while (input);
return 0;
}
代码改写为使用函数指针数组:
//函数指针数组的用法
int main()
{
int input = 0;
int x = 0;
int y = 0;
int res = 0;
int(*paar[5])(int, int) = { 0,add,sub,mul,div };
do
{
menu();
printf("请选择 ==> ");
scanf("%d", &input);
if (input==0)
{
printf("退出计算器\n");
}
else if (input >= 1 && input <= 4)
{
printf("请输入2个 *** 作数:>");
scanf("%d%d", &x, &y);
res = (*paar[input])(x, y);//直接调用函数
printf("%d\n", res);
}
else
{
printf("选择错误,重新输入数字\n");
}
} while (input);
return 0;
}
总结
指针进阶的内容较多,而且这些内容的名词都相似,如果不能牢固掌握,将会混淆这些知识点。
第一遍学习不能完全掌握,要经常复习,温故而知新。
通过类比,可以有效地区分各种定义。
下一篇继续学习指针进阶的内容。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)