ANSI C89中char*转符合对齐要求的其他指针类型是否为定义行为?
结论:你给出的代码在满足对齐要求的前提下,属于ANSI C89标准的完全定义行为,不存在未定义风险。
规则适配说明
malloc返回的指针天然满足所有基础类型的对齐要求,你传入的elem_size是struct some_struct的大小,该结构体首个成员是long,因此结构体的对齐要求至少等于long的对齐要求,其大小也必然是long对齐值的整数倍。因此(char *)arr + elem_size*5计算得到的地址,天然满足long*的对齐要求,符合C89强制转换规则,转换后的long*指针完全有效。- C89明确允许通过对齐正确的目标类型指针访问未被声明为特定类型的动态分配内存:动态分配的内存本身没有固定声明类型,首次写入时会确定对应的有效类型,你这里写入
long类型值,后续通过结构体首个long类型成员访问该位置,属于兼容的类型访问,不存在别名冲突。
注:C89的别名规则远没有后续C99/C11严格,即使不开宽松别名编译选项,主流编译器也不会对C89模式下的这类代码做导致运行异常的优化。
符合标准的通用替代方案
如果担心极端场景下的对齐或别名问题,可以用标准明确允许的字节操作方式实现相同逻辑,完全不需要知道元素的完整类型:
/* 替代原来的long*赋值逻辑 */ unsigned char *ptr = (unsigned char *)arr + elem_size*5; long val = 0xffffff; /* 逐字节复制long值到目标地址,完全规避对齐和别名问题 */ for (size_t i = 0; i < sizeof(long); i++) { ptr[i] = ((unsigned char *)&val)[i]; }
这种写法在任何符合C标准的实现下都不会产生未定义行为,完全兼容"分配代码不知道元素完整类型"的需求。
实际实现差异说明
不存在符合C89标准的实现会让你的代码运行结果和((struct some_struct *)arr)[5].id = 0xffffff;不同:
- 只要对齐正确,指针转换的语义在所有主流架构(x86、ARM、PowerPC、SPARC等)下都是一致的
- 结构体首个成员的偏移量在C89标准下明确为0,因此两种写法访问的是完全相同的内存地址
- 唯一可能出问题的场景是手动传入的
elem_size不是目标结构体的真实大小、或者结构体首个成员不是long,但这属于业务逻辑错误,不属于标准兼容问题。
内容的提问来源于stack exchange,提问作者mio
相关产品推荐
相关产品推荐

