资讯动态

c语言学习--函数与指针

发布时间:2026/8/23 7:27:44 来源:尧图企业网站定制
指针传递地址传递、引用传递值传递实参的值是非地址类型实参将自己的值的拷贝传递给形参形参与实参只有值相等是两个独立的变量。指针传递实参是地址指针变量、数组名形参接收到时地址函数内部使用形参可以修改外部的数据。#include stdio.h void fn1(int n) { n100; } void fn2(int *n) { *n100; } int main() { int num10; printf(num%d\n,num);// 10 fn1(num); printf(num%d\n,num);// 10 fn2(num); printf(num%d\n,num);// 110 return 0; }为什么调用fn1之后num的值还是10呢为什么fn2就是110呢调用fn1(num);这是值传递。main里面num 10调用 fn1 的时候把 num 里面的 10 复制一份给到 fn1 的形参变量n。⚠注意这里是复制一份fn1 里面的n是 fn1 函数自己栈上的局部变量和 main 的num是两块完全独立的内存两个盒子。调用fn2(num);num拿到 main 中 num 的内存地址把这个地址传给形参指针n。fn2 的指针变量n保存的是 main 里面 num 的地址。*n 100*解引用顺着地址直接找到 main 函数的 num 变量修改它里面的数据。main 的 num 直接变成 110。fn2 的指针变量n本身函数结束之后同样会销毁但是它已经通过地址把外面 num 的值改完了。指针函数是个函数返回指针地址注意指针函数不能返回局部变量的地址函数调用结束局部变量被销毁可以返回静态局部变量的地址。#include stdio.h #include string.h // 定义指针函数 返回两个字符串中较长的一个 char *get_longger_string(char *str1, char *str2) { return strlen(str1) strlen(str2) ? str1 : str2; } int main() { char *city Shanghai; printf(较长字符串%s \n, get_longger_string(city, Beijingnan)); return 0; }注意#include stdio.h // 定义函数 int *get_value() { // num 是局部变量函数调用结束会被销毁 int num 250; return num; } int main() { int *ptr get_value(); printf(ptr%p \n , ptr); printf(*ptr%d \n , *ptr); return 0; }错误现象: 编译的时候这串代码是没有问题的但是函数返回局部变量地址后运行时出现内存访问错误原因num是局部变量调用完之后其中的数据会被销毁。调用完之后是能拿到地址但是拿到的地址是销毁后的地址。现代系统会检测到这种错误并将地址重置为0NULL指针如何解决呢设置为静态局部变量。静态局部变量函数调用结束不销毁static int num 250;函数指针是个指针指向一个函数#include stdio.h // 定义函数 double avg(int a, int b) { return (a b) / 2.0; } int main() { // 打印地址 printf(avg: %p \n, avg); printf(avg: %p \n, avg); printf(*avg: %p \n, *avg); return 0; }打印出来的全是地址这是为什么1.avg在 C 语言中函数名会隐式转换为函数的入口地址代表函数代码在内存中存放的起始地址。avg→拿到函数代码块的首地址。2.avg取函数的地址对函数名取地址得到的还是函数入口地址。avg和avg输出结果完全一样。普通变量num是值num是地址两者不一样。 函数特殊avg、avg得到同一个函数入口地址。3.*avg*解引用函数指针。 函数指针解引用得到的还是这个函数本身又会再次转换成函数地址。 所以*avg依然打印函数入口地址。函数指针这套规则很特殊avg、avg、*avg、**avg、***avg全部等价全都是函数入口地址 你写多少层*解引用结果不变。// 调用函数 printf(%.2lf \n, avg(10, 40)); printf(%.2lf \n, (avg)(10, 40)); printf(%.2lf \n, (*avg)(10, 40));算出来的结果都是25.00对于函数avg、avg、*avg、****avg全部是同一个函数入口地址调用的是同一个函数传入同样参数返回值完全一样#include stdio.h // 定义函数 double avg(int a, int b) { return (a b) / 2.0; } int main() { // 函数指针 --------------------------------------------- // 定义函数指针 指针函数 avg 一下三种写法都一样 // double (*ptr)(int, int) avg; // double (*ptr)(int, int) *avg; double (*ptr)(int, int) avg; // 打印 printf(ptr: %p \n, ptr); printf(*ptr: %p \n, *ptr); printf(ptr: %p \n, ptr); // 使用函数指针调用avg printf(%.2lf \n, (*ptr)(70, 40)); printf(%.2lf \n, ptr(70, 40)); // printf(%.2lf \n, (ptr)(70, 40));// 这个不行因为这个是指针自己的地址 return 0; }为什么打印printf(ptr: %p \n, ptr);结果跟前面两个不一样呢因为指针ptr存放的值是函数的地址但是指针有他自己的地址所以取址后得到的是ptr自己的地址。总而言之有了函数的地址就可以调用函数总结函数的代码存放于内存的代码区调用函数的本质就是使用函数代码的地址去调用函数名、函数名、*函数名 都可以获取到函数代码的地址都可以调用函数。回调函数函数指针可以作为某个函数的参数来使用的简单来讲回调函数就是一个通过函数指针调用的函数。就是当参数的才是回调函数。回调函数作为另一个函数的参数在另一个函数的内部可以调用回调函数。// 函数指针声明格式返回值类型 (*指针名)(参数列表);#include stdio.h #include math.h // 定义函数, 需要接收一个函数作为参数 // cb 的类型是 void (*)(), 是个指针指向 void () 类型的数据 void A(void (*cb)()) { // 调用cb指向的函数 cb(); } void fn1() { printf(hello fn1); } // 定义函数实现对数组进行初始化赋值需要接收一个函数作为参数 void initarray(double *arr, int len, double (*cb)(double)) { for (int i 0; i len; i) { arr[i] cb((double)i); } } void fn2() { printf(hello fn2); } // 定义 initArray 的回调函数 double half(double n) { return n / 2; } int main() { A(fn1); printf(\n); double nums[6]; initarray(nums, 6, half); for (int i 0; i 6; i) { printf(%.2lf\n, nums[i]); } printf(\n); initarray(nums, 6, sqrt); for (int i 0; i 6; i) { printf(%.2lf , nums[i]); } printf(\n); initarray(nums, 6, cbrt); for (int i 0; i 6; i) { printf(%.2lf , nums[i]); } printf(\n); return 0; }A(fn1);fn1函数名转换为函数入口地址赋值给形参函数指针cb 在A函数内部执行cb()等价执行fn1()。initarray(nums,6,half);把half函数地址传给形参double (*cb)(double) 循环中arr[i] cb((double)i);实际调用half(i)把返回值赋值给数组。库函数也可以作为回调回调函数思想回调的核心思想框架函数只负责流程把具体业务逻辑交给外部传入的回调函数。 例子initarray只负责循环遍历数组框架生成什么数字交给回调函数half/sqrt/cbrt业务逻辑。 后续想要新的数组赋值规则只需要写新回调函数不用修改initarray内部代码。嵌入式开发中大量使用回调按键中断回调、定时器回调框架触发条件到了自动调用我们写好的回调函数。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价