首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么可以在不定义数组长度的情况下用C声明数组?

为什么可以在不定义数组长度的情况下用C声明数组?
EN

Stack Overflow用户
提问于 2019-03-11 14:53:38
回答 3查看 256关注 0票数 6

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

代码语言:javascript
复制
int arr[] = {10, 20, 30, 40, 50};

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-03-14 16:03:49

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

但是要理解这个答案,就必须深入挖掘,并询问为什么在编译过程中知道确切的大小是如此重要。泛指:为您的程序定义虚拟地址空间。其中一部分是存储局部变量的堆栈,不能将其与堆内存( malloc工作的地方)混淆。堆栈是一个LIFO列表,还包含所有函数调用及其参数。它用于函数的末尾,用于回跳,您来自哪里,并为此存储了一个内存地址。当您处于函数中时,堆栈上的所有内容都必须被释放,以便到达正确的回跳地址并避免潜在的分段错误。

幸运的是,C为我们自动进行这种类型的内存管理,并在所有自动变量被认为“超出作用域”时释放它们。要做到这一点,我们需要精确的大小,我们已经推到堆栈,这就是为什么编译器已经需要知道的大小。

要说明编译器如何翻译代码和硬代码,请参见此处:

代码语言:javascript
复制
$ 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
票数 2
EN

Stack Overflow用户

发布于 2019-03-11 14:56:35

这是一个方便的功能。数组的大小是从初始化器中推导出来的,因此不必拼写它:

代码语言:javascript
复制
int arr[] = {10, 20, 30, 40, 50};

相等于

代码语言:javascript
复制
int arr[5] = {10, 20, 30, 40, 50};

这方面的另一个例子(感谢Eugene Sh.)是字符串初始化器:

代码语言:javascript
复制
char str[] = "asd";

相等于

代码语言:javascript
复制
char str[4] = "asd";

需要知道的一件重要的事情是,当用作函数参数的类型时,情况是不同的。以下所有表格:

代码语言:javascript
复制
void foo(int v[])

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

所有这些都是相互等效的,它们被转化为:

代码语言:javascript
复制
void foo(int* v)

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

票数 16
EN

Stack Overflow用户

发布于 2019-03-11 15:16:38

补充现有答案,引用C11,第6.7.9节,P22

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

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

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55104620

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档