4.数组参数、指针参数
一维数组传参
#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 };
test(arr);
test2(arr2);
}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
二维数组传参
void test(int arr[3][5])
{}
//void test(int arr[][])//这是错误的写法
//{}
void test(int arr[][5])
{}
void test(int* arr)
{}
void test(int* arr[5])
{}
void test(int(*arr)[5])
//指向数组的指针来接受一个二维数组也行
//但是要注意这个指针会指向数组中的某一行,刚开始可能是第一行
{}
void test(int** arr)
{}
int main()
{
int arr[3][5] = { 0 };
test(arr);
}
一级指针传参
#include <stdio.h>
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(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;
}
运行结果:1 2 3 4 5 6 7 8 9 0
那么反过来,当一个函数的形参是一个一级指针是,实参可以是什么呢①
如
void test1(int *p)
{}
//test1函数能接收什么参数
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
二级指针传参
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
int* arr[10] = { p };
test(pp);
test(&p);
test(arr);
return 0;
}
运行结果:
num = 10
num = 10
num = 10
总结:二级指针的形参可以传一级指针的地址、二级指针变量,甚至可以是一个指针数组
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
5.函数指针
由前面学习的数组指针、指针数组,我们应该可以推测出函数指针的作用。是的,函数指针就是存放函数地址的指针
函数的地址
我们先来看看函数地址是什么再来讨论怎么把它存起来
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
运行结果:
00007FF7F09713CF
00007FF7F09713CF
可见,函数确实是有一个地址的,但是两次打印的结果是一样的,难道说&函数名是函数的地址,函数名是首元素地址吗?当然不可能,函数哪来的什么首元素。所以,&函数名和函数名都是函数的地址,这两种方式拿到的都是函数的地址
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
函数指针
那我们应该怎么去把函数的地址给保存起来呢?回想一下之前学的数组指针,我们是这样去构建的
对于一个整型数组arr:int arr[10]。我们要写出对应的数组指针
第一步,它要是一个指针,所以有*p
第二步,考虑到*优先级低于 [ ],所以要用小括号括起来,让*先与p结合得到(*p)
第三步,用[ ]表示这个指针指向的是数组,数组有几个元素呢?10个,所以得到(*p)[10];第四步,每个元素是什么类型的呢,是int类型,所以最终得到int (*p)[10] = &arr
同样,对于一个加法函数Add,我们想要把它的地址存起来,就需要一个函数指针
int Add(int x, int y)
{
return x + y;
}
第一步,我们需要一个指针,所以有*p
第二步,指针p指向的是一个函数,我们这样来表示*p(int,int),用一个小括号,里面放参数,用于表示这个指针函数有哪些参数(参数里面可以写变量即*p(int x,int y),也可以不写,就写一个类型*p(int,int))
第三步,函数的返回类型是int,所以得到 int *p(int,int)
第四步,由于这样写就不是一个指针,因为p先和小括号结合,然后int与*结合,最后变成了“p这个函数有两个int类型的参数,返回类型是int*”,这当然不是我们想要的结果。所以还是要给*p加上小括号,最终得到int (*p)(int,int) = Add
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
函数指针的使用
我们来验证一下,这个p里面到底是不是函数的地址。是不是函数地址我们用一下不就知道了。
首先我们拿到p,这里面放到是函数的地址,那我们对它解引用一下(*p)不就找到这个函数了吗,然后对着这个传参看看到底能不能使用
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 3;
int b = 5;
int (*p)(int, int) = Add;
printf("直接调用函数Add得到:%d\n", Add(a, b));
printf("使用函数指针p得到:%d\n",(*p)(a,b));
return 0;
}
运行结果:
直接调用函数Add得到:8
使用函数指针p得到:8
可见,这个指针p里面确实可以存放一个函数的地址
注
void (*p) (int) = Add;//这是函数指针
void (*) (int) ———— 这是函数指针类型
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
尝试解释一下面两段代码
代码一
(*(void(*) () )0 ) ();
1)void(*) ():这是函数指针类型
2)(void(*) ()) 0:这是把0强制类型转换成void(*)()函数指针类型,也就是0变成地址了
3)(*(void(*) () )0 ):对地址解引用再加一个函数调用操作符(即小括号 “()” ),表示我要调用地址为0处的函数了
注:
1. 函数去掉函数名和参数就是返回类型
2. 数组去掉数组名和元素个数+方括号( [ ] )剩下的就是数组的元素类型
-- -- -- -- -- -- -- -- -- --
代码二
void (*signal(int , void(*)(int)))(int);
1)signal是一个函数声明
2)signal函数的参数有两个,int 和 void(*)(int),后者是一个函数指针,该函数指针指向的函数的参数是int,返回类型是void
3)signal函数的返回类型是void(*)(int),也是一个函数指针类型
也可以对代码二进行简化
typedef void (*pfun_t)(int);
//对void(*)(int) 这个函数指针类型重命名时,要把pfun_t写在*后面
//然后代码二就可以简化成下面的形式
pfun_t signal(int,pfun_t);
//但是不能像对类型重命名那样,写成 typedef unsigned int uint 这样的形式
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
对函数指针解引用真的有用吗
先看一段代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int add(int x, int y)
{
return x + y;
}
int main()
{
int(*pa)(int, int) = &add;//add也行,前面讲过这都是函数的地址
printf("%d\n", pa(2, 3));
printf("%d\n", (*pa)(2, 3));
printf("%d\n", (**pa)(2, 3));
printf("%d\n", (********************pa)(2, 3));
return 0;
}
运行结果:
5
5
5
5
由此可见,其实这个*根本没用啊,所以以后可以根据自习的习惯喜好来添加。当然不要像最后一行那样加一大堆 *,搞的别人以为你有什么特殊意思想要表达呢~~
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
6.函数指针数组
数组是一个存放相同类型数据的存储空间,那我们已经介绍了指针数组,比如:
int *arr[10];
//数组的每个元素是int*
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组。那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:int (*parr1[10])();
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
函数指针数组的用途:转移表
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
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 main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************\n");
printf(" 1.add *********** 2.sub \n");
printf(" 3.mul *********** 4.div \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
使用函数指针数组可以省去一些case语句
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
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 main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
while (input)
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);//调用函数
}
else
printf("输入有误\n");
printf("ret = %d\n", ret);
}
return 0;
}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
7.指向函数指针数组的指针
“指向函数指针数组的指针”是一个指针,指针指向一个数组,数组中的元素都是函数指针
定义方式
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int test(int x, int y)
{
return x + y;
}
int main()
{
int arr[10] = { 0 };//一个数组
int(*p)[10] = &arr;//数组指针
int(*pf)(int, int);//函数指针
int (*pfArr[4])(int, int) = { &test };//pfArr是函数指针数组
int (*(*ppfArr)[4])(int, int) = &pfArr;//ppfArr是一个指向函数指针数组的指针
//ppdArr是一个指针,指针指向的数组有四个元素
//指向的数组中每个元素的类型是:int(*) (int,int),即函数指针
return 0;
}
附
①此时实参可以是地址,也可以是存放了地址的一级指针变量
void test1(int* p)
{}
int main()
{
int i = 10;
int* p = &i;
test1(&i);//ok
test1(p);//ok
return 0;
}