写 C 语言函数的时候,你是不是也遇到过这些情况?传个参数进去,函数里改了半天,外面的值愣是没变;明明写的代码能跑,可数据量大了就卡得不行;或者不知道为啥程序突然崩溃,提示内存错误?其实这些问题,多半和参数传递、内存分配、性能优化没搞明白有关。今天就掰开揉碎了说,从参数传递的门道,到内存分配的规矩,再到性能优化的小技巧,希望能帮到正在琢磨这些的你。
一、参数传递方式:值传递和地址传递,到底有啥不一样?
刚学函数的时候,最懵的可能就是参数传递了。为啥有的参数改了有用,有的改了白改?其实主要就两种传递方式,咱们一个个个明白。
值传递:简单说就是 “复制一份”。函数调用的时候,实参的值会被复制给形参,函数里改的是这份复制的副本,外面的实参一点都不受影响。比如这个例子:
void change (int x) {
x = 100;
}
int main () {
int a = 10;
change (a);
printf ("% d", a); // 这里打印的还是 10
return 0;
}
为啥 a 没变?因为 change 函数里改的是 x,x 只是 a 的复制品而已。这种值传递简单,适合传递简单的变量,比如 int、float 这些。但要是传个大数组或者结构体,复制传递就会复制很多数据,挺费内存的。
地址传递:也就是传指针,这回传的是变量的地址。函数里通过指针能直接找到外面的变量,改的是原变量的值。还是刚才的例子,改改参数:
void change (int *x) {
*x = 100;
}
int main () {
int a = 10;
change (&a);
printf ("% d", a); // 这里就变成 100 了
return 0;
}
指针传递的好处是省内存,尤其是传大的数据时,不用复制一份。但新手容易犯的错是啥?就是函数里直接改指针本身,比如 x = &b,这改的是指针的指向,不是原变量,外面还是没变。想改原变量,得用 * x。
有人会问,数组作为参数时,是哪种传递?其实数组传参本质是传地址,函数里拿到的是数组首元素的地址,所以在函数里改数组元素,外面是会变的。这也是为啥数组传参不用加 & 的原因。
二、内存分配:函数用的内存从哪来?栈和堆要分清
函数运行的时候,总得占点内存存变量、参数这些吧?这些内存主要来自两个地方:栈和堆。
栈内存:函数一调用,系统就会在栈上给它分配一块内存,叫 “栈帧”,里面放着参数、局部变量、返回地址这些。函数执行完,栈帧就自动释放了,不用咱们管。比如函数里定义的 int a = 5; 这个 a 就存在栈上。
栈的好处是方便,自动管理,但有个缺点:空间有限,一般就几 MB。要是在栈上定义个超大的数组,比如 int arr [1000000]; 很可能会栈溢出,程序直接崩了。我之前就犯过这错,调试了半天才发现是数组太大,栈装不下了。
堆内存:要是需要大内存,或者想让变量在函数结束后还能用,就得用堆了。堆内存得自己申请(用 malloc),用完了还得自己释放(用 free),不然会内存泄漏。比如:
int* createArray(int n) {
int arr = (int) malloc (n * sizeof (int)); // 堆上分配
return arr;
}
int main () {
int *p = createArray (1000000);
// 用完记得释放
free (p);
p = NULL; // 好习惯,避免野指针
return 0;
}
堆的空间大,但麻烦也多。忘了 free 会漏内存,重复 free 会出错,释放后再用会成野指针。新手怎么避免?记住 “谁申请谁释放”,申请了就记着在合适的地方 free,释放后把指针设为 NULL。
三、性能优化:函数怎么写才能跑得更快?
写对函数不难,写得又快又省资源才见功力。分享几个实用的小技巧,都是平时踩坑总结出来的。
- 传递大结构体用指针:结构体大的时候,值传递会复制整个结构体,费时间又费内存。用指针传递,只传个地址,快多了。比如:
// 不好的写法
void printStudent (Student s) { ... }
// 好的写法
void printStudent (Student *s) { ... }
- 减少函数调用次数:循环里频繁调用函数,尤其是带计算的,会拖慢速度。比如 for 循环里每次都调用 strlen (arr),不如先算好存起来:
// 较慢
for (int i=0; i
int len = strlen (arr);
for (int i=0; i
- 合理用静态局部变量:函数里的静态变量(static)只会初始化一次,下次调用还能用之前的值。适合那些需要累计计算的场景,比如统计函数被调用的次数,不用每次都传个计数变量了。
- 避免递归过深:递归虽然好看,但太深了会不断创建栈帧,容易栈溢出,还影响速度。能改用循环的就用循环,比如斐波那契数列,递归效率低,循环快多了。
有人可能会说,这些优化是不是小题大做?其实小程序看不出来,但数据量大了,差别就明显了。我之前写过一个处理上万条数据的函数,一开始没注意这些,跑起来要十几秒,优化后两秒就完事了。
最后说点我的心得,函数这东西,表面看是调用和返回,骨子里是内存和效率的权衡。新手刚开始不用追求一下子全吃透,先把参数传递和内存分配的基础打牢,写多了自然就有感觉了。遇到性能问题,别急着改代码,先想想哪里最费时间 —— 是参数传得不对,还是内存用得不合理?一点点排查,总能找到优化的点。希望这些能帮你把函数写得更明白,更顺手。
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。
还木有评论哦,快来抢沙发吧~