尧图精选

C/C++的指针与函数(指针函数与函数指针辨析)

🕒 发布时间:2026/9/15 21:48:43 📁 来源:尧图网络
文章目录指针函数语法指针函数的工程应用---malloc与new函数指针函数的地址函数的地址与函数的返回值函数指针的声明使用函数指针的调用函数函数指针数组使用typedef为函数指针取别名函数指针的工程应用---回调函数callback指针函数指针函数就是指针的函数是个函数是一个返回值为指针的函数。什么叫指针的函数呢为了便于理解笔者自己引入了一下的概念。注意这只是便于理解而且是笔者自己想的如果有问题还请指正首先先从函数来说起函数可以分为两种一种是有返回值的一种是没有返回值的void声明。在有返回值这一类函数中有int、float等等因为这一类函数执行完毕后相当于他的返回值占据了函数名称的位置如下intadd(inta,intb){returnab;}intmain{intcadd(1,2);//这里相当于12这个int值占据了add(1,2)这个位置}所以我们可以说例子当中的函数是个int的函数。明确了这个概念之后接下来我们来理解指针的函数这个概念指针的函数也就是说函数的返回值是个指针。一个指针代替了原来函数的位置。语法返回类型* 函数名(参数列表);例如int*createArray(intsize){int*arr(int*)malloc(size*sizeof(int));for(inti0;isize;i){arr[i]i1;//这里的arr[i]相当于*(arri)如果不懂可以去看我的上一篇博客}returnarr;// 返回指向动态分配数组的指针}函数的返回值属性是一个指针这个指针的类型属性是整型int。指针函数的工程应用—malloc与newmalloc作为内存分配的重要函数其在stdlib.h文件中函数原型是void*malloc(size_t);malloc就是一个返回值为void*的指针的指针函数。new函数同理函数原型如下void*operatornew(size_t);new也是一个返回值为void*的指针的指针函数。函数指针函数指针可以理解为函数的指针是个指针是一个指向函数的指针。函数的地址指针和地址是分不开的我们要理解函数的指针首先先要了解函数的地址。在程序编译时函数的地址是如何确认的呢首先编译器将每个源文件.c 或 .cpp编译成目标文件.o 或 .obj时在目标文件中函数的地址是相对于文件起始位置的偏移量相对地址这是因为编译器还不知道最终的内存布局无法确定函数的绝对地址。在链接时链接器将多个目标文件合并成一个可执行文件.exe 或 .elf链接器会为函数确定一个最终的地址。函数的函数名就是函数的地址。#includestdio.hvoidmyFunction(){printf(Hello, World!\n);}intmain(){printf(Address of myFunction: %p\n,(void*)myFunction);return0;}输出结果为Address of myFunction:0x400526我们知道在编译完成后函数地址已经确认了接下来我们看一下在程序的执行过程中程序如何通过地址来调用执行函数。程序执行的本质时PC程序指针计数器不断加一运行所指向内存单元的代码。当程序执行到函数部分时即PC程序指针计数器指向函数时发生函数调用。第一步调用者将参数压入栈中。参数压入顺序通常根据调用约定不同压入的方式也不同常见的有cdecl、stdcall、fastcall第二步调用者将返回地址函数的下一条指令的地址压入栈中。第三步PC程序指针计数器会更新为被调用函数的地址从而跳转到函数的代码段执行。第四步函数即被调用者创建栈帧由序言代码执行完成将调用者的栈帧指针ebp压入栈。设置新的栈帧基址ebp esp。为局部变量分配空间保存重要寄存器的值第五步开始执行函数代码。第六步执行完毕后将返回值存储到寄存器当中。释放栈帧恢复调用者的栈帧指针。第七步从栈中弹出返回地址到PC指针计数器当中。至此函数调用完成程序继续执行。额外提及一点在单片机的中断执行过程中与程序函数调用过程类似可以帮助理解。函数的地址与函数的返回值辨析函数的地址与函数的返回值program(function1);//这里传入program函数的是function1函数的地址program(function1());//这里传入program函数的是function1函数的返回值二者仅有一对括号的差别注意辨析。第一个是为了在program函数中调用function1函数第二个是先调用function1函数如何将function1函数的返回值当作参数输入到program函数中去。函数指针的声明函数的函数名就是函数的地址这一点已经被我们反复提及如下intfunction(inta,intb);function就是地址接下来我们来看函数指针的声明intfunction(inta,intb);int(*pf)(inta,intb);pffunction;这里的pf就是函数function的指针这时候大家可能会有疑惑function是个地址*pf所替换的是个地址的位置而pf却被解释为了指针对指针进行 * 运算解应用运算不应该得到的是pf这个指针所存储的地址的指向的内容的值吗为什么是个地址这里笔者来给出解释上文提过函数名就是函数的地址这一点是不变的但是由于函数本身是在程序段进行的存储也就是代表着对于“函数”的理解绝不能等同于数据的理解实际上函数名代表着函数本身也是代表着函数的指针也就是说function *function function这三者是完全等价的存在。上面说的可能有点晕我们也可以这么来理解函数指针首先来看数据指针的定义过程。inta0;int*pa;paa;这里*并没有解引用的含义有的仅仅是声明的含义。同理在函数指针的定义过程中*也没有解引用的含义有的仅仅是声明的含义。intfunction(inta,intb);int(*pf)(inta,intb);pffunction;//这里将function理解为了函数的地址还有一种理解方式将function理解为函数本身。function是函数那么(*pf)也是函数(*pf)是个函数pf就是指针对指针进行解引用就得到了函数也就是(*pf)。intfunction(inta,intb);int(*pf)(inta,intb);pffunction;//这里将function理解为了函数本身讲到这里笔者也成功的将自己绕晕。其实本质上函数函数的地址函数的指针都是函数名这三者是一个东西。就是因为函数是存储在程序段当中的所以无法将函数的地址运算方式同数据的地址运算方式对等起来。function *function function使用函数指针的调用函数intadd(inta,intb);intmain(){int(*pf)(inta,intb);pfadd;inttest1add(1,2);inttest2(*pf)(1,2);//使用*来对指针进行解引用得到函数return0;}intadd(inta,intb){returnab;}实际上下面这种调用函数也是正确的inttest3pf(1,2);//pf为指针存储的是地址地址即为函数名在这里 C Primer Plus给出了解释感觉这种设计真的是难以理解不过确实也证实了上面提及的函数函数的地址函数的指针都是函数名这三者是一个东西函数指针数组语法返回类型(*函数指针[n])(参数列表);//例子void(*funcPtrArray[3])();关于为啥在这个位置加上[ n ]我认为 C Primer Plus这里的这个解释很好我之间粘到下面。使用typedef为函数指针取别名常见的typedef的使用方法为typedef原类型 新类型名;但是在函数指针这里略微不同其语法格式为typedef返回类型(*函数指针)(参数列表);typedefvoid(*PF)(int);// 定义 PF为函数指针类型的别名实际上在这里原类型是返回类型 ( * )(参数列表)即void ( * )(int)而新类型名则是对应函数指针即PF。这样我们在创建一个void ( * )(int)形式的函数指针时就可以使用如下的形式PF pf;//相当于以下形式typedefvoid(*pf)(int);函数指针的工程应用—回调函数callback// 回调函数类型typedefvoid(*Callback)(int);// 接受回调函数作为参数的函数voidprocess(intvalue,Callback callback){printf(Processing value: %d\n,value);callback(value);// 调用回调函数}// 回调函数实现voidprintValue(intvalue){printf(Callback: %d\n,value);}intmain(){process(10,printValue);// 传递函数指针return0;}上述例子中使用了函数指针的方式创建回调函数还有一种弱定义__weak的方式相信学过STM32单片机HAL库一定不会对回调函数陌生在HAL库中就是这种弱定义__weak的方式。这篇文章主要是将函数指针的所以在这里就不多赘述关于弱定义的内容了。为什么要使用回调函数为了实现低耦合编程代码之间的耦合度过高会导致难以移植修改等问题使用回调函数就可以将部分的实现分离出去比如在单片机中中断处理的函数就是以回调函数的形式出现的用户只需要修改回调函数满足功能即可不用修改整个中断服务函数
上一篇/下一篇内容由系统自动关联 返回资讯列表 →