【问题标题】:C local variable reusedC 局部变量重用
【发布时间】:2016-07-19 19:29:06
【问题描述】:

据我所知,每次调用函数后,局部变量都会被丢弃。但是当我执行这段代码时,变量保留了它们以前的值。这是什么行为?

编译器:gcc 4.8.4

#include<stdio.h>
void t();
int main(void){
    t();
    t();
    t();
    return 0;
}

void t(){
    int i;
    i++;
    printf("%d\n",i);
}

输出:

4bh1@mybox:~/C-fi$ ./test 
1
2
3

【问题讨论】:

  • stackoverflow.com/questions/6441218/… 的副本(尽管它是针对不同的编程语言)
  • @PascalCuoq 这根本不是骗子。当变量超出范围时,OP 不会尝试访问它。
  • @NathanOliver 我相信当您说“超出范围”时,您的意思是“在其生命周期之后”。
  • @PascalCuoq 是的,这不是 OP 正在做的事情。 OP 正在使用未初始化的变量。那是另一回事。

标签: c linux gcc


【解决方案1】:

您的程序的行为未定义。 i 未初始化,因此除了设置其值之外,对 i 的任何使用都是未定义的行为。

您所看到的很可能是 i 每次都获得相同的“堆栈空间”分配给它,因此您只是重用了以前的值。

请注意,当int(或任何类型)超出范围时,编译器不会将其值重置为某个值,因为这会浪费 CPU 周期。这就是为什么您可以看到值增加的原因,因为它只是重用了内存空间中的内容。

【讨论】:

  • 请记住,未定义的行为也是不可预测的行为,因此即使您有解释,也不应该假设它会按您期望的方式工作。 永远不要让你的程序有未定义的行为!
  • @NathanOliver 是的,我认为该值正在被重用。有什么办法可以避免吗?除了 int i=0; 或者它只是一个编译器的东西
  • @4bh1 好吧,您总是可以在超出范围之前将值设置为某个值,但我认为没有自动重置变量的方法。你真的不需要,因为如果你遵守 RAII,你应该总是初始化一个变量。
  • POD 类型是 C++ 术语,不是吗?问题是关于 C 的。
  • @glglgl 这个问题最初是用 C++ 标记的。我想我可以删除它,因为它已更改为 C。
【解决方案2】:

这是栈人

在这种特殊情况下,它可能与函数中变量的内存分配方式有关。

有一块连续的内存块是为函数变量保留的,称为堆栈。

当您调用一个函数时,堆栈的一部分是为该函数保留的,这就是该函数中变量值的存储位置。

当你退出函数时,堆中的那部分不再保留,将被下一个调用的函数使用。但是,其中的值不会被清零,值仍然存在。

因此,在这种情况下,如果您不初始化函数变量,它将包含堆栈中发生的任何内容。

在这种情况下,由于您一遍又一遍地调用相同的函数,剩下的内容将是可预测的。但在大多数情况下,您会遇到问题并且无法预测。

例如,如果您在两者之间调用不同的函数。

在两者之间调用不同的函数时中断

#include<stdio.h>
void t();
void otherFunction();
int main(void){
    t();
    otherFunction();
    t();
    t();
    return 0;
}

void t(){
    int i;
    i++;
    printf("%d\n",i);
}

void otherFunction(){
    int x = 10;
}

输出:

1
11
12

如果 x() 在堆栈的更深处调用,它的行为也不会相同:

从堆栈的不同深度调用时中断

#include<stdio.h>
void t();
void callT();
int main(void){
    t();
    callT();
    t();
    t();
    return 0;
}

void t(){
    int i;
    i++;
    printf("%d\n",i);
}

void callT(){
    t();
}

输出:

1
1
1
2

谈论未定义的行为,我什至无法解释。

在我们的示例中更糟糕的是使用int 或在我们的函数中不使用任何内容。堆栈不知道堆栈中的数据类型,它只是一堆字节。因此,您最终可能会遇到数据被错误地解释为另一种类型的情况。

例如,假设其中一个函数使用浮点数而不是整数。

使用不同类型时中断

#include<stdio.h>
void t();
void assignFloat();
int main(void){
    t();
    assignFloat();
    t();
    t();
    return 0;
}

void t(){
    int i;
    i++;
    printf("%d\n",i);
}

void assignFloat(){
    float x = 10;
}

输出:

1
1092616193
1092616194

这不是您总是会遇到的行为,该行为是“未定义的”,这意味着您无法预测从编译器到编译器甚至从一个编译器配置到另一个编译器的行为。未定义意味着它不可靠,你不应该这样做。

此答案中的示例使用以下方法进行了测试:

g++ (GCC) 5.3.1 20151207(红帽 5.3.1-2)

使用在线工具:

http://www.tutorialspoint.com/compile_cpp_online.php

【讨论】:

    【解决方案3】:

    你应该初始化你的变量 i 。您也可以使用void 作为t() 的签名。

    这是一个更新版本(也冒昧地使用%dprintf):

    #include<stdio.h>
    void t(void);
    int main(void){
    t();
    t();
    t();
    return 0;
    }
    
    void t(void){
    int i = 0;
    i++;
    printf("%d \n",i);
    }
    

    【讨论】:

    • 将“int”定义为返回类型也会生成相同的值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-22
    • 1970-01-01
    • 1970-01-01
    • 2020-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多