在进行程序设计的时候,难免会出现将数组或指针传给参数,那函数的参数该如何设计呢?
一维数组传参的方式:
举例:
#include<stdio.h> int main() { int arr[10] = { 0 }; int* arr2[20] = { 0 }; test(arr); test2(arr2); return 0; }
对整型数组:
根据数组名:
1:指明数组长度
void test(int arr[10])
2:不指明数组长度
void test(int arr[])
用指针指向的方式:
void test(int *arr)
对指针数组:
1:用数组进行传递
void test2(int *arr[20])
2:用指针指向进行传递
void test2(int **arr)
总结一下:一维数组在进行传参的时候,参数可以是指针也可以是数组,数组大小可以忽略。
二维数组传参的方式:
举例:
#include<stdio.h> int main() { int arr[3][5] = { 0 }; test(arr); return 0; }
用数组名的方式:
1:行列都指明
void test(int arr[3][5])
2:只指明列
void test(int arr[][5])
3:行列都不指明:这种方式是错误的
二维数组传参,函数参数的设计只能省略第一个[]的数字,因为对一个二维数组,可以不知道有多少行,但是必须知道一行有多少个元素
void test(int arr[][])
用指针的方式:
1:用普通一级指针的方式:这种方式是错误的!
数组名代表首地址,但因为arr是二维数组,首地址为第一行的地址,而int*arr只能用来存放整形的地址,传地址用指针来接收,指针只占四个字节,而一般一个二维数组的字节数都大于四个字节。
void test(int *arr)
2:这种方式也是错误的,int*arr[5]不是指针
void test(int *arr[5])
3:用数组指针的方式:
int (*arr)[5],为数组指针,该指针里面存放的是包含5个整形的一个数组
void test(int(*arr)[5])
4:用二级指针的方式:该方法是错误的
int arr是二级指针,只能用来存放一级指针变量的地址
void test(int **arr)
一级指针传参:
举例:
核心:指针传参,指针接收
#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]); print(p, sz);//指针传参 return 0; }
1 2 3 4 5 6 7 8 9 0
当函数的参数部分为一级指针的时候,函数能接收什么参数?
举例:
一级指针为整形指针的时候:
void test1(int*p) {}
int main() { int a = 10; int* p1 = &a; test1(&a);//传递地址 test1(p1);//传递指针 return 0; }
一级指针为字符型指针的时候:
举例:
void test2(char*p) {}
int main() { char ch = 'w'; char* pc = &ch; test2(&ch);//传递地址 test2(pc);//传递指针 return 0; }
二级指针传参:
举例:
核心:可通过直接传递二级指针或者传递二级指针中存放的一级指针的地址
#include<stdio.h> void test(int** ptr) { printf("num=%d\n", **ptr); } int main() { int n = 10; int* p = &n; int** pp = &p;//二级指针pp存放一级指针p的地址 test(pp);//传递二级指针 test(&p);//二级指针pp存放一级指针p的地址,等价于传递二级指针pp //int* arr[10];二级指针也可接受指针数组 //test(arr);传递数组名 return 0; }
num=10 num=10
函数指针:指向函数的指针
如何得到函数的地址?
&函数名和函数名,都是函数的地址,与数组类似但有所差异
举例:
#include<stdio.h> int add(int x, int y) { int z = 0; z = x + y; return z; } int main() { int a = 10; int b = 20; printf("%d\n", add(a, b));//对函数进行调用 printf("%p\n", &add); printf("%p\n", add); return 0; }
函数指针该怎么书写呢?
举例:
与数组指针的写法相类似
int (*pa)(int ,int)=add;//*pa一定要用括号括起来,比面pa和后面的括号结合变成函数 //可以类别数组指针的定义方法 //(int,int)代表函数的参数,参数名可写可不写
下面我们通过示例验证一下:
#include<stdio.h> int add(int x, int y) { int z = 0; z = x + y; return z; } int main() { int a = 10; int b = 20; int (*pa)(int, int) = add;//定义指针变量pa指向函数add printf("%d\n", (*pa)(a, b));//对指针进行解引用操作 return 0; }
30
将某变量强制类型转化为函数指针类型:
举例:
该行代码的含义就是,将0强制类型转化为函数指针类型,此时0就代表了函数的地址,再对其进行解引用操作,相当于函数的调用。
函数的返回也是函数指针:
举例:
该行代码就是signal函数的参数有两个,第一个是int,第二个是函数指针,该函数指针指向的函数的参数是int,返回类型是void,signal函数的返回也是一个函数指针,该函数指针指向的函数的参数是int,返回类型是void,signal(int,void(*)(int))为函数的声明
怎么理解呢?我们可以用 之前学过的typedef来将其拆分;
多个*在函数调用中其实无实际意义:
举例:
#include<stdio.h> int add(int x, int y) { int z=0; z = x + y; return z; } int main() { int a = 10; int b = 20; int (*pa)(int, int) = add; printf("%d\n", add(2, 3));//常规方法对函数进行调用 printf("%d\n", (pa)(2, 3));//用指针的方法对函数进行调用 //以下几种都是通过先对pa进行解引用在调用函数,*pa等价于add printf("%d\n", (*pa)(2, 3)); printf("%d\n", (**pa)(2, 3)); printf("%d\n", (***pa)(2, 3)); return 0; }
5 5 5 5 5
函数指针数组:
数组是一个存放相同类型数据的存储空间,之前我们已经学习过了指针数组,比如:
int *arr[10];//数组的每个元素是int*
那么函数指针数组该如定义呢?
其实和数组指针的定义有相似之处:
如下图所示:pa指向的函数类型是int,函数参数类型也是int
函数数组只能该怎么使用呢?
举例:
#include<stdio.h> int add(int x, int y) { int z = 0; z = x + y; return z; } int sub(int x, int y) { int z = 0; z = x - y; return z; } int mul(int x, int y) { int z = 0; z = x * y; return z; } int div(int x, int y) { int z = 0; z = x / y; return z; } int main() { //指针数组 //int* arr[5]; //需要一个数组,这个数组可以存放4个函数的地址----函数指针的数组 //int(*pa)(int, int)=add; int(*parr[4])(int, int) = { add,sub,mul,div };//定义函数指针数组,使其指向add,sub,mul,div函数 int i = 0; for (i = 0; i < 4; i++)//通过for循环,使其对对函数指针数组中的函数进行遍历 { printf("%d\n",parr[i](2, 3));//当i=0,调用add函数,当i=1,调用sub函数,以此类推 } return 0; }
5 -1 6 0
函数指针数组的用途:转移表
下面我们通过示例感受一下:
该实例想实现的功能为模拟计算器进行数据四则运算
#include<stdio.h> void menu() { printf("********************************\n"); printf("** 1.and 2.sub **\n"); printf("** 3.mul 4.div **\n"); printf("********************************\n"); } //通过不同的函数,实现不同的功能 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; } int main() { int input = 0; int x = 0, y = 0; do { menu(); printf("请选择:>"); scanf_s("%d", &input); switch (input) { case 1: printf("请输入两个操作数:"); scanf_s("%d%d", &x, &y); printf("%d\n",add(x, y)); break; case 2: printf("请输入两个操作数:"); scanf_s("%d%d", &x, &y); printf("%d\n", sub(x, y)); break; case 3: printf("请输入两个操作数:"); scanf_s("%d%d", &x, &y); printf("%d\n", mul(x, y)); break; case 4: printf("请输入两个操作数:"); scanf_s("%d%d", &x, &y); printf("%d\n", div(x, y)); break; case 0://不需要输入操作数 printf("退出\n"); default ://不需要输入操作数 printf("选择错误\n"); break; } } while (input); }
上述方法是通过调用不同的函数实现两数之间的不同运算,但数据的运算不仅是加减乘除,如果我们想实现位与,位或等等的其他运算呢?有人说,那就再编写功能函数啊,这样的方法确实没有错,但每增加一个功能函数,就需要增加一个case语句,这样大大增加了代码的量,执行效率也不高。
既然要实现的是两个数的四则运算,每个功能函数的参数个数相同,那么我们就可以结合之前学过的函数指针数组将函数,将其放在一个数组中,这样就不需要编写一个个case语句了,大大减少了代码量。
具体操作如下:
#include<stdio.h> void menu() { printf("********************************\n"); printf("** 1.and 2.sub **\n"); printf("** 3.mul 4.div **\n"); printf("********************************\n"); } 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; } int main() { int input = 0; int x = 0, y = 0; int(*pfarr[])(int, int) = { 0,add,sub,mul,div };//pfarr是函数指针数组,用过下标转移到对应的函数 do { menu(); printf("请选择:>"); scanf_s("%d", &input); if (input >= 1 && input <= 4) { printf("请输入操作数:>"); scanf_s("%d%d", &x, &y); int ret = pfarr[input](x, y);//ret作为接收转移的函数的返回值 printf("%d\n", ret); } else if (input == 0) { printf("退出"); } else { printf("选择错误\n"); } } while (input); }
此后如果还想增加位与,位或的操作,则只需要在该行代码中增加其函数名,并编写相对相应的函数
int(*pfarr[])(int, int) = { 0,add,sub,mul,div };
再在可选菜单中加入该选项即可。
void menu() { printf("********************************\n"); printf("** 1.and 2.sub **\n"); printf("** 3.mul 4.div **\n"); printf("********************************\n"); }