四、数组传参和指针传参
4.1 一维数组传参
#include<stdio.h>
int main()
{
int arr[] = {1,2,3};
test(arr);
return 0;
}
如果要把数组arr传给test,test的形参可以设置成什么样呢?
第一种用数组的方式接收——test(int arr),test(int arr[3]),test(int arr[])。或者是以指针的方式接收,因为数组名是首元素的地址——test(int* p)。
指针数组的传参,如果这时候将一个指针数组int*arr[10] 传给test,形参又可以怎么接收呢?与上面类似的,可以写成test(int(*arr)),test(int **arr),第二种传参的方式是因为数组名是首元素的地址,而首元素又是一个指针,从而是一个arr是一个二级指针。
4.2 二维指针的传参
#include<stdio.h>
int main()
{
int arr[3][5] = {0};
test(arr);
return 0;
}
类似的,对于一个3行5列的二维数组,它传参给test时候,test可以以数组的方式接收,也可以以指针的方式接收。
具体的实现方式是——test(int arr[3][5]),test(int arr[][5]),test(int (*arr)[5]).
4.3 一级指针的传参
#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;
}
上图就是一个一级指针p传给print函数的方式。
思考:
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
答案:
#include<stdio.h> int main() { int a = 1; int arr[] = {1}; int* arr[] = {&a}; int arr[3][5] = {0}; print(&a);//将一个整形的地址传入 print(arr);//整形数组的数组名就是首元素地址 故也是一个一级指针,也可传入 print(arr[0]);//一个整形指针数组的某一个元素都可以传入print print(*(arr+i)+j)//其中i和j是保证不越界访问下的合适偏移量 //一个二维数组的数组名相当于第一行的一维数组的地址 //只要在数组范围内对arr加减都可以得到任意一行的某个元素的地址。 //再解引用,相当于得到了某一行一维数组的数组名,数组名就是首元素地址。 return 0; }
上述给出了基本能用到的所有传入一级指针的方式,可能还有其他的很多套娃传参方式,这里不一一列举了。
4.4 二级指针传参
顾名思义就是将二级指针当作参数传给函数 和一级指针十分类似,不赘述。
下面不加解释地给出一些二级指针的传参方式。
#include<stdio.h>
int main()
{
int a = 10;
int *p = &a;
int** pp = &p;
int* arr[] = {p};
int** krr[] = {pp};
print(pp);//p是指针 pp是它的地址 也就是二级指针
print(&p);//和上面是等价的
print(arr);//指针数组的数组名是首元素地址 首元素是指针 从而数组名是二级指针
print(krr[0]);//一个二级指针数组的任意元素都是二级指针
return 0;
}
五、函数指针
函数指针——一个指向函数的指针,可以通过它来调用函数,这为后面讨论回调函数打下基础。
假设现在实现了一个test函数,想要得到它的地址,具体的声明是什么样子?
void test(int n)
{
return 0;
}
int main()
{
void (*pt)(int n) = &test;//pt是函数指针。
//一个函数指针的声明——返回值+(*函数指针名)+(函数的参数);
printf("%p\n",pt);
return 0;
}
一个数组名是它的首元素地址,那一个函数名呢?
答案是就是它自己的地址。
void test(int n)
{
return 0;
}
int main()
{
void (*pf)(int n) = test;
void (*pt)(int n) = &test;
printf("%p\n",pt);
return 0;
}
输出的结果是一样的,说明函数名也是函数的地址。
有了函数指针,就可以通过函数指针来调用函数,和一般的指针变量一样,只要解引用就好,不过函数指针解引用后还需要传参,毕竟是一个函数。
#include<stdio.h>
void test(int n)
{
printf("%d",n);
return 0;
}
int main()
{
void (*pf)(int n) = test;
(*pf)(1);//解引用进行函数的调用 一定要用括号把pf和星号括起来 否则pf会先和后面的括号结合,出现错误
return 0;
}
事实上,对于函数指针变量,不加星号解引用也是可以直接调用函数的!
#include<stdio.h>
void test(int n)
{
printf("%d",n);
return 0;
}
int main()
{
void (*pf)(int n) = test;
pf(1);//解引用进行函数的调用
return 0;
}
请思考一下这两个代码的意思
1、(* (void (*)() ) 0) ();
2、void (* signal(int , void(*)(int) ) ) (int) ;
答案:(void (*)())0表示将0强制转化为一个返回值为void 空参的函数指针。用星号解引用得到了这个函数,最后一个括号用于传参。
signal(int , void(*)(int) ) signal先和括号结合,它的参数是一个int和一个函数指针。由于*要和signal函数的返回值结合 说明它的返回值是一个函数指针,括号外面的void和int是它的返回类型和形参类型。
六、函数指针数组
声明的方式:函数的返回类型+(*数组名[元素个数])+(函数的形参类型)
int (*parr1[10])();
代码中parr1就是数组名