何时在 C 中初始化局部变量?



>考虑下面的例子

void func(int i){
if(i) {
int arr[2048] = {0};
//Doing something related to arr;
} else {
//Doing something
}
}

我在if块中有一个大数组声明。 此数组的初始化应该需要一些时间。我的问题是:如果i == 0,这个数组会被初始化吗?

如果 i == 0,这个数组会被初始化吗?

因为你的代码是

if(i) {
int arr[2048] = {0};
//Doing something related to arr;
} else {
//Doing something
}

数组不存在 ifi==0因此无法初始化,则该数组仅存在于if的分支中 其中i != 0

要理解编译器的行为,您应该考虑在 C 语言中每个变量都有一个存储(ISO/IEC 9899:201x §6.2.4 对象的存储持续时间),它表征了它的行为及其">生命",这意味着存在这样的对象(变量就是对象),以及它的法律访问条件。 存储类为 4 个:静态、线程、自动和已分配。后者使用动态内存分配。

在您的情况下,数组arr[2048]是一个自动对象,其生存期(在标准@点6的同一段落中)定义为:

对于没有可变长度数组类型的此类对象, 它的生命周期从进入它所在的块开始延长 关联,直到该块的执行以任何方式结束。(输入 封闭块或调用函数挂起,但不结束, 执行当前块。

如果以递归方式输入块,则对象的新实例为 每次创建。

对象的初始值不确定

如果为对象指定了初始化,则每次执行 在执行中达到声明或复合文本的时间 的块;否则,每次 达成宣言。

这说明:

  1. 对象的生命周期从定义它的块的开头开始。
  2. 初始值(在我们的例子中是数组内容)是不确定的:编译器不初始化内存区域。当指定初始化时,它会在执行到达块时完成。

第一点很清楚,已经是你问题的答案了。您引用的代码是:

{    //Block init
int arr[2048] = {0};
//Doing something related to arr;
}    // block end

如果你不进入块,你的对象,数组的生命,就不会开始:数组不存在。当然,在这种情况下,不能对对象执行任何操作,甚至初始化。

现在第2 点有助于更好地澄清。表达式:

int arr[2048] = {0};

编译器不会在功能上将其解释为具有对象初始化的声明,因为数组对象的存储类。此外,它基本上被视为声明加转让

有什么区别?

具有不同于自动存储类的初始化变量的声明是使用对用户代码透明的机制实现的,在 BSS 部分中静态分配值或使用属于编译器序言和尾声的代码,即使不访问对象也可能发生这种情况

在另一种情况下,初始化代码(赋值)是用户代码的一部分,因此遵循执行流逻辑执行

这是官方行为。在后台检查你可以看到,在某些情况下,自动变量的空间恰好是从对象生命周期开始就提前分配的,但这些行为严格取决于 CPU 架构,基本上当发生时不会在代码和语言标准之间产生功能差异(这是兼容编译器的基本属性)。

在实践中,变量将在使用前初始化,无论它是否放置在内部作用域中。此代码:

void func1 (int i){
if(i) {
int arr[2048] = {0};
printf("%d", arr[666]);
} else {
//Doing something
}
}

给出与此代码完全相同的机器代码:

void func2 (int i){
int arr[2048] = {0};
if(i) {
printf("%d", arr[666]);
} else {
//Doing something
}
}

x86 上的 gcc -O3 给出:

.LC0:
.string "%d"
func1:
test    edi, edi
jne     .L4
ret
.L4:
xor     esi, esi
mov     edi, OFFSET FLAT:.LC0
xor     eax, eax
jmp     printf

.LC0:
.string "%d"
func2:
test    edi, edi
jne     .L7
ret
.L7:
xor     esi, esi
mov     edi, OFFSET FLAT:.LC0
xor     eax, eax
jmp     printf

如您所见,它们是相同的。

不过,尽可能限制变量的范围是一种很好的设计实践,但这与性能无关。

最新更新