亲爱的同事们!我想了解堆栈的工作原理,使用三个变量的函数示例。
void f2()
{
int B = 5;
int *pB = &B; // задание адреса в указатель
int &sB = B; //задание объекта в ссылку
cout << "addres of B = " << &B << "\n";
cout << "value of B = " << B << "\n";
cout << "addres of pB = " << &pB << "\n";
cout << "value of pB = " << pB << "\n";
cout << "addres of sB = " << &sB << "\n";
cout << "value of sB = " << sB << "\n";
}
int main()
{
f2();
system("PAUSE");
return 0;
}
输出:
addres of B = 0038F644
value of B = 5
addres of pB = 0038F638
value of pB = 0038F644
addres of sB = 0038F644
value of sB = 5
1.一线三线的误解。在地址空间上,我认为新变量的地址应该大于旧变量的地址。在实践中:
`0038F638 - 0038F644 = -C;`
这是向后移动 12 个位置。尽管其中一位经验丰富的程序员表示,移位将向前移动 4 个位置 - 按维度int,以字节表示。
2.地址sB——一般与地址重合B!而价值sB——与价值重合B!对我来说很明显,放入的值sB应该等于地址B。并且地址sB必须sizeof(int)相对于前一个变量的地址向前移动四个位置 ( )。
问题:
如何解释观察到的行为?相对于前一个变量,下一个变量的地址分配(实际上)如何?是否有可能以比我所做的更智能的方式打印堆栈?(我想将堆栈视为三个条目 -类型 - 值 - 地址)。
在大多数“传统”平台中,堆栈从上到下增长:从高地址到低地址。因此,即使编译器“按顺序”分配您的变量,如果“较新”变量的地址小于“较旧”变量的地址也不足为奇。
但实际上,各个变量的放置是没有顺序的,你比较地址是没有意义的。
在传统的实现中,一个函数的所有局部变量的内存是在函数开头的一个“堆栈帧”中一次性分配的。在这个堆栈框架内,编译器会在编译阶段制定一些局部变量位置的固定映射。同时,它可以(并且将会)以完全任意的方式在这个映射中安排局部变量,以对齐、内存节省等优化考虑为指导。等等 因此,您的变量声明顺序根本没有任何意义,两个“相邻”变量的地址之间的差异可以是任何大小和符号。
“地址
sB”是什么?sB- 关联。引用类型不是对象类型,概念上它不占用内存,也没有地址。C++ 语言中没有而且也不可能有“地址 sB”。声明后,int &sB = B;表达式&sB将准确给出地址B,这就是您正在观察的内容。Baddress 与 address相同这一点并不奇怪B。没有特别的。正如编译器在这种特殊情况下所希望的那样,就这样吧。这些决定是由编译器根据在语言级别不可见的逻辑做出的。
C++ 语言中的某些顺序仅存在(可能存在)在同一数组的元素之间或同一类的字段之间。
在语言层面,当然不是。
然后您可以研究编译器生成的调试信息的格式,以及您的实现提供(如果提供)用于访问此调试信息的 API。一切都会在那里。
我们将稍微看一下编译器是如何分配变量的。我将利用原始帖子没有说明作者使用哪个编译器这一事实,因此,正如数学家所说,在不失一般性的情况下,我们可以假设编译器是 gcc。
让我们将原始帖子中的代码保存到一个文件
prnlocal.cpp中,并添加到其中并注释掉
许多人认为 Intel 汇编代码比 AT&T 汇编代码更容易阅读,因此我们将使用以下命令生成汇编代码
让我们获取一个文件
prnlocal.s,我们在其中找到标签_Z2f2v- 这就是 C++ 对函数 f2 的名称进行编码的方式,该函数具有参数(void).让我们更详细地处理这里写的内容。在我们的架构中,堆栈是使用两个专用寄存器实现的:rbp 和 rsp。rbp 指向栈底,rsp 指向栈顶。
我们的栈从大地址增长到小地址,也就是在正常情况下
rbp > rsp。第一个有趣的运算符:
在
[rbp-28 ... rbp-25]位于address的4个字节中,需要写入数字5。这样一来,数字5将落入位于address的字节中rbp-28,其余三个字节将被初始化为0。下一个命令
将地址加载
[rbp-28]到寄存器rax中。第三条语句将从寄存器中加载值
rax到八个字节 [rbp-40...rbp-33]。有趣的是,我们的程序不使用地址 [rbp-32...rbp-29] 处的字节。这是将 64 位变量放置在 8 的倍数的地址上的结果——所谓的数据对齐在 8 的倍数的地址上。
结果我们得到了这样一个栈
这里,
rbp-40变量的值放在地址处,变量的值放在pB地址处 。rbp-28B您还可以了解在汇编代码之间
rbp-24和rbp从汇编代码中放置的内容,但这是一个单独的故事。