C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。(上):https://developer.aliyun.com/article/1513043
4.3 一级指针传参
#include <stdio.h> void print_arr(int* p, int sz) { int i = 0; for (i = 0; i < sz; i++) { printf("%d ", *(p + i)); } } int main() { int arr[10] = { 0,1,2,3,4,5,6,7,8,9 }; int* p = arr; int sz = sizeof(arr) / sizeof(arr[0]); //一级指针p,传给函数 print_arr(p, sz); return 0; }
思考:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
一级指针传参,一级指针接收:
void test1(int* p) {} void test2(char* p) {} int main() { int a = 10; int* pa = &a; test1(&a); // √ test1(pa); // √ char ch = 'w'; char* pc = &ch; test2(&ch); // √ test2(pc); // √ return 0; }
4.4 二级指针传参
#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); // 取p指针的地址,依然是个二级指针 return 0; }
思考:当函数的参数为二级指针的时候,可以接收什么参数?
void test(int** p) // 如果参数时二级指针 { ; } int main() { int* ptr; int** pp = &ptr; test(&ptr); // 传一级指针变量的地址 √ test(pp); // 传二级指针变量 √ //指针数组也可以 int* arr[10]; test(arr); // 传存放一级指针的数组,因为arr是首元素地址,int* 的地址 √ return 0; }
5. 函数指针
指针数组:存放指针的数组。数组指针:指向数组的指针,
函数指针:指向函数的指针,存放函数地址的指针。
取函数指针地址:函数也是有地址的,取函数地址可以通过 &函数名 或者 函数名 实现。
注意事项:
① 函数名 == &函数名 (这两种写法只是形式上的区别而已,意义是一模一样的)
② 数组名 != &数组名
取函数地址:
#include<stdio.h> int Add(int x, int y) { return x + y; } int main() { // 函数指针 - 存放函数地址的指针 // &函数名 - 取到的就是函数的地址 printf("%p\n", &Add); printf("%p\n", Add); return 0; }
输出的是两个地址,这两个地址是 test 函数的地址。
那函数的地址要想保存起来,怎么保存?
看代码:
void test() { printf("hehe\n"); } //下面pfun1和pfun2哪个有能力存放test函数的地址? void (*pfun1)(); void *pfun2();
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参
数(有参数要写上参数),返回值类型为void。
void test(char* str) { } int main() { void (*pt)(char*) = &test; return 0; }
5.1函数指针的运用:
#include<stdio.h> int add(int a,int b) { return a + b; } int main() { int a = 0, b = 0; scanf("%d %d", &a, &b); int (*p)(int, int) = &add; //int (*p)(int, int) = add;也行 printf("%d\n", (*p)(a, b)); return 0; }
那能不能把 (*pf) (3,5) 写成 *pf (3,5) 呢?
不行,这么写会导致星号对函数返回值进行解引用操作
字符指针 char* 型函数指针:
#include<stdio.h> void Print(const char* str) { printf("%s\n", str); } int main() { void (*p)(const char*) = Print; // p先和*结合,是指针 (*p)("hello wrold"); // 调用这个函数 return 0; }
阅读两段有趣的代码:
//代码1 (*(void (*)())0)(); //代码2 void (*signal(int , void(*)(int)))(int);
《C陷阱和缺陷》这本书中提及这两个代码。
代码1图解:
代码2图解:
文字解析:
int main() { (*(void (*)())0)(); //()0 把0强制类型转换 //void (*)()是函数指针 (void (*)())0;//把0强制类型转化为函数指针类型 (*(void (*)())0);//解引用最后加()调用函数 //(*(void (*)())0)();所以这是调用0地址处的函数,该函数无参,返回类型是void void (*signal(int, void(*)(int)))(int); //按优先级signal和()结合,所以signal是函数名 //signal(int, void(*)(int))是函数名和函数类型, //第一个类型是int 第二个类型是函数指针 //一个函数,函数名和函数类型讨论了,剩下是就是函数返回类型 //void (*)(int)就是函数的返回类型,为函数指针 //void (*signal(int, void(*)(int)))(int);所以这是一个函数的声明 //如果可以写成void (*)(int) signal(int, void(*)(int))就容易理解,但语法不支持 //但语法支持的太复杂,如何简化: //对void (*)(int)重命名成pfun_t //但语法又不能写成typedef void (*)(int) pfun_t //所以写成以下格式 typedef void(*pfun_t)(int); pfun_t signal(int, pfun_t); return 0; }
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。(下):https://developer.aliyun.com/article/1513049