关于C数组、指针及数组指针的语法疑问:这段代码为何能运行?
哈哈,这段代码乍一看确实有点绕,我来给你拆解清楚~
先搞清楚各个变量的类型
const int * const buf:这是一个指向const int的常量指针——意思是buf本身不能被修改(第二个const),它指向的int值也不能被修改(第一个const)。当你把数组传给这个函数时,数组名会自动退化成指向第一个元素的指针,所以buf其实是数组首元素的地址。const int (*p)[]:这是一个指向const int数组的指针——简单说,p是个指针,它指向的是一整个数组(这里数组大小没指定,属于不完整类型)。
为什么p = (const int(*)[]) buf能正常工作?
你把buf(数组首元素的地址)强制转换成了const int (*)[]类型,相当于手动告诉编译器:“别把这个地址当成单个int的指针,它其实是一个数组的起始地址”。
当你写(*p)的时候,是解引用这个数组指针,得到它指向的数组(C允许对不完整类型的数组指针进行解引用,只要后续下标访问不越界)。而(*p)[n]等价于*(*p + n)——这里的*p本质上就是buf的地址(因为p指向的数组起始地址就是buf),所以*p + n和buf + n是同一个地址,解引用后自然就是数组的第n个元素,和直接用buf[n]完全等价。
编译器之所以会警告,是因为const int *和const int (*)[]是不兼容的类型,强制转换相当于你“ override ”了编译器的类型检查,告诉它“我知道我在做什么”。
为什么p = (const int(*)[]) &buf会触发段错误?
问题出在&buf的含义上:buf是函数的参数,它是一个局部指针变量(存在栈上),&buf取的是这个指针变量本身的地址,而不是原数组的起始地址!
当你把&buf强制转换成const int (*)[]后,p指向的是栈上buf变量所在的位置。这时候(*p)[n]会把栈上的这个位置当成数组起始地址来访问:
- 当n=0时,你取的是栈上buf变量的前
sizeof(int)字节(比如64位系统中指针占8字节、int占4字节,那就是buf指针值的低4字节),把它当成int累加,这已经是错误的值; - 当n增大时,你会继续访问栈上buf变量后面的内存,很快就会超出合法范围,触发段错误。
本质上,&buf的类型是const int * const *(指向“指向const int的常量指针”的指针),和const int (*)[]完全不是一回事,强制转换后访问自然会出问题。
总结
这段代码能运行,本质是靠强制转换“欺骗”了编译器,把单个元素的指针当成数组指针来用,但实际访问的内存还是原来的数组。不过这种写法非常不规范、可读性极差,实际开发中完全没必要这么写,直接用buf[n]或者*(buf +n)就够了。
内容的提问来源于stack exchange,提问作者Oskar Enoksson

