关于分配小于结构体大小的缓冲区是否触发未定义行为的技术问询
这是个挺有意思的理论问题,我来给你拆解清楚:
首先得抓牢C标准的核心规则:要通过结构体指针访问它的成员,这个指针必须指向一个有效的、完整的结构体对象。而一个有效的结构体对象,要求它所在的内存块大小至少等于sizeof(struct S)——毕竟结构体的大小是标准定义的、包含所有成员和必要填充字节的总长度。
回到你贴的代码:
struct S { char b[256]; }; struct S *buf = malloc(128); buf->b[0] = 1;
这里sizeof(struct S)至少是256字节(如果没有额外的对齐填充的话),但你只分配了128字节。这意味着buf指向的内存根本装不下一个完整的struct S对象——就像你租了一块只能放下半栋房子的地皮,却硬要把它当成一整栋房子来用。哪怕你只碰了b[0]这一个字节,从C标准的角度来说,这已经属于未定义行为了。
你可能会纳闷:“我明明只访问了分配到的内存区域,为啥会有问题?” 这是因为C编译器有权基于“指针指向有效结构体对象”这个假设来做优化。比如,某些编译器可能会默认struct S*指针指向的内存块至少有sizeof(struct S)那么大,于是在生成代码时做出一些你预料之外的调整——比如把这个赋值操作和结构体其他成员的访问逻辑合并优化,甚至直接忽略你的赋值,这些都是未定义行为可能导致的诡异后果。
再说说你提到的柔性数组的区别:柔性数组是C标准特意留的例外情况,比如定义成struct S { int len; char b[]; };,这时候sizeof(struct S)只计算len的大小,你可以用malloc(sizeof(struct S) + 128)来分配内存,然后合法访问b[0]到b[127]——这是标准明确允许的扩展用法。但你的固定数组结构体不属于这种例外,标准没给它开“只分配部分大小”的绿灯。
总结一下:哪怕实际运行中你的代码看起来正常,从C语言标准的理论层面来说,这种“分配小于结构体大小的内存,再通过结构体指针访问成员”的操作,确实会触发未定义行为,绝对不能依赖这种写法。
内容来源于stack exchange

