CPU如何知道任何变量的地址?

时间:2012-01-02 22:25:39

标签: c++ assembly cpu

说你做:

void something()
{
   int* number = new int(16);

   int* sixteen = number;
}

CPU如何知道我要分配给16的地址?

由于

2 个答案:

答案 0 :(得分:6)

您的示例代码中没有任何魔力。拿这个片段,例如:

int x = 5;
int y = x;

带指针的代码完全相同 - 计算机不需要知道任何魔法信息,它只是将number中的任何内容复制到sixteen

至于你的评论如下:

  

但它如何知道x或y在内存中的位置。如果我要求将x复制到y中,它是如何知道其中任何一个的。

实际上,在大多数机器上,这些天可能它们都不在内存中,它们会在寄存器中。但是如果它们在内存中,那么是的,编译器将发出代码,根据需要跟踪所有这些地址。在这种情况下,它们将位于堆栈中,因此机器代码将访问堆栈指针寄存器,并使用一些编译器决定的偏移量来解除引用它,这些偏移量指的是每个特定变量的存储。

这是一个例子。这个简单的功能:

int f(void)
{
  int x = 5;
  int y = x;
  return y;
}

使用clang编译并且没有优化时,在我的机器上给出以下输出:

_f:
 pushq  %rbp               ; save caller's base pointer
 movq   %rsp,%rbp          ; copy stack pointer into base pointer
 movl   $5,0xfc(%rbp)      ; store constant 5 to stack at rbp-4
 movl   0xfc(%rbp),%eax    ; copy value at rbp-4 to register eax
 movl   %eax,0xf8(%rbp)    ; copy value from eax to stack at rbp-8
 movl   0xf8(%rbp),%eax    ; copy value off stack to return value register eax
 popq   %rbp               ; restore caller's base pointer
 ret                       ; return from function

我添加了一些注释来解释生成的代码的每一行的作用。需要注意的重要事项是,堆栈中有两个变量 - 一个位于0xf8(%rbp)(或rbp-8更清晰),另一个位于0xfc(%rbp)(或rbp-4)。基本算法就像原始代码所示 - 常量5被保存到x rbp-4,然后该值被y复制到rbp-8 }。

“但是堆栈来自哪里?”你可能会问。不过,问题的答案是依赖于操作系统和编译器。它是在调用程序的main函数之前设置的,与操作系统所需的其他运行时设置同时进行。

答案 1 :(得分:3)

CPU知道,因为你的程序告诉它。这里的魔力在于编译器。首先,我在Visual Studio 2010中构建此程序。

这是它生成的反汇编(在DEBUG模式下):

void something()
{
003A13C0  push        ebp  
003A13C1  mov         ebp,esp  
003A13C3  sub         esp,0E8h  
003A13C9  push        ebx  
003A13CA  push        esi  
003A13CB  push        edi  
003A13CC  lea         edi,[ebp-0E8h]  
003A13D2  mov         ecx,3Ah  
003A13D7  mov         eax,0CCCCCCCCh  
003A13DC  rep stos    dword ptr es:[edi]  
   int* number = new int(16);
003A13DE  push        4  
003A13E0  call        operator new (3A1186h)  

调用operator new后,EAX = 00097C58这是内存管理器决定给我这个程序运行的地址。这是您取消引用号码时将使用的地址。

003A13E5  add         esp,4  
003A13E8  mov         dword ptr [ebp-0E0h],eax  
003A13EE  cmp         dword ptr [ebp-0E0h],0  
003A13F5  je          something+51h (3A1411h)  
003A13F7  mov         eax,dword ptr [ebp-0E0h]  
003A13FD  mov         dword ptr [eax],10h  
003A1403  mov         ecx,dword ptr [ebp-0E0h]  
003A1409  mov         dword ptr [ebp-0E8h],ecx  
003A140F  jmp         something+5Bh (3A141Bh)  
003A1411  mov         dword ptr [ebp-0E8h],0  
003A141B  mov         edx,dword ptr [ebp-0E8h]  
003A1421  mov         dword ptr [number],edx  
   int* sixteen = number;
003A1424  mov         eax,dword ptr [number]  
003A1427  mov         dword ptr [sixteen],eax  

在这里,您只需要确保十六是与数字相同的值。所以现在他们指向同一个地址。

}

您可以通过在Locals调试窗口中检查它们进行验证:

+       number  0x00097c58  int *
+       sixteen 0x00097c58  int *

您可以执行此实验并逐步完成反汇编。这通常很有启发性。