为什么可以在C中声​​明数组而不定义其长度?

时间:2019-03-11 14:53:39

标签: c arrays initialization

在C语言中,您应该定义数组的长度。但是,以下代码是有效的:

int arr[] = {10, 20, 30, 40, 50};

为什么这种可接受的语法?

3 个答案:

答案 0 :(得分:14)

这是一项便利功能。数组的大小是从初始化程序推导出的,因此您不必拼写:

int arr[] = {10, 20, 30, 40, 50};

等同于

int arr[5] = {10, 20, 30, 40, 50};

这的另一个示例(感谢Eugene Sh。)是字符串初始化程序:

char str[] = "asd";

等同于

char str[4] = "asd";

要知道的一件事是,当用作函数参数的类型时,它们是不同的。所有以下形式:

void foo(int v[])

void foo(int v[1])
void foo(int v[5])
void foo(int v[1000])

彼此之间都是等效的,并且将它们转换为

void foo(int* v)

始终使用后者(void foo(int* v)),而不要使用其他。因为第一种形式使它看起来像您具有数组类型,但实际上您拥有的是一个指针。这是一种误导。

答案 1 :(得分:6)

要补充现有的答案,请引用C11,第§6.7.9章,P22

  

如果初始化了一个未知大小的数组,则其大小由索引最大的数组确定   带有显式初始化程序的元素。数组类型在其末尾完成   初始化程序列表。

因此,数组的大小将由“最大索引元素”决定,或者简单地说,由初始化列表中存在的元素数决定。

答案 2 :(得分:2)

这是可以接受的,因为整数的大小(以字节为单位)在编译期间是已知的,因此编译器知道整个列表需要多少空间。

但是要理解这个答案,必须深入一点,并问为什么在编译时知道确切的大小如此重要。一般而言:为程序定义virtual address space。 其中一部分是用于存储局部变量的堆栈,并且不得将其与堆内存(malloc工作的地方)混淆。堆栈是一个LIFO列表,还包含所有函数调用及其参数。它在函数的末尾用于回跳,即您来自何处,并已存储该地址。在使用函数时,放到堆栈上的所有内容都必须释放,以便获得正确的回跳地址并避免潜在的段错误。

幸运的是,C被视为为我们自动执行了这种类型的内存管理,并在我们所有的automatic variables被认为超出范围后将其释放。要做到这一点,我们需要确切的大小,这就是我们压入堆栈的大小,这就是为什么编译器需要知道该大小的原因。

要说明编译器如何翻译您的代码并对这些数字进行硬编码,请参见此处:

$ echo "int int_size = sizeof(int); int main(void) { int arr[] = {10, 20, 30, 40, 50}; }" |\ 
    gcc -c -xc -S -o- -masm=intel - 


    .file   ""
    .intel_syntax noprefix
    .text
    .globl  main
    .type   main, @function
# [...] removed int_size here to keep it shorter. its "4" ;)
main:
.LFB0:
    .cfi_startproc
    push    rbp               # < backup rbp / stack base pointer
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp              # < rsp / stack shift pointer = top of the stack
    .cfi_def_cfa_register 6
    sub rsp, 32
    mov rax, QWORD PTR fs:40
    mov QWORD PTR -8[rbp], rax
    xor eax, eax
    mov DWORD PTR -32[rbp], 10  # < 10 is one element from the array
    mov DWORD PTR -28[rbp], 20  # < -28 means relative to the top of the stack
    mov DWORD PTR -24[rbp], 30 
    mov DWORD PTR -20[rbp], 40
    mov DWORD PTR -16[rbp], 50
    mov eax, 0
    mov rdx, QWORD PTR -8[rbp]
    xor rdx, QWORD PTR fs:40
    je  .L3
    call    __stack_chk_fail@PLT
.L3:
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 8.2.1 20181127"
    .section    .note.GNU-stack,"",@progbits