【问题标题】:How garbage values are assigned to variables in c垃圾值如何分配给c中的变量
【发布时间】:2013-03-10 19:37:44
【问题描述】:

C 代码:

int a;
printf("\n\t %d",a); // It'll print some garbage value;

那么这些垃圾值是如何分配给C语言幕后未初始化的变量的呢?

这是否意味着 C 首先将内存分配给变量“a”,然后该内存位置的值变成了“a”的值?还是别的什么?

【问题讨论】:

  • 虽然这个特定代码不会发生这种情况,但如果int a; 在文件范围内(任何函数之外)声明,它将自动初始化为0。
  • @teppic: static int a; 也将自动初始化为零,无论其范围如何。
  • @Destructor - 我知道,但它是专门写成int a :)
  • 请注意,此代码会导致 UB。不要读取未初始化的值,char 除外。

标签: c memory


【解决方案1】:

这是否意味着 C 首先将内存分配给变量“a”,然后该内存位置的值变成了“a”的值?

没错!

基本上,C 不会做任何你没有告诉它的任何事情。这既是它的强项,也是它的弱点。

【讨论】:

  • 说“完全正确!”是不正确的。 C 标准要求未初始化的对象具有一些未指定的值。但是,您不能假设 value 将是恰好为其分配对象的内存中的值。根据优化和其他编译器行为,该值可能取自寄存器或不同于分配的内存或其他位置的临时堆栈位置。
  • @EricPostpischil 感谢您提供额外的细节。编译软件时会发生很多事情,这些事情太过广泛,无法在这样一个简单的问题中涵盖。
  • 我必须更正我的评论。 C标准说一个未初始化的对象可能有一个陷阱表示或一个未指定的值,并且,通过未指定的值,它意味着“相关类型的有效值,本文档对在任何情况下选择哪个值没有要求”( C 2018 3.9.3)。这意味着该值可能在对象的不同用途中有所不同。例如,C 标准将允许printf("%d %d %d\n", a, a, a); 打印“34 -10200773 2147483204”。该值不一定是标称内存位置中的任何值。
【解决方案2】:

这是否意味着C首先将内存分配给变量'a',然后是什么 曾经有在那个内存位置变成'a'的值吗?或者 还有什么?

正确。值得一提的是,int a 等自动变量的“分配”实际上是不存在的,因为这些变量存储在堆栈或 CPU 寄存器中。对于存储在堆栈中的变量,在调用函数时会执行“分配”,归结为将堆栈指针移动编译时计算的固定偏移量的指令(函数使用的所有局部变量的组合存储,四舍五入到正确对齐)。

分配给CPU寄存器的变量的初始值是寄存器之前的内容。由于这种差异(寄存器与内存),有时会发生在没有优化的情况下编译时正常工作的程序在打开优化的情况下编译时会开始中断。未初始化的变量,以前指向恰好被零初始化的位置,现在包含以前使用相同寄存器的值。

【讨论】:

    【解决方案3】:

    最初内存有一些值,那些是未知值,也称为垃圾值, 当我们声明一个变量时,根据我们在声明时指定的数据类型为变量保留了一些内存,因此内存初始值是未知值,如果我们初始化一些其他值,那么我们的值将在那个内存位置。

    【讨论】:

      【解决方案4】:

      int a;

      在声明变量时,内存被分配。但是这个变量没有被赋值,这意味着变量a 没有被初始化。如果这个变量a 只被声明了但在程序中不再使用,则称为垃圾值。 例如:

      int a, b;
      
      b=10;
      printf("%d",b);
      return 0;
      

      这里只声明了它,但不再分配或初始化。所以这叫做垃圾值

      【讨论】:

        【解决方案5】:

        这是否意味着 C 首先将内存分配给变量 'a',然后该内存位置的值变成了 'a' 的值?

        不,不是那个意思。

        当一个对象未​​初始化时,C 标准不提供任何关于如何确定其值的计划。不仅如此,程序不需要表现得好像对象具有任何固定值。它可以变化,就好像为其保留的内存没有保持在任何固定状态,只是在波动。

        以下是具体的 C 2018 规则:

        • 未初始化对象的所谓“值”是indeterminate,对于没有可变长度数组类型的具有自动存储持续时间的对象,按照 6.2.4 6,对于具有可变长度的对象,按照 6.2.4 7数组类型,7.22.3.4 2 用于分配malloc 的对象,7.22.3.5 2 用于realloc 分配的额外空间,7.22.3.1 2 用于aligned_alloc。初始化其他对象,例如具有静态存储持续时间的对象。
        • 根据 3.19.2,不确定值是“未指定的值或陷阱表示”。
        • 根据 3.19.3,未指定值是“相关类型的有效值,本文档对在任何情况下选择哪个值没有任何要求”。

        这意味着在每个实例中使用了一个对象,C 标准没有对使用哪个值强加任何要求。它不需要与以前的使用相同。程序可能表现得好像它没有任何固定值。当它被多次使用时,程序可能会表现得好像每次都具有不同的值。例如,C 标准将允许printf("%d %d %d\n", a, a, a); 打印“34 -10200773 2147483204”。

        可能发生这种情况的一种方式是,在尝试编译代码int a; printf("%d %d %d\n", a, a, a); 时,编译器无处可从中获取a,因为它从未被赋予任何固定值。因此,编译器不会生成无用的指令来将数据从未初始化的内存移动到传递参数的位置,而是不会生成任何内容。然后printf 被调用,传递参数的寄存器或堆栈位置包含它们之前的任何数据。这很可能是printf 打印的三个不同的值。因此,对于输出的观察者来说,aprintf("%d %d %d\n", a, a, a); 中具有三个不同的值。

        (此外,使用具有自动存储持续时间且尚未获取其地址的未初始化对象的值是明确未定义的行为,因为 6.3.2.1 2,关于将对象转换为其值,说“如果左值指定可以使用寄存器存储类声明的自动存储持续时间的对象(从未获取其地址),并且该对象未初始化(未使用初始化程序声明并且在使用之前未对其进行分配),行为是未定义的。”所以,当有一个对象满足这些条件时,使用它的值可以完全破坏程序;它可能不仅在不同的时间有不同的值,而且程序可能会中止,走一个与你预期不同的分支, 源代码中有printf时不调用printf,以此类推。)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多