含数组成员的联合数组中单个值的安全访问及malloc场景疑问
问题解答
先明确前提里的代码定义:
union Set { uint64_t z[2]; uint32_t y[4]; uint16_t x[8]; }; // 数组声明版本 union Set sets[SIZE] = { /* ... */ };
已知sets[1].x[0]是安全的访问方式,且联合无内存填充,下面分两种场景分析:
场景1:sets是声明的数组
从内存布局上看,每个union Set的大小是16字节(2个uint64_t,每个8字节),刚好对应8个uint16_t。sets[1]的起始地址相对于sets偏移16字节,而((uint16_t *)sets)[8]的偏移是8*2=16字节,两者的内存位置完全重合。
但从C标准的角度,这个操作存在未定义行为:
- 严格别名规则规定,不同类型的指针不能用来访问彼此的对象(字符类型指针除外)。这里把
union Set[]的指针强制转换成uint16_t*,然后访问第二个union Set的位置,属于违反严格别名规则的行为。 - 编译器可能基于严格别名规则做优化,比如认定
sets指向的是union Set类型数组,不会考虑uint16_t*类型的访问对它的影响,可能导致实际运行结果和预期不符(比如缓存的值被优化掉、地址计算出错)。
场景2:sets由malloc分配
如果sets是通过malloc分配的:
union Set *sets = malloc(SIZE * sizeof(union Set));
情况就不一样了:
malloc返回的是无类型内存块,直到你向其中存储数据,才会确定对应内存区域的有效类型。- 如果你已经通过
sets[1].x[0]写入过数据,那么该内存位置的有效类型就是uint16_t,此时用((uint16_t *)sets)[8]访问是合法的,不存在未定义行为。 - 如果还没对该位置做任何写入,直接读的话,问题出在读取未初始化内存本身是未定义行为,而非类型转换的问题。
内容的提问来源于stack exchange,提问作者CPlus
相关产品推荐
相关产品推荐

