C语言-函数指针

📅 2026/7/31 5:07:56 👁️ 阅读次数 📝 编程学习
C语言-函数指针

回顾:数组指针,本质是指向数组首地址的指针,操作一维、二维数组均只需要知道数组首元素即可,二维数组就是一维数组的嵌套,实际上为基类型的数组。通过指针访问二维数组中的数据:

p+i //第i行的地址 p是的数据类型 int(*)[4] *(p+i) //切换 到第2维 *p三步运算 ==> *(p+i) 代表就是一个int[4]的数组 *(p+i)就是第i行 的数组名 *(p+i) + j //第2维 对应的 数组 ,地址 偏移到 第j个元素 *(*(p+i) + j) //第2维 对应的 第j个元素
p+i //行地址 *(p+i) //行地址 转换为列地址 *(p+i) +j //列地址 进行偏移 *(*(p+i)+j) //访问第i行第j列的元素

字符型指针数组:

char *s[] = {"hello","world","china"};

数组修饰符基类型为char *型因此数组内存储的是三个char *类型的元素,分别指向三个字符串常量区的地址,对应着三个常量字符串。

二级指针:如果要存放的是一个一级指针变量的地址就需要使用二级指针指向一级指针

char *s = "hello"; char **p = &s; char** *r = &p;

一级指针:如果要存放的是一个普通变量的地址,直接使用一级指针即可

int a = 10; int * p = &a;

练习:选择排序,定义一维字符型指针数组,对指针数组内指针指向的字符串进行排序

void select_point_twoArraystr(char **a,int len) { int i=0; while(i<len-1) { int j = i+1; while(j<len) { if(strcmp(*(a+i),*(a+j))>0) { char * temp = *(a+i); *(a+i) = *(a+j); *(a+j) = temp; } j++; } i++; } }

由于排序的是指针指向的内容,因此需要使用二级指针来指向一级指针。依次遍历从指针数组内的指针指向的字符串的大小,外循环控制当前比较的第几个指针元素指向的字符串,内循环控制与后面每一个指针指向的字符串进行比较。每比较一次,循环变量自增,与后面的指针指向的字符串继续比较。循环完一轮后,外循环变量自增,排序好的元素指针向后偏移一个。

练习:字符型号指针数组进行排序后继续二分查找

void find_point(char **a,char *b,int len) { char **begin = a; char **end = a+len-1; char **mid = begin + (end-begin)/2; while(begin <= end) { mid = begin + (end-begin)/2; if(strcmp(*mid,b)>0) { end = mid - 1; }else if(strcmp(*mid,b)<0) { begin = mid + 1; }else { printf("find yes\n"); return ; } } printf("not found\n"); }

同样使用二级指针,接收进行二分查找的指针数组的地址,定义起始地址指针begin指向指针数组的首地址,结束地址指针指向指针数组的末地址。计算中间指针进入循环。循环内重新求取中点,用 strcmp 对比中点字符串与目标串,若中点字符串字典序更大就把左区间右边界前移,更小就把右区间左边界后移,相等则提示找到并直接返回。当 begin 超过 end 时循环终止,说明遍历完所有区间都未匹配,输出查找失败。

指针函数:函数名就是函数的入口地址 ,一个函数,它的返回值是指针

char * strcpy()
char * strcat()

函数指针:主体为指针变量

int (*fun)(int x);

括号的优先级最高,因此结合之后,fun为函数指针类型,指向返回值类型为int型,传入一个参数,且参数为int型的这类型这类函数。函数的指针 目的是 希望通过指针的方式使用到函数

回调函数:把一个函数 A 的地址,作为参数传给另一个函数 B,由函数 B 在内部去调用函数 A,A 就叫回调函数。本质依托:函数指针实现。

练习:以回调函数实现,输入两个数字和运算符,进行加减乘除然后输出结果。

int sum(int a,int b) { return a+b; } int sub(int a,int b) { return a-b; } int mux(int a,int b) { return a*b; } int div1(int a,int b) { return a/b; } int div2(int a,int b) { return a%b; } void processData(int a,int b,int (*pfunc)(int,int)) { printf("result = %d\n",pfunc(a,b)); } int main(int argc, const char *argv[]) { int a,b; char c; int (*p1)(int ,int) = sum; int (*p2)(int ,int) = sub; int (*p3)(int ,int) = mux; int (*p4)(int ,int) = div1; int (*p5)(int ,int) = div2; printf("input tow num and operator:"); scanf("%d%c%d",&a,&c,&b); switch(c) { case '+': processData(a,b,p1); break; case '-': processData(a,b,p2); break; case '*': processData(a,b,p3); break; case '/': processData(a,b,p4); break; case '%': processData(a,b,p5); break; }

首先定义加减乘除取余五个双目运算函数,通过函数指针变量分别指向这些运算接口,再封装 processData 统一调用函数指针执行运算并打印结果,在主函数接收用户输入的两个整数与运算符,利用 switch 匹配符号传递对应运算函数指针,完成简易计算器四则及取余计算。

编写系统快速排序中的回调函数:

void qsort( void *base, //要排序的数组的起始地址 size_t nmemb, //要排序的数组的元素个数 size_t size, //数组中单个元素的大小 int (*compar)(const void *, const void *) //回调函数的指针 compare --- 用来指定数组元素比较的规则 );
#include<stdio.h> #include<stdlib.h> #include<string.h> int compar(const void* a,const void* b) { const int *p = a; const int *q = b; return *p - *q; } int compar_str(const void* a,const void* b) { const char *p = a; const char *q = b; return strcmp(p,q); } int (*q) (const void *,const void *) = compar_str; int main(int argc, const char *argv[]) { int a[] = {6,5,4,3,2,1,9,8,10,7}; char b[][10] = {"F","W","O","E","C"}; int len = 5; qsort(b,len,10*sizeof(char),q); for(int i=0;i<len;i++) { printf("%s\n",b[i]); } putchar('\n'); return 0; }

void *类型指针

void *空类型指针,可以接收任意类型变量的地址,不能直接解引用、不能做指针偏移运算,必须强制转换成具体类型指针后才能操作。主要用于实现泛型回调函数的底层基础,使用空指针接收后在进行强制类型转换,从而实现回调函数对多种功能的支持。

首先编写两个回调比较函数,compar用于整型数值大小相减返回差值,满足整数升序排序规则,compar_str通过strcmp字符串库函数完成字符串字典序比对;随后定义函数指针变量 q 指向字符串比较回调函数,在主函数中分别准备 int 一维数组、二维字符字符串数组,调用qsort对字符串数组传入元素个数、单个元素占用字节长度与自定义比较函数指针,由库函数内部通过回调指针反复调用比对逻辑完成排序,最后循环遍历打印排序后的字符串数组,本质是利用函数指针把排序算法框架和元素比较规则解耦,一套 qsort 接口可适配整数、字符串等不同类型数据的排序需求。

堆区空间的使用:

堆是操作系统管理、程序员手动申请手动释放的内存空间,栈由编译器自动分配释放,堆完全靠代码控制;生命周期从malloc申请开始,直到free释放程序结束才回收。

void *malloc(size_t size);
  • 功能:申请size 字节未初始化堆内存,内容随机垃圾值
  • 返回:成功返回堆起始地址(void*),失败返回 NULL
// 申请10个int空间 int *p = malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc fail"); return -1; }

空间释放:归还堆内存给操作系统,避免内存泄漏

  1. 同一块堆不能多次 free双重释放崩溃;
  2. free 后指针变成野指针,最好立刻置 NULL:传入参数就是malloc申请的内存地址入口指针
void free(void *ptr);

注意:
1. free释放 一般 建议将对应指针设置NULL //free(p)之后,此时p 悬空指针 (原型指向有效空间,但是后来空间释放了,变的无效,但是p仍然指向这块空间)
2. 如果只malloc但是没有free,此时会发生内存泄露 //内存泄露 --- 使用了空间,但是没有释放,导致一直被占用
3. 告诉操作系统,这块空间用完了,你可以拿回去自由分配
4. malloc和free要成对出现
5. malloc申请到的空间 是一片连续空间
6. 不要多次free同一块空间
7. 注意:指针函数中,不能返回局部变量(auto这种局部变量-空间开在栈上的)的地址

练习:定义一个整型数组空间在堆上,并输入数字,最后进行求和输出

main函数的参数:
int main(int argc, const char* *argv)
argc -- 表示命令行参数的个数
argv -- 表示命令行参数的 指针数组 命令行参数 起始都是 字符串 以NULL作为结尾的数组