C++中如何使用指针获取第一个数组紧邻的下一个数组的地址
差异产生的核心原因
- 首先明确C/C++的指针运算通用规则:对任意类型指针
T* p执行p + 1操作,实际偏移的字节数等于sizeof(T)。对数组名取地址&arr得到的是指向整个数组的指针,对应类型为T (*)[N](T为数组元素类型,N为数组总长度),因此&arr + 1的偏移量等于sizeof(arr),也就是整个数组占用的总字节数。该规则对一维、二维数组完全统一,本身不存在差异。 - 你观察到的一维、二维场景下的结果差异,本质和数组维度无关,是栈上局部变量的内存排布规则导致的:
- C/C++标准从未规定栈上连续定义的局部变量的内存地址顺序,也不保证相邻定义的同类型变量之间没有填充字节。编译器会根据内存对齐要求、编译优化等级、栈保护机制等因素,自主调整变量的排布,甚至在变量之间插入填充字节。
- 栈的默认增长方向是从高地址向低地址,也就是说先定义的局部变量通常地址更高,后定义的更低,所以
&a+1得到的是a数组后方的高地址,而后定义的b数组通常分配在a数组前方的低地址,二者本来就不必然相等。 - 二维数组场景下
&a+1刚好等于&b,只是当前编译环境下的巧合:编译器刚好把b数组放在a数组的高地址方向,且中间没有插入填充,这种情况不具备可移植性,换编译参数或编译器就可能失效。 - 一维数组场景下两个地址不一致,才是更符合栈排布规则的常见情况。
正确的实现方案
首先需要明确:如果你操作的是同一个数组内的元素或子数组,直接用标准指针算术即可,比如二维数组a的第i个一维子数组地址可以直接用a + i获取,结果完全可靠。
如果你确实需要保证两个数组的地址相对固定,有两种可移植的实现方案:
方案1:将数组封装到结构体中
C标准保证结构体成员的地址按定义顺序递增,仅在成员之间插入满足对齐要求的最少填充,因此可以得到固定的相对偏移:
struct ArrGroup { int A[5]; int B[5]; }; // 此时B相对于A的偏移量固定为 &(((ArrGroup*)0)->B)
方案2:合并为更大的数组
从根源上保证内存连续,完全符合C标准要求:
// 两个一维数组合并为二维数组 int AB[2][5] = {{1,2,3,4,5}, {1,2,3,4,5}}; // AB[0]对应原A数组,AB[1]对应原B数组,AB + 1 就是B数组的地址,100%可靠
禁止直接依赖栈上独立变量的相对地址,这种写法没有可移植性,修改优化等级、更换编译器、开启栈保护都会导致结果异常。
内容的提问来源于stack exchange,提问作者Karthik G
相关产品推荐
相关产品推荐

