通过unsigned char别名访问对象:加载存储行为及断言有效性问询
C++17数组别名访问:断言会触发吗?修改是UB吗?
咱们先拆解第一个代码片段的问题:
第一个断言assert(*alias_to_array_s=='X')会不会触发?
首先得明确C++17里的两个核心规则:
- 严格别名规则有个关键例外:根据[basic.lval]/8,任何对象的存储值都可以通过
unsigned char类型的指针读取,不管原对象的具体类型是什么。 - 虽然数组和它的首元素不是指针可互转换的([basic.compound]中明确了这一点),但
&s(数组的地址)和&s[0](首元素的地址)在数值上完全一致——把指向数组的指针pointer_to_array_s强转成unsigned char*后,alias_to_array_s的地址值就是数组s的起始地址,也就是首元素'X'所在的地址。
这里的核心是,用unsigned char*访问的是数组对象的存储字节,而数组的第一个字节正好就是首元素'X'的字节。严格别名的unsigned char例外允许这种访问,所以这个断言绝对不会触发,行为是完全定义良好的。
第二个场景:修改别名后,assert(s[0]=='Y')会触发吗?修改本身是UB吗?
再看第二个代码片段:
*alias_to_array_s='Y'; //UB? assert(s[0]=='Y');//may fire?
同样套用[basic.lval]/8的规则:用unsigned char指针修改对象的存储字节也是允许的——这属于严格别名规则的合法例外。数组s的存储和它的首元素s[0]的存储完全重叠(数组的起始地址就是首元素的地址,数组的存储包含首元素的所有字节),所以修改*alias_to_array_s本质上就是修改了s[0]对应的存储字节。
结论很明确:
*alias_to_array_s='Y';不是未定义行为,完全符合标准规则。assert(s[0]=='Y');也不会触发,因为直接访问s[0]读取的就是被修改后的字节值。
别被“alias_to_array_s不是指向s[0]的有效指针”这个细节迷惑——严格别名的unsigned char例外不要求指针必须指向某个子对象,只要求你是在访问目标对象的存储字节。这里我们访问的是数组对象s的存储,而它的第一个字节就是s[0]的内容,所以操作完全合法。
内容的提问来源于stack exchange,提问作者Oliv
相关产品推荐
相关产品推荐

