很多人学C语言,卡在指针那一章就再也没翻过去。我见过一个朋友,书签停在“指针与数组”那页整整两年,书都泛黄了,人还在原地。这不是智商问题,是大多数教材把顺序搞反了——先讲变量、循环、函数,最后才抛出指针这个大魔王,学生早被前面那些平淡无奇的内容磨没了耐心。我的建议是:如果你已经会一点编程,直接跳到指针,硬啃三天,啃不动再回头补基础。C语言的核心就两件事:内存怎么摆,数据怎么流。指针就是内存的地址,数组就是连续的内存块,函数调用就是栈上压数据。想通这层,剩下的都是语法糖。
我刚开始写C的时候,最烦的是#include <stdio.h>这行。凭什么每次都要写?后来自己写了个小工具,把常用的头文件、宏定义、甚至main函数框架打包成一个模板,用脚本一键生成。这招省了我至少两百次敲键盘的时间。别小看这种偷懒,C语言本身不提供什么高级抽象,你得自己给自己造轮子。比如malloc和free配对使用,写多了容易漏,我就用宏包一层,每次分配内存自动记录文件名和行号,释放时检查是否匹配。虽然土,但管用。
说到内存,有个坑我踩过三次。第一次是忘了给指针分配空间就往里写数据,程序直接段错误,连报错信息都没有。第二次是free之后没把指针置空,后面又误用了一次,结果释放了野指针指向的垃圾内存。第三次最冤——在循环里反复malloc却忘了free,跑了一个小时内存吃满,系统直接卡死。后来我养成了习惯:任何malloc后面立刻写free,中间的逻辑再慢慢填。这招叫“防御性编程”,听起来很正式,其实就是怕死。
数组和指针的关系,教材总爱画一堆箭头图。我的理解很简单:数组名在大多数情况下会退化成指向首元素的指针。但有两个例外——sizeof和取地址&。比如int arr[10],sizeof(arr)是40,sizeof(指针)在64位系统上是8。这个区别在传参时特别要命。函数参数里的int arr[]和int *arr完全等价,你没法在函数内部用sizeof算出数组长度。所以老手都会额外传一个size_t len参数。我见过一个开源项目,因为漏传了这个长度,导致缓冲区溢出,被黑客利用拿下了服务器。C语言不帮你检查边界,你得自己盯着。
字符串处理是另一个雷区。strcpy、strcat这些函数没有长度检查,目标缓冲区不够大就直接踩内存。我现在的做法是:能用snprintf就不用sprintf,能自己写循环拷贝就不调库函数。虽然代码长一点,但至少不会莫名其妙崩溃。有个同事曾经用strcpy拷贝用户输入,结果有人输入了超长字符串,程序直接跳转到非法地址执行,那场面跟黑客电影似的。
调试C程序,printf大法好。别急着上gdb,先在关键位置打印变量值和地址。指针问题十有八九是地址不对——要么是NULL,要么是已经释放的内存,要么是越界访问。打印地址用%p,打印size_t用%zu,别用%d,否则在64位系统上会截断。我见过有人用%d打印指针,结果只拿到低32位,调试了一下午才发现是格式符写错了。
学C语言没有捷径,但可以少走弯路。别一开始就追求写什么操作系统、编译器,先写点小工具:十六进制查看器、简易计算器、链表增删改查。写多了自然就懂了。指针不是洪水猛兽,它只是内存的地址而已。你每天用的手机、电脑,底层全是C在跑。想想这个,是不是觉得那些segmentation fault也没那么可怕了?