
数组名不是指针可它偏偏总被当指针用那它到底算个啥呢。我学C语言快两个月了光是“a到底怎么算出来的”这个问题就反复查了三回标准文档问了两个同学还扒了GCC的汇编输出。不是我不想弄懂是网上太多说法自相矛盾——有的说“数组就是指针”有的又说“根本不是”连老师PPT上写的和教材小字批注都对不上。后来我干脆拿纸笔画内存int a {1,2,3,4,5};。我标出每个元素地址手动算a2是多少再对照a发现真的一样。但a写在纸上时我愣住了地址数字和a并排写着可我给它标类型时硬生生多画了个括号写成int (*)——这玩意儿编译器认识我一开始真不认识。我试了sizeof(a)和sizeof(a)结果一个是20一个是8。就这俩数字让我盯了十分钟。20是5个int占的总空间8是地址本身大小。原来“值一样”和“东西一样”真不是一回事。a不是变量它没内存地址可存a也不是取“a这个符号”的地址是取整个数组块的起始位置。我写了个最简单的程序cint a {10,20,30};printf(%p %p\n, (void*)a, (void*)a);运行结果两行地址一模一样。我差点又信了“它俩就是一回事”。直到我把a1和a1打出来——前者是a地址4后者直接跳了12字节3×4。这才明白指针加1走多远全看它指的“东西”有多大。a指的不是int是一整个int所以加1就跨过整个数组。下标a这事课本说等于*(ai)。我原来以为这只是写法不同直到看godbolt.org里生成的汇编a和*(ai)编译出来真的一条指令都不差。它不是“看起来像”是编译器规定它就必须是。但a为啥错因为a根本不是能放地址的盒子它连盒子都不是是贴在内存块上的一张标签。你不能让标签自己动但你能拿个新盒子比如int *p a;把标签上的地址抄进去再让盒子动。函数传数组最骗人。我写void f(int arr10)还傻乎乎在函数里sizeof(arr)结果永远是8。我删掉10写成int arr还是一样。问了助教才懂C标准白纸黑字写了形参里的数组声明编译器当场就替换成指针。它不存长度不存边界只存首地址。你传进去的是a函数里拿到的是a转换后的int *仅此而已。陷阱真容易踩。有次我把a直接赋给int *pGCC警告我“类型不匹配”我没理结果程序跑一半崩了。后来加-Wall重编警告清清楚楚写“initialization from incompatible pointer type”。还有一次越界访问a程序居然没报错还打印出一个鬼数字我真以为它“能用”。直到换台机器跑直接段错误。UB不是吓唬人是它真不管你会不会出事。我拿int (*p)10 a;和int *q a;对比着写了几遍。前者p1跳40字节后者q1跳4字节*p是整个数组*q只是一个int。要访问a用q是q用p得写成(*p)——多一对括号少一对就错。这不是绕口令是类型系统在拉警报。结构体里的数组让我又卡了一次。struct { int x; } s;我打s.x和s.x值一样但类型一个是int (*)一个是int *。我试了printf(%d, sizeof(s.x));输出8sizeof(s.x)也是8。但把它们传给函数参数类型写错编译器立马翻脸。现在写代码前我先想三件事这个东西类型是啥sizeof它多大它得到什么想清楚了a、p、*(pi)在我脑子里就真成了一回事——不是“差不多”是底层指令一模一样只是写法不同。我删掉了所有笔记里“数组就是指针”这几个字。改成“数组名在大部分地方会自动变成指向第一个元素的指针但它自己从来不是。”今天用-O2编译三个遍历写法看了汇编三条mov指令排得整整齐齐没差一个字节。