C中使用字符串常量初始化数组的机制
该定义是否:C中使用字符串常量初始化数组的机制,c,arrays,c-strings,C,Arrays,C Strings,该定义是否: char arr_of_chars[] = "hello world"; 在内存中的某个位置创建一个常量字符数组(以null结尾),然后将该数组的内容复制到arr\u of_chars,还是直接将其分配到arr\u of_chars 到底是什么机制在这里起作用 “…在内存中的某个位置创建一个常量字符数组(以null结尾),然后将该数组的内容复制到arr_of_chars” 的确如此。字符串literal“hello world”存储在程序的.rodata部分的某个位置,除非编译器
char arr_of_chars[] = "hello world";
在内存中的某个位置创建一个常量字符数组(以null结尾),然后将该数组的内容复制到arr\u of_chars
,还是直接将其分配到arr\u of_chars
到底是什么机制在这里起作用
“…在内存中的某个位置创建一个常量字符数组(以null结尾),然后将该数组的内容复制到arr_of_chars”
的确如此。字符串literal
“hello world”
存储在程序的.rodata
部分的某个位置,除非编译器设法完全优化它(取决于数组的范围)。从那里它被复制到您的数组中 这将在常量段中创建以null结尾的字符串hello world\0
在main函数中,该字符串将被复制到字符数组中
让我突出显示汇编输出中的几行,以澄清这一点
PUBLIC ??_C@_0M@LACCCNMM@hello?5world?$AA@
这将创建一个公共令牌
CONST SEGMENT
??_C@_0M@LACCCNMM@hello?5world?$AA@ DB 'hello world', 00H
CONST ENDS
这会将以null结尾的常量字符串分配给令牌
lea rax, QWORD PTR arr_of_chars$[rbp]
lea rcx, OFFSET FLAT:??_C@_0M@LACCCNMM@hello?5world?$AA@
mov rdi, rax ; Set destination to stack location
mov rsi, rcx ; Set source to public token
mov ecx, 12 ; Set counter to number of times to repeat
rep movsb ; Copy single byte from source to destination and increment locations
这将设置源和目标,并逐字符复制12次,即“hello world”和空终止符的长度。目标是堆栈上的一个位置,源是公共令牌。您所要求的不是由C指定的。简而言之,C是根据抽象机器及其可观察的行为指定的。在本例中,这意味着您只知道有一个数组变量
arr\u of_chars
是从字符串文本初始化的
CONST SEGMENT
??_C@_0M@LACCCNMM@hello?5world?$AA@ DB 'hello world', 00H
CONST ENDS
当谈到段、复制等时,您已经谈到了C的具体实现以及它们在做什么。假设您的
arr\u of_chars
在文件范围内,并且给定了一个目标机器/系统,该机器/系统知道带有数据段的二进制文件,C编译器可以将初始化的数组直接放在数据段中——可观察的行为与运行时首先将字节复制到数组的方法没有什么不同。它是字符串在C中存储的主题
字符串可以通过以下方式存储:
char *str = NULL;
int size = 6;
str = (char *) malloc(sizeof(char)*size);
*(str+0) = 'H';
*(str+1) = 'E';
*(str+2) = 'L';
*(str+3) = 'L';
*(str+4) = 'O';
*(str+5) = '\0';
hello world
是放置在系统中某处的以null结尾的字符串。初始化变量时,字符串被复制到RAM。如果变量是全局的或静态的,则初始化也是如此,并且在运行时不会进行复制。如果变量是自动的,则初始化是动态的,每次实例化变量时都会执行初始化。@TomKarzes我不相信这一点。考虑这一点:<代码> const char A[] =“Hello World”;字符b[]=“你好,世界”代码>。您有两个精确的值,即使它们是全局值或静态值,在.rodata部分中也只有一个hello world
内存存储,特别是在嵌入式系统上。它将首先在运行时复制到非常量变量。@tilz0R这完全由编译器决定。什么“更好”取决于目标系统和确切的要求。@在这种情况下,b
通常与相应的初始化值一起放置在数据部分。初始化将作为程序加载到内存的结果发生。这是一般规则中唯一的例外,因为它复制字符串本身,而不是像通常那样复制指针:)。这里没有任何例外。这是一个初始化,不是赋值。什么是.rodata
?-)@JensGustedt尽管C标准对计算机内存和段一无所知,但现实世界中对段有行业标准的命名约定.rodata
将是只读数据段,由流行的链接文件格式使用。可以找到行业标准内存段的摘要和说明。检查一个具体实现的行为如何回答这个问题?@FelixPalmen-你是对的。我所发布的只是一个观察。但我不确定标准是否定义了执行此操作的方法。但是,该操作是使用字符串文本初始化的数组。因此字符串文字必须存储在某个地方,并且必须在初始化期间复制到数组中。标准没有指定它,这是我的观点;)不,不一定要将字符串文本存储在某个地方。这完全取决于目标的二进制格式和编译器的决定,如果这个确切的字符串文字仅用于初始化这个变量,那么“消除”它并将初始化的数组放入数据段可能是一个有效的决定——例如:)我同意。因此,我想我还将对其他一些著名的C编译器进行一些观察。:)确切地如果字符串很短,初始化的另一种可能性是使用硬件寄存器或立即数,并且根据char
数组的使用情况,根本不在内存中表示它。
char *str = NULL;
int size = 6;
str = (char *) malloc(sizeof(char)*size);
*(str+0) = 'H';
*(str+1) = 'E';
*(str+2) = 'L';
*(str+3) = 'L';
*(str+4) = 'O';
*(str+5) = '\0';