c / c ++指针的内存地址在哪里?

时间:2013-04-13 13:20:27

标签: c++ c operating-system

如果我执行以下操作:

int i, *p = &i;
int **p2p = &p;

我获取i的地址(在堆栈上)并将其传递给p,然后我获取p的地址(在堆栈上)并将其传递给p2p。

我的问题是,我们知道i的值保存在内存地址p等等,但操作系统如何知道该地址在哪里?我想他们的地址在堆栈中保持有条理。每个声明的变量(标识符)是否被视为与堆栈当前位置的偏移量?全局变量怎么样,操作系统和/或编译器如何在执行期间处理这些变量?操作系统和编译器如何在不使用内存的情况下“记住”每个标识符的地址?是否所有变量都按顺序输入(推送)到堆栈中,并且它们的名称被替换为它们的偏移量?如果是这样,那些可以改变声明顺序的条件代码呢?

4 个答案:

答案 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