如果我执行以下操作:
int i, *p = &i;
int **p2p = &p;
我获取i的地址(在堆栈上)并将其传递给p,然后我获取p的地址(在堆栈上)并将其传递给p2p。
我的问题是,我们知道i
的值保存在内存地址p
等等,但操作系统如何知道该地址在哪里?我想他们的地址在堆栈中保持有条理。每个声明的变量(标识符)是否被视为与堆栈当前位置的偏移量?全局变量怎么样,操作系统和/或编译器如何在执行期间处理这些变量?操作系统和编译器如何在不使用内存的情况下“记住”每个标识符的地址?是否所有变量都按顺序输入(推送)到堆栈中,并且它们的名称被替换为它们的偏移量?如果是这样,那些可以改变声明顺序的条件代码呢?
答案 0 :(得分:2)
我曾经是汇编语言程序员,所以我知道我曾经使用的CPU的答案。重点是CPU的一个寄存器用作堆栈指针,这些天称为SP(或x86 CPU上的esp
)。编译器引用相对于SP的变量(在您的情况下为i,p和p2p)。换句话说,编译器决定每个变量的偏移应该来自SP,并相应地生成机器代码。
答案 1 :(得分:1)
操作系统不关心程序使用的地址。每当发出需要在地址空间中使用缓冲区的系统调用时,程序都会提供缓冲区的地址。
您的编译器为您的每个函数提供了一个堆栈框架。
push ebp
mov ebp,esp
然后,可以相对于EBP寄存器的值寻址任何函数参数或局部变量,该寄存器是该堆栈帧的基址。编译器通过特定于编译器的参考表来处理这个问题。
退出函数后,编译器会拆除堆栈帧:
mov esp,ebp
pop ebp
在低级别,CPU仅使用文字BYTE / WORD / DWORD / etc值和地址(相同,但使用方式不同)。
需要的内存地址存储在命名缓冲区(例如全局变量)中,编译器在编译时或在CPU的寄存器中替换它的已知地址(非常简化,但仍然是真的)
进入操作系统开发时,如果您愿意,我很乐意更深入地解释我所知道的任何内容,但这确实超出了SOF的范围,因此如果您感兴趣,我们需要找到另一个渠道。
答案 2 :(得分:1)
i的值保存在内存地址p等等,但操作系统如何知道该地址在哪里?
操作系统不知道也不关心变量的位置。
我认为[变量']地址在堆栈中保持有条理。
堆栈不会组织变量的地址。它只包含/保存变量的值。
每个声明的变量(标识符)是否被视为与堆栈当前位置的偏移量?
对于某些局部变量,这可能确实适用。但是,优化可以将变量移动到CPU寄存器中,也可以完全消除它们。
全局变量怎么样,操作系统和/或编译器如何在执行期间处理这些变量?
编译器在编译程序时不处理变量。它已经完成了它的工作。
操作系统和编译器如何在不使用内存的情况下“记住”每个标识符的地址?
操作系统不记得任何一个。它甚至不知道你的程序的变量。对于操作系统,您的程序只是一些有点无定形的代码和数据的集合。变量名称没有意义,很少在编译程序中可用。它们仅供程序员和编译器使用。 CPU和操作系统都不需要它们。
是否所有变量都按顺序输入(推送)到堆栈中并且它们的名称被替换为它们的偏移量?
这对于局部变量来说是一个合理的简化模型。
如果是这样,那么可以改变声明顺序的条件代码呢?
这就是编译器必须处理的内容。一旦程序编译完毕,所有程序都已经完成。
答案 3 :(得分:0)
正如@Stochastically所解释的那样:
编译器引用变量(在您的情况下为i,p和p2p) 相对于SP。换句话说,编译器决定偏移量 每个变量应该来自SP,并产生机器代码 相应
也许这个例子另外解释了你。它在amd64上,因此指针的大小为8个字节。如您所见,没有变量,只有寄存器的偏移量。
#include <cstdlib>
#include <stdio.h>
using namespace std;
/*
*
*/
int main(int argc, char** argv) {
int i, *p = &i;
int **p2p = &p;
printf("address 0f i: %p",p);//0x7fff4d24ae8c
return 0;
}
拆卸:
!int main(int argc, char** argv) {
main(int, char**)+0: push %rbp
main(int, char**)+1: mov %rsp,%rbp
main(int, char**)+4: sub $0x30,%rsp
main(int, char**)+8: mov %edi,-0x24(%rbp)
main(int, char**)+11: mov %rsi,-0x30(%rbp)
!
! int i, *p = &i;
main(int, char**)+15: lea -0x4(%rbp),%rax
main(int, char**)+19: mov %rax,-0x10(%rbp) //8(pointer)+4(int)=12=0x10-0x4
! int **p2p = &p;
main(int, char**)+23: lea -0x10(%rbp),%rax
main(int, char**)+27: mov %rax,-0x18(%rbp) //8(pointer)
! printf("address 0f i: %p",p);//0x7fff4d24ae8c
main(int, char**)+31: mov -0x10(%rbp),%rax //this is pointer
main(int, char**)+35: mov %rax,%rsi //get address of variable, value would be %esi
main(int, char**)+38: mov $0x4006fc,%edi
main(int, char**)+43: mov $0x0,%eax
main(int, char**)+48: callq 0x4004c0 <printf@plt>
! return 0;
main(int, char**)+53: mov $0x0,%eax
!}
main(int, char**)()
main(int, char**)+58: leaveq
main(int, char**)+59: retq