变量的生命周期

Life cycle of a variable

我不清楚在 C 中保证分配多长时间的变量。

例如,如果我有:

void foo(void) {
  int x;
  int* y = &x;
  ...
}

是否保证在 x 的堆栈上分配的 space 在 foo() 的整个持续时间内专门为该变量保留?换句话说,y 是否保证指向将在 foo 的整个持续时间内保留的位置,或者编译器是否可以决定由于 x 未被使用,堆栈space 可以在 foo 中用于其他用途,因此 *y 可以在不直接访问 y(或 x)的情况下更改?

自动变量的生命周期是声明它的范围的整个持续时间;在您的情况下,该范围是整个 foo 函数。

允许编译器进行优化(包括完全删除变量),这些优化可能不会产生明显的影响;但是,一旦将 x 的地址分配给 y,那么对 *y 的任何使用都将使用 x,因此分配给 x 的内存不能用于其他用途,始终有可能访问或修改 *y.

当你问这样的问题时,你应该清楚你问的是C语义还是程序实现。

C 语义是使用抽象计算机模型描述的,其中所有操作都按照 C 标准描述的方式执行。编译器在编译程序时,只要得到相同的结果,它就可以改变程序的实现方式。 (必须正确的结果是程序的 可观察行为 :它的输出,包括写入文件的数据、它的 input/output 交互,以及它对易失性对象的访问。)

在抽象计算机中,x 的内存从 foo 的执行开始到 foo 的执行结束。1, 2

所以,在抽象计算机中,是否使用x并不重要;为它保留内存直到 foo returns 或它的执行以其他方式结束(例如 longjmp 或程序终止)。

当编译器实现这个程序时,允许完全优化掉x(如果它和它的地址没有以任何需要保留内存的方式使用)或者使用相同的内存x 它用于其他用途,只要用途不以改变可观察行为的方式发生冲突。例如,如果我们有这样的代码:

int x;
int *y = &x;
x = 3;
printf("%d\n", x);
int b = 4;
printf("%d\n", b);

那么编译器可能会为 b 使用与 x.

相同的内存

另一方面,如果我们有这个代码:

int x;
int *y = x;
printf("%p\n", (void *) y);
int b = 4;
printf("%p\n", (void *) &b);

那么程序必须为两个 printf 语句打印不同的值。这是因为在抽象计算机模型中同时存在的不同对象必须具有不同的地址。抽象计算机会为它们打印不同的地址,因此编译器必须生成一个忠实于该模型的程序。

脚注

1 由于嵌套函数调用,一个函数可以同时多次执行。

2 有时人们说x的生命周期是函数的作用域,但这是不正确的。该函数可以调用另一个例程并传递给它 y,其地址为 x。然后其他例程可以使用此地址访问 x。内存仍保留给 x,即使它不在其他例程的源代码范围内。在子程序调用过程中,foo的执行暂时停止,但并没有结束,所以x的生命周期还没有结束。

x正在被使用,y正在被传递它的地址!简而言之,只要编译器作者是明智的,答案就是“是”。大多数编译器(至少 visual studio)不会编译这个或者至少警告 x 未初始化,所以这不是一个非常现实的例子。

Y 绝对不能通过改变除 x 或 y 之外的另一个变量来改变。那是 100%。当你进入一个函数时,参数然后局部变量被压入堆栈,然后当你退出一个函数时,它们被弹出。没有共享内存的范围(除非您使用联合)。

这个问题背后的原因是什么?如果你真的想知道 c 是如何定义的,你应该阅读 Kernighan 和 Ritchie 的“The C Programming Language”