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。