我在函数中读到,局部变量放在堆栈上,因为它们是在参数放在那里之后定义的。
这也提到here
5.所有函数参数都放在堆栈上。 6.说明 函数内部开始执行。 7.推送本地变量 在定义它们时进入堆栈。
所以我怀疑如果C ++代码是这样的:
#include "stdafx.h"
#include <iostream>
int main()
{
int a = 555;
int b = 666;
int *p = &a;
std::cout << *(p+1);
return 0;
}
如果这里的整数有4个字节,我们调用堆栈上的内存空间,其中包含int
555
x 的前8位,然后'移动'另外4个字节通过*(p+1)
到堆栈的顶部我们应该查看地址 x + 4 的内存。
然而,这是-858993460
的输出 - 无论int b
具有什么值,它总是如此。显然它有一些标准价值。当然我正在访问一个我不应该访问的内存,因为这是变量b。这只是一个实验。
为什么我既没有得到预期的价值也没有非法的访问错误?
我的假设错在哪里?
-858993460
代表什么?
答案 0 :(得分:2)
其他人所说的(即“不要那样做”)绝对是真的。不要那样做。但是,要真正回答您的问题,p+1
很可能指向调用者堆栈帧的指针或返回地址本身。当您在其上推送某些内容时,系统维护的堆栈指针会递减。从官方上讲,这是依赖于实现的,但我见过的每个堆栈指针(这是自16位时代以来)都是这样的。因此,如果正如您所说的那样,局部变量在初始化时被压入堆栈,&a
应该== &b + 1
。
也许插图是有序的。假设我在没有优化的情况下编译32位x86的代码,并且在调用函数之前,堆栈指针esp
为20(这对记录来说不太可能)。这就是你调用cout
的行之前的内存:
4: 12 (value of p)
8: 666 (value of b)
12: 555 (value of a)
16: -858993460 (return address)
p+1
,因为p
是int*
,因此是16.此位置的内存不受保护,因为需要返回调用函数。
请注意,这个答案是学术性的;编译器的优化或处理器之间的差异可能导致意外结果。但是,我不希望任何处理器架构上的p+1
== &b
与任何我见过的调用约定,因为堆栈通常会向下增长。
答案 1 :(得分:1)
您的假设在理论上是正确的(从CS的角度来看)。
在实践中,无法保证以这种方式进行指针运算,期望这些结果。
例如,您的假设“所有函数参数都放在堆栈上”不成立:函数argumments的分配是实现定义的(根据体系结构,它可以使用寄存器或如果感觉有必要,编译器可以自由地在寄存器中分配局部变量。
asumption “int
大小也是4个字节,因此向指针添加4到b
”是假的。编译器可以在a
和b
之间添加填充以确保内存对齐。
这里的结论是:不要使用低级技巧,它们是实现定义的。即使您必须(无论我们的建议如何),您也必须知道编译器的工作原理以及它如何生成代码。