【问题标题】:Default value to non initialized variables非初始化变量的默认值
【发布时间】:2014-03-14 21:47:09
【问题描述】:

我正在阅读 this tutorial 关于调试的信息。我将阶乘代码粘贴到我的 .c 存档中:

#include <stdio.h>

int main()
{
    int i, num, j;
    printf ("Enter the number: ");
    scanf ("%d", &num );

    for (i=1; i<num; i++)
        j=j*i;    

    printf("The factorial of %d is %d\n",num,j);
}

当我运行可执行文件时,它总是打印0,但是,教程的作者说它返回数字垃圾值。我已经用谷歌搜索了这个,我读到这是正确的,除了静态变量。所以它应该返回一个垃圾号而不是0

我认为这可能是由于 C 的不同版本,但指南是 2010 年的。

为什么我总是看到0,而不是垃圾值?

【问题讨论】:

  • 打印0,并不意味着它不是垃圾值。如果那块内存之前包含0,那么它的垃圾值将是0。你不应该依赖它。

标签: c undefined-behavior


【解决方案1】:

C99 draft standardC11 draft standard 都表示未初始化的自动变量的值是不确定的,来自草案 c99 标准部分 6.2.4 对象的存储持续时间 段落 5 说(强调我的):

对于这样一个没有变长数组类型的对象,它的生命周期会延长 从进入与其关联的块直到该块的执行结束 反正。 (进入封闭块或调用函数会暂停,但不会结束, 执行当前块。)如果块是递归进入的,则该块的新实例 每次都会创建对象。 对象的初始值是不确定的。如果一个 为对象指定初始化,每次声明时执行 在执行块时达到;否则,每个值都变得不确定 到达声明的时间。

标准草案将不确定定义为:

未指定的值或陷阱表示

一个未指定的值被定义为:

本国际标准不强制规定的相关类型的有效值 在任何情况下选择哪个值的要求

所以值可以是任何值。它可能因编译器、优化设置而异,甚至可能因运行而异,但它不能被依赖,因此任何使用不确定值的程序都会调用undefined behavior

该标准在6.5.2.5复合文字17段中的一个示例中说这是未定义的,它说:

请注意,如果使用迭代语句而不是显式 goto 和标记语句,则未命名对象的生命周期将仅是循环的主体,并且在下次进入时 p 将具有不确定的值,这将导致未定义的行为。

这也包含在Annex J.2 未定义的行为

具有自动存储持续时间的对象的值在它被使用时使用 不确定(6.2.4、6.7.8、6.8)。

在一些非常具体的情况下,您可以对此类行为做出一些预测,演示文稿Deep C 涉及其中的一些。这些类型的检查只能用作进一步了解系统如何工作的工具,甚至不应该接近生产系统。

【讨论】:

    【解决方案2】:

    您需要将j 初始化为1。如果j 恰好为零,则答案将始终为零(一种垃圾)。如果j 发生非零,你会得到不同的垃圾。使用未初始化的变量是未定义的行为; 'undefined' 并不排除在您迄今为止所做的测试中始终为零。

    【讨论】:

      【解决方案3】:

      某些系统的内存设置为 0(例如 Mac OS),因此您的变量在初始化时通常会包含 0,但这是一种不好的做法,会导致结果不稳定。

      【讨论】:

      • 即使系统将内存初始化为全零,在程序运行一段时间后,堆栈所在的空间也会被几个变量的值覆盖。除非堆栈被清理,否则该值将保持不变。在 OP 的情况下,由于程序很简单(一个函数,之前没有调用其他函数),它可能只是拾取最初的 0。
      【解决方案4】:

      您不能说在这种情况下应该发生什么,因为语言规范没有说明应该发生什么。事实上,它说未初始化的非静态变量的值是不确定的。

      这意味着它们可以是任何值。它们在程序的不同运行时可能是不同的值,或者当您的代码在不同的编译器上编译时,或者在具有不同优化设置的同一编译器上编译时。或者在一周中的不同日子、国定假日或下午 6 点之后。

      一个未初始化的变量甚至可以保存所谓的陷阱表示,这是一个对该类型无效的值。如果您访问这样的值,那么您将进入一个可怕的未定义行为世界,实际上任何事情都可能发生。

      【讨论】:

      • 在您访问未初始化变量的那一刻,您正处于未定义行为的可怕世界中。一台机器可以帮助你的程序崩溃(如你所描述的),或者它可以启动nethack。或者工作日给你0,节假日给你42
      • @vonbrand 我认为这是有争议的。如果未初始化的值不是陷阱表示,则它必须是该类型的有效值,并且访问有效值不是 UB。所以在一个没有陷阱表示的平台上,访问一个未初始化的值是安全的——它必须给你一个有效的值,而不是一个你可以预测的值。
      • @NigelHarper 我强烈反对,如果你阅读了John Regehr 的大部分工作,你就会知道编译器可以用 UB 做一些意想不到的事情,比如完全删除代码。所以这绝不是安全的,期间。
      • @ShafikYaghmour 我知道编译器可以在 UB 面前做任何事情。问题是,对于未初始化的变量,究竟是什么被声明为 UB。创建一个未初始化的变量是明确定义的。该变量的内容必须是陷阱或有效值。访问陷阱是UB。访问有效值是明确定义的。因此在不支持陷阱的平台上,未初始化的变量必须包含有效值,并且访问该值时没有UB。
      • @NigelHarper,获得任何价值显然是“未定义”
      猜你喜欢
      • 2013-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-29
      • 2010-09-13
      • 1970-01-01
      相关资源
      最近更新 更多