C中的内存地址是如何分配的?
How are memory addresses assigned in C?
我了解到数组的值是根据内存地址“并排”存储的,因此数组的名称是指向数组第一个值的指针:
#include <stdio.h>
int main() {
int array[] = {1, 2, 3};
printf("%d", *array); // The first value of array: 1
printf("%d", *(array + 1)); // The second value of array: 2
}
凭直觉,我认为代码中一个接一个声明的变量只是分配了相邻的内存地址。这个想法违背了数组在内存中的定义方式,因为我的代码中的所有变量将组成一个大数组。
我的问题基本上是,有没有办法知道一个变量的地址是什么,相对于我程序中定义的其他变量,而不用打印它的地址?
Intuitively, I thought that variables declared in the code one after another were simply assigned neighbouring memory addresses.
C 编译器将您的代码(为“C 抽象机”设计)转换为以完全不同的语言(例如目标 CPU 的机器代码)创建相同行为的任何内容。
作为这种“转换为完全不同的东西”的一部分,局部变量通常不复存在(被没有内存地址的寄存器代替,因为寄存器不是内存),即使它们确实存在,它们也可以是以任何顺序或重叠(例如,相同的内存用于在不同时间使用的 2 个不同的局部变量)。
数组“更特殊”,因为它们通常更大,编译器更难优化(同时遵守定义抽象机行为的语言规则);所以数组的元素更有可能在内存中保持连续;但这不是任何形式的保证。
例如,请考虑以下代码:
int foo(int bar) {
int myArray[] = { 1, 2, 3, 4};
if(bar < 0) return bar + myArray[0];
if(bar > 0) return bar + myArray[2];
return bar + myArray[1];
}
如果你编译它(就像我在 https://godbolt.org/ 中使用 godbolt 所做的那样)并检查输出,你可能会看到类似这样的东西:
foo(int):
test edi, edi
js .L6
lea eax, [rdi+3]
mov edx, 2
cmove eax, edx
ret
.L6:
lea eax, [rdi+1]
ret
如你所见;该数组根本不再存在(并且 none 个数组元素具有内存地址),因为在这种情况下,编译器足够聪明以进行优化。
您的代码也发生了同样的事情(数组不再存在并且根本没有地址)。变成这样:
.LC0:
.string "%d"
main:
sub rsp, 8
mov esi, 1 // The value "1" originally came from the array
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
mov esi, 2 // The value "2" originally came from the array
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
My question is, essentially, is there a way to know what the address of a variable is.
本质上;不。这就像给马喂胡萝卜,然后试图确定原始胡萝卜分子在马粪便后会在哪里结束。
你唯一能做的就是在 run-time 获取地址(例如使用 &variable
),这(当且仅当编译器无法证明获取地址的代码可以是 discarded/ignored) 具有强制编译器确保变量确实具有地址的 side-effect。
我了解到数组的值是根据内存地址“并排”存储的,因此数组的名称是指向数组第一个值的指针:
#include <stdio.h>
int main() {
int array[] = {1, 2, 3};
printf("%d", *array); // The first value of array: 1
printf("%d", *(array + 1)); // The second value of array: 2
}
凭直觉,我认为代码中一个接一个声明的变量只是分配了相邻的内存地址。这个想法违背了数组在内存中的定义方式,因为我的代码中的所有变量将组成一个大数组。
我的问题基本上是,有没有办法知道一个变量的地址是什么,相对于我程序中定义的其他变量,而不用打印它的地址?
Intuitively, I thought that variables declared in the code one after another were simply assigned neighbouring memory addresses.
C 编译器将您的代码(为“C 抽象机”设计)转换为以完全不同的语言(例如目标 CPU 的机器代码)创建相同行为的任何内容。
作为这种“转换为完全不同的东西”的一部分,局部变量通常不复存在(被没有内存地址的寄存器代替,因为寄存器不是内存),即使它们确实存在,它们也可以是以任何顺序或重叠(例如,相同的内存用于在不同时间使用的 2 个不同的局部变量)。
数组“更特殊”,因为它们通常更大,编译器更难优化(同时遵守定义抽象机行为的语言规则);所以数组的元素更有可能在内存中保持连续;但这不是任何形式的保证。
例如,请考虑以下代码:
int foo(int bar) {
int myArray[] = { 1, 2, 3, 4};
if(bar < 0) return bar + myArray[0];
if(bar > 0) return bar + myArray[2];
return bar + myArray[1];
}
如果你编译它(就像我在 https://godbolt.org/ 中使用 godbolt 所做的那样)并检查输出,你可能会看到类似这样的东西:
foo(int):
test edi, edi
js .L6
lea eax, [rdi+3]
mov edx, 2
cmove eax, edx
ret
.L6:
lea eax, [rdi+1]
ret
如你所见;该数组根本不再存在(并且 none 个数组元素具有内存地址),因为在这种情况下,编译器足够聪明以进行优化。
您的代码也发生了同样的事情(数组不再存在并且根本没有地址)。变成这样:
.LC0:
.string "%d"
main:
sub rsp, 8
mov esi, 1 // The value "1" originally came from the array
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
mov esi, 2 // The value "2" originally came from the array
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
My question is, essentially, is there a way to know what the address of a variable is.
本质上;不。这就像给马喂胡萝卜,然后试图确定原始胡萝卜分子在马粪便后会在哪里结束。
你唯一能做的就是在 run-time 获取地址(例如使用 &variable
),这(当且仅当编译器无法证明获取地址的代码可以是 discarded/ignored) 具有强制编译器确保变量确实具有地址的 side-effect。