【问题标题】:Why are my printfs giving different values? [duplicate]为什么我的 printfs 给出不同的值? [复制]
【发布时间】:2012-08-31 06:15:19
【问题描述】:

可能重复:
Can a local variable's memory be accessed outside its scope?

第二个printf有什么问题?

#include<stdio.h>
int* fun() {
   int a =10;
   return &a;
}
int main() {
   int *a;
   a = fun();
   printf("%d",*a);
   printf("%d",*a);
   return 0;
}

我已返回局部变量的地址并将其传递给printf。第一次正确打印为“10”,但第二次显示为垃圾值。

如果最初a 是一个指向10 地址的悬空指针,为什么第二次不是?

谁能解释一下?

我什至尝试在第一次调用 printf 之前调用其他函数,但我仍然得到相同的输出。

在 BeniBela 的回答之后,我尝试了这个..

#include<stdio.h>
int* fun()
{
int a =10;
return &a;
}
void fun2(int d)
{
int a,b,c;
}

int main()
{
int *a,b;
a = fun();
fun2(5);
printf("%d",*a);
printf("%d",*a);
return 0;
}

还是一样的输出..:(

【问题讨论】:

  • 我猜英语不是您的第一语言,但您可能仍想考虑通过拼写检查器运行它 - 大多数现代浏览器都有一些相当不错的语言。
  • 不,我想知道堆栈究竟是如何工作的......并且第一次它会正确打印......在第一次调用 printf() 后堆栈发生了什么?
  • 10 也是一个垃圾值,它恰好是您所期望的值。如果你尝试printf("%d", rand());,它有时也会打印10

标签: c scope printf


【解决方案1】:

printf 没有问题,您的代码有一个未定义行为
你返回函数局部变量的地址,变量的生命周期被限制在函数的作用域({,})内,一次函数作用域结束,变量存在时间更长。标准不再需要变量存在。

所以你的指针指向的是不存在的东西(虽然它可能)。简单地说,它是 未定义的行为,你受编译器的支配,它可能会向您展示任何行为,但不提供任何解释。

【讨论】:

  • @KerrekSB:平心而论,如果 OP 真的知道,那么 S/He 就不会问 Q。
  • 是的,我正在返回局部变量的地址,所以 m 在编译器的摆布下,但我的问题是它只第一次正确打印?如何堆栈操作der?甚至我在第一次调用 printf() 之前尝试调用其他函数,但它仍然显示相同...
  • @Als:嗯,我的理解是,OP 想要使用 UB 的细节来深入了解 printf 的内部工作原理...
  • @KerrekSB:据我了解,您上面的 OP 评论寻求对发生未定义行为后观察到的行为的解释。
【解决方案2】:

实际发生的是:

所有局部变量都存储在堆栈中。

  1. 在调用fun之前,栈只包含main的*a变量,如:|int *a = undefined||

  2. 当 fun 被调用时,fun 的参数(即没有)、main 的地址和 fun 的局部变量被添加到堆栈中:|int *a = undefined| return to main | int a = 10 ||(还有帧指针,但那没有'没关系)

  3. fun 返回后,堆栈为|int *a = 2nd next stack var|| return to main | int a = 10 |。最后 2 个堆栈变量无效,但它们仍然存在。

  4. 当第一个 printf 被调用时 printf 的参数(以相反的顺序 *a 然后“%d”),在 *a 之后再次添加 printf 的返回地址和局部变量并覆盖旧值:
    它首先变成|int *a = 2nd next stack var| int a = 10 || int a = 10 |
    然后|int *a = 2nd next stack var| int a = 10 | "%d" |
    然后|int *a = 2nd next stack var| int a = 10 | "%d" | return to main ||
    最后是|int *a = 2nd next stack var| int a = 10 | "%d" | return to main | local vars of printf ||

  5. 当第二个 printf 被调用时,*a 仍然指向第二个 bin,但是那个 bin 现在包含“%d”,它将显示为一个奇怪的数字

[编辑:]

fun2 不会覆盖堆栈上的 10,因为 gcc 在堆栈上保留了空的 bin,用于放置被调用函数的参数。所以它不是我上面写的|int *a = 2nd next stack var| return to main | int a = 10 |,而是更像|int *a = 4th next stack var | empty | empty | return to main | int a = 10 |
当 fun2 被调用时,它变成了|int *a = 4th next stack var | empty | 5 | return to main | int a = 10 |,并且 10 仍然没有被覆盖。

函数中的int *a,b,c 无关紧要,因为它们没有分配值。

使用 gdb,您可以查看实际堆栈(它向后增长):

娱乐之前:

0xffffd454:    0x080496f0    0xffffd488    0x080484eb    0x00000001
                 noise     (framepointer)  address      noise/empty bin     
                                           in main

在 fun 之后,在 fun2 及其参数之前:

0xffffd454:    0x0000000a    0xffffd488    0x08048441    0x00000001
                 a         (framepointer)   address     noise/empty bin     
                                            in main

fun2 之后:

0xffffd454:    0x0000000a    0xffffd488    0x08048451    0x00000005

                 a         (framepointer)   address       argument     
                                            in main       for fun2

【讨论】:

  • 编辑了我的问题..请查看..
【解决方案3】:

编译时注意警告:

yourfile.c: In function ‘fun’:
yourfile.c:6:14: warning: function returns address of local variable [enabled by default]

这是范围问题,变量“a”在“fun”函数内有一个局部scope。这意味着当您尝试从 main (在其范围之外)访问该局部变量的地址时,它不再有效。 如果你想解决这个问题,你可以将变量设为全局变量,(但更改名称!)然后你会看到代码按预期工作:

int b;

int* fun() 
{  
  b = 10;
  return &b; 
} 

int main() 
{ 
  int *a; 
  a = fun(); 
  printf("%d",*a); 
  printf("%d",*a);
  getc(stdin);
  return 0; 
}

输出:

> 1010

【讨论】:

    【解决方案4】:

    您返回在函数中创建的对象的地址,因此在函数终止后可能不再有效。而是创建一个指针!

    【讨论】:

    • “可能无效”是不准确的。无论如何,使用它是不合法的。有时你“侥幸逃脱”这一事实毫无意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    • 2019-03-04
    • 1970-01-01
    相关资源
    最近更新 更多