返回指向局部变量的指针

我不知道为什么会这样。 由于x是局部变量,我以为在尝试返回时会出错。 然而,第一个printf工作正常,但它只是打印出来0.任何人都可以解释这里发生了什么?

#include  int* func1() { int x = 123123; int *y = &x; return y; } int main() { int* c = func1(); printf("%d\n", *c); // output: 123123 printf("%d\n", *c); // output: 0 return 0; } 

发生以下情况:

  1. func1 ,您创建了本地x变量并使用值初始化它,即x现在位于堆栈中。
  2. 你得到x的地址并将其返回给main
  3. 在返回时, func1及其变量x (以及与问题无关的y )是free d,或者从堆栈中弹出,即它们的存储位置不再被保留以保存它们的值。 在此之后,允许程序的任何其他部分使用在func1x分配的内存空间,因为func1不再处于活动状态。
  4. 您的第一个printf调用仍然会看到x曾经存在的内存位置的旧值(但这不能保证)和
  5. 第二个printf调用显示其他东西(值为0,例如R. Joiny描述的第一个printf的返回值)在func1使用与x相同的地址。

C编程训练营的这篇文章几乎描述了你的情况:

理解堆栈的一个关键是当函数退出时,它的所有变量都从堆栈中弹出(因此永远丢失)。 因此,堆栈变量本质上是本地的。 这与我们之前称为变量范围或局部变量和全局变量的概念有关。 C编程中的一个常见错误是尝试从该函数之外的程序中的某个位置(即在该函数退出之后)访问在某个函数内的堆栈上创建的变量。

printf基本上是一个函数。 就像你的func1

函数使用一些RAM,您的工作空间用于局部变量。 如果你离开这个function,它会变成像“清除使用”(未删除!)之类的东西。

因为第一个printf()直接来自func1()函数,所以局部变量C仍然存在,因为它还没有被覆盖。 这样才行。 但是如果你看一下这个MAN页面,你可以看到,printf将int作为返回值。 所以必须写到某个地方,那么你的PC会做什么? 当然把它写到RAM的第一个免费地址,分配给你的程序。 你有零。

重要的是要注意,没有其他程序可以访问您的RAM,Windows会自动为每个进程保留RAM,因此它必须是printf()的返回值(或printf在执行时使用的其他一些局部变量)。

由于x是局部变量,我以为在尝试返回时会出错。

这个问题的一般答案是:C代码中的错误代码不一定会产生编译器或运行时错误。

如果你很幸运,你会收到编译器错误或警告。

如果你很幸运,当错误的代码运行时,你会遇到运行时崩溃(这通常发生在空指针错误中)。 使用调试器很容易跟踪。

但是,如果你不幸运,错误的代码可能会导致崩溃(在看似无关的代码中),或者无声地破坏某些数据结构(使程序以奇怪的方式运行而不会崩溃),甚至看起来工作正常(直到你添加在其他地方看似无害的代码,或使用不同的编译器,或相同编译器的不同版本,或只是不同的编译选项)。


从技术上讲,您的代码在此行中具有未定义的行为:

 int* c = func1(); 

您正在使用func1的返回值(将其写入c )。 但是该值是func1局部变量的地址, func返回时不再存在。 这意味着返回值是一个无效的指针,只是使用这样的指针导致未定义的行为(你甚至不必取消引用它)。

 printf("%d\n", *c); // output: 123123 

好吧,这一行都使用坏指针(从c读取)并取消引用它。 该程序似乎仍然正常。

 printf("%d\n", *c); // output: 0 

看似无害的添加,但这条线不会产生预期的输出。 现在它看起来像无声数据损坏。

请注意,这些都不能保证。 使用不同优化设置的不同编译器或相同编译器可能会产生行为不同的代码。 就标准而言,编译器必须生成与C代码的可观察行为匹配的代码。 但是行为未定义的C代码会导致任何事情发生; 对编译器可以用它做什么没有任何保证或限制。