Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C中声明的、未初始化的变量会发生什么情况?它有价值吗?_C_Initialization_Declaration - Fatal编程技术网

C中声明的、未初始化的变量会发生什么情况?它有价值吗?

C中声明的、未初始化的变量会发生什么情况?它有价值吗?,c,initialization,declaration,C,Initialization,Declaration,如果我用C写: int num; 在我给num赋值之前,num的值是否不确定?静态变量(文件范围和函数静态)初始化为零: int x; // zero int y = 0; // also zero void foo() { static int x; // also zero } 非静态变量(局部变量)是不确定的。在赋值之前读取它们会导致未定义的行为 在实践中,它们一开始往往只是有一些无意义的值——一些编译器甚至可能会输入特定的固定值,以便在调试器中查看时变得明显——但严格地说,

如果我用C写:

int num;
在我给
num
赋值之前,
num
的值是否不确定?

静态变量(文件范围和函数静态)初始化为零:

int x; // zero
int y = 0; // also zero

void foo() {
    static int x; // also zero
}
非静态变量(局部变量)是不确定的。在赋值之前读取它们会导致未定义的行为

在实践中,它们一开始往往只是有一些无意义的值——一些编译器甚至可能会输入特定的固定值,以便在调试器中查看时变得明显——但严格地说,编译器可以自由地执行任何操作,从崩溃到调用

至于为什么它是未定义的行为而不是简单的“未定义/任意值”,有许多CPU体系结构在其表示中为各种类型添加了额外的标志位。一个现代的例子是;当然,C标准起草者正在考虑一些较旧的体系结构


尝试使用设置了这些标志位的值可能会导致操作中的CPU异常,该操作实际上不应该失败(例如,整数加法或分配给另一个变量)。如果你不初始化一个变量,编译器可能会在设置了这些标志位的情况下拾取一些随机垃圾,这意味着触摸未初始化的变量可能是致命的。

这取决于具体情况。如果该定义是全局的(在任何函数之外),则
num
将初始化为零。如果它是局部的(在函数内部),那么它的值是不确定的。从理论上讲,即使尝试读取该值也有未定义的行为——C允许不影响该值的位的可能性,但必须以特定的方式进行设置,以便您甚至可以从读取变量中获得定义的结果。

基本答案是,是的,它未定义


如果您因此看到奇怪的行为,这可能取决于声明的位置。如果在堆栈上的函数中,那么每次调用该函数时,内容都很可能不同。如果是静态范围或模块范围,则未定义但不会更改。

这取决于变量的存储持续时间。具有静态存储持续时间的变量始终隐式初始化为零

对于自动(本地)变量,未初始化的变量具有不确定值。不确定值,除其他外,意味着无论您在该变量中“看到”什么“值”,它不仅是不可预测的,甚至不能保证是稳定的。例如,在实践中(即忽略UB一秒钟)此代码

int num;
int a = num;
int b = num;
不保证变量
a
b
将接收相同的值。有趣的是,这不是一些迂腐的理论概念,这在实践中很容易发生,因为优化的结果

因此,一般来说,流行的答案“它是用内存中的任何垃圾初始化的”甚至一点都不正确。未初始化变量的行为与使用垃圾初始化的变量的行为不同。

0如果是静态或全局的,则为不确定,如果存储类为自动的,则为不确定 C对于对象的初始值总是非常具体的。如果是全局或
静态
,它们将归零。如果
auto
,则该值不确定

在C89之前的编译器中就是这样,K&R和DMR的原始C报告中都有这样的规定

C89就是这种情况,请参见第6.5.7节初始化

如果对象具有自动 存储持续时间未初始化 明确地说,它的价值是 不确定的如果一个物体 静态存储持续时间不可用 明确地初始化,它是 隐式初始化,好像每个 具有算术类型的成员为 已分配0和已分配的每个成员 指针类型被分配为空 指针常数

C99就是这种情况,请参见第6.7.8节初始化

如果对象具有自动 存储持续时间未初始化 明确地说,它的价值是 不确定的如果一个物体 静态存储持续时间不可用 显式初始化,然后:
-如果 具有指针类型,则初始化为 空指针
-如果它有算术 类型,则初始化为(正) 或无符号)零
-如果它是一个 聚合时,每个成员都被初始化 (递归地)根据这些 规则
-如果是联合,则第一个 命名成员已初始化 (递归地)根据这些 规则

至于不确定到底意味着什么,我不确定C89,C99说:

3.17.2
不确定值

未指定值或陷阱 代表

但不管标准怎么说,在现实生活中,每个堆栈页实际上都以零开始,但当您的程序查看任何
auto
存储类值时,它会看到您自己的程序上次使用这些堆栈地址时留下的内容。如果你分配了大量的
auto
数组,你会看到它们最终以零开始


你可能会想,为什么会这样?另一个不同的SO答案处理了这个问题,请参见:

就我所知,它主要取决于编译器,但通常情况下,编译器会预先假定该值为0。
在VC++中,我得到了垃圾值,而TC给出的值是0。 我把它打印如下

int i;
printf('%d',i);

如果存储类是静态的或全局的,则在加载过程中,BSS会将变量或内存位置(ML)初始化为0,除非最初为变量分配了某个值。在局部未初始化变量的情况下,陷阱表示被分配到内存位置。因此,如果包含重要信息的任何寄存器被编译器覆盖,程序可能会崩溃

但有些编译器可能有避免这种问题的机制

我是在与nec v850系列合作时
int i;
printf('%d',i);
#include <stdio.h>
int main() {
    int i;
    printf("%d\n", i);
}
gcc -O0 -std=c99 a.c
0
objdump -dr a.out
0000000000400536 <main>:
  400536:       55                      push   %rbp
  400537:       48 89 e5                mov    %rsp,%rbp
  40053a:       48 83 ec 10             sub    $0x10,%rsp
  40053e:       8b 45 fc                mov    -0x4(%rbp),%eax
  400541:       89 c6                   mov    %eax,%esi
  400543:       bf e4 05 40 00          mov    $0x4005e4,%edi
  400548:       b8 00 00 00 00          mov    $0x0,%eax
  40054d:       e8 be fe ff ff          callq  400410 <printf@plt>
  400552:       b8 00 00 00 00          mov    $0x0,%eax
  400557:       c9                      leaveq
  400558:       c3                      retq
#include <assert.h>

int f() {
    int i = 13;
    return i;
}

int g() {
    int i;
    return i;
}

int main() {
    f();
    assert(g() == 13);
}
#include <stdio.h>
int i;
int main() {
    printf("%d\n", i);
}

gcc -00 -std=c99 a.c
0000000000400536 <main>:
  400536:       55                      push   %rbp
  400537:       48 89 e5                mov    %rsp,%rbp
  40053a:       8b 05 04 0b 20 00       mov    0x200b04(%rip),%eax        # 601044 <i>
  400540:       89 c6                   mov    %eax,%esi
  400542:       bf e4 05 40 00          mov    $0x4005e4,%edi
  400547:       b8 00 00 00 00          mov    $0x0,%eax
  40054c:       e8 bf fe ff ff          callq  400410 <printf@plt>
  400551:       b8 00 00 00 00          mov    $0x0,%eax
  400556:       5d                      pop    %rbp
  400557:       c3                      retq
  400558:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40055f:       00
readelf -SW a.out
[25] .bss              NOBITS          0000000000601040 001040 000008 00  WA  0   0  4
uint16_t hey(uint32_t x, uint32_t mode)
{ uint16_t q; 
  if (mode==1) q=2; 
  if (mode==3) q=4; 
  return q; }

 uint32_t wow(uint32_t mode) {
   return hey(1234567, mode);
 }
void moo(int mode)
{
  if (mode < 5)
    launch_nukes();
  hey(0, mode);      
}