C++翻译单元中全局变量存储连续性与指针算术合法性问询
全局变量内存布局与指针算术的C++标准问题
背景代码
全局变量定义
extern const std::string s0{"s0"}; extern const std::string s1{"s11"}; extern const std::string s2{"s222"}; // 以此类推...
原实现函数get_1
size_t get_1(size_t i) { switch (i) { case 0: return s0.size(); case 1: return s1.size(); case 2: return s2.size(); // 以此类推... } }
提议替换的函数get_2
size_t get_2(size_t i) { return *(&s0 + i); }
核心问题
- 翻译单元中相邻定义的全局变量是否保证按定义顺序连续存储?
- 即
&s1 == &s0 + 1、&s2 == &s1 + 1是否恒成立? - 编译器是否允许调换
s0与s1的内存位置(比如把s0放在s1的高地址处)?
- 即
- 对这类非数组、非子对象的全局变量执行
get_2中的指针算术操作是否属于定义良好的行为?- 指针关系运算符的相关规则是否同样适用于指针算术?(关于
std::less等可对任意void*产生全序,而普通关系运算符不行的说明是否与此相关?)
- 指针关系运算符的相关规则是否同样适用于指针算术?(关于
注:本问题聚焦全局变量,并非栈上变量的内存布局,虽答案可能类似,但问题本身不同。
解答
问题1:全局变量的存储顺序与连续性
C++标准不保证同一翻译单元中相邻定义的全局变量会按定义顺序连续存储,也不保证它们的地址顺序与定义顺序一致:
&s1 == &s0 + 1这类等式不恒成立:全局变量属于静态存储期对象,标准允许编译器对同存储期的非数组对象进行重排、插入填充字节(比如为了内存对齐),所以它们的内存地址之间没有强制的连续或顺序关系。- 编译器完全有权调换
s0和s1的内存位置,甚至在它们之间插入其他数据,只要不违反程序的语义规则。
问题2:非数组对象的指针算术合法性
get_2中的指针算术操作属于未定义行为:
- C++标准规定,指针算术仅适用于数组对象(包括指向数组元素的指针,以及指向数组末尾后一个位置的指针),或者指向完整对象的指针(此时只能进行
+0或-0的操作)。对独立的非数组全局变量使用&s0 + i(i≠0)时,这个指针操作已经超出了标准允许的范围,行为完全未定义。 - 指针关系运算符的规则与指针算术不直接等价:普通关系运算符(
<、>等)仅能用于指向同一数组或同一对象的指针,否则行为未定义;而std::less等标准库比较函数对任意void*有全序保证,但这只适用于比较操作,不能延伸到指针算术——指针算术的合法性规则更严格,和比较操作的规则是分开的,两者没有直接关联。
内容的提问来源于stack exchange,提问作者Arghnews
相关产品推荐
相关产品推荐

