说你做:
void something()
{
int* number = new int(16);
int* sixteen = number;
}
CPU如何知道我要分配给16的地址?
由于
答案 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 *
您可以执行此实验并逐步完成反汇编。这通常很有启发性。