【问题标题】:According to the current C Standard, what is the default value assigned to an `int` which is declared but not defined? [duplicate]根据当前的 C 标准,分配给已声明但未定义的“int”的默认值是多少? [复制]
【发布时间】:2021-03-02 11:37:08
【问题描述】:

C标准问题

简单的问题,但似乎无法通过鸭子鸭或搜索 SO(此处)找到答案。

我知道在 C 中,标准规定 ints 的未初始化数组会导致未定义的行为。 (或者至少大多数编译器都是这样的。)

int a[100]; // 100 x 32bits on stack, values are whatever was left over on the stack
printf("a[5]=%d", a[5]); // undefined behaviour, can be any valid `int` value (-2**15 to 2**15 - 1)

但是,单个int 的默认值是多少?

int a; printf("a=%d", a);

我的猜测是,由于这是在堆栈上,因此 CPU 必须执行“推堆栈”指令,并且该指令必须采用一个值,并且如果没有值,编译器将使用最合理的值被指定为零。

我说的对吗?

示例测试程序和反汇编

#include <stdio.h>

int a;
    
int main(void)
{
    printf("%d\n", a);
    return 0;
}

这是反汇编:(`gcc -save-temps -Wall test.c -o test)

    .file   "test.c"
    .text
    .globl  a
    .bss
    .align 4
    .type   a, @object
    .size   a, 4
a:
    .zero   4
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    a(%rip), %eax
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 10.2.1-6) 10.2.1 20210110"
    .section    .note.GNU-stack,"",@progbits

这似乎有一行

a:
    .zero 4

这是否意味着 a 在 .data 部分中被初始化为一块内存为 4x 0x00(字节)?

【问题讨论】:

  • 自动和动态变量没有默认值。对于静态变量,默认值为 0,但 IIRC 过去也未定义。
  • 我听说使用未初始化数组的值不会调用未定义的行为,因为数组不能用register 声明。
  • Re“我的猜测是,由于这是在堆栈上,因此必须由CPU执行“推堆栈”指令”:不,可以通过调整堆栈来保留堆栈上的空间指针,带有加法或减法指令。通常,在某些体系结构中,例程开始处的代码会对堆栈指针进行一次调整,以便为例程在堆栈上使用的所有数据腾出空间。
  • 通常他们会进入一个名为 .bss 而不是 .data 的区域。有些系统将.bss 命名为.data 的一部分。

标签: c assembly standards


【解决方案1】:

在你的例子中

#include <stdio.h>

int a;
    
int main(void)
{
    printf("%d\n", a);
    return 0;
}

变量a是全局声明的,所以初始化为零。

另一方面,如果变量a是在本地非静态声明的

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

它有一个不确定的值,使用该值会调用未定义的行为

引自N1570 6.7.9 初始化10:

如果具有自动存储持续时间的对象未显式初始化,则其值为
不定。如果具有静态或线程存储持续时间的对象未初始化
明确地,那么:
— 如果是指针类型,则初始化为空指针;
— 如果它具有算术类型,则将其初始化为(正或无符号)零;
— 如果是聚合,则每个成员都根据这些规则(递归地)初始化,
并且任何填充都被初始化为零位;
— 如果是联合,第一个命名的成员根据这些被初始化(递归)
规则,并且任何填充都被初始化为零位;

【讨论】:

  • 大概“线程存储持续时间”在这种情况下意味着“全局变量”?
  • @FreelanceConsultant:不,全局变量具有“静态存储持续时间”,与文件范围 static int foo 变量相同。 “线程存储持续时间”是线程本地存储,其中每个线程看到全局范围或文件范围变量的不同副本。不要被 static 关键字只是静态(在编译时分配)存储的一种情况所迷惑。
  • 这完全是错误的,或者至少是不准确的。首先,它是UB,因为自动存储变量的地址没有被占用。其次,读取不确定的值会在大多数系统上调用未指定的行为,除非存在陷阱表示,int 很少出现这种情况。在(Why) is using an uninitialized variable undefined behavior? 上查看我的回答。
  • @Lundin 对上述问题中的反汇编有何评论?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-22
相关资源
最近更新 更多