我明白当你使用char array[] = "string"时,字符串常量"string"会从数据段复制到堆栈。这个字符串常量是逐个字符地复制吗?还是编译器获取字符串常量的起始和结束地址并一次性将整个字符串复制到堆栈中?
谢谢。
我明白当你使用char array[] = "string"时,字符串常量"string"会从数据段复制到堆栈。这个字符串常量是逐个字符地复制吗?还是编译器获取字符串常量的起始和结束地址并一次性将整个字符串复制到堆栈中?
谢谢。
没有理由认为有副本存在。
以以下代码为例。
int main() {
char c[] = "hi";
}
main:
pushq %rbp
movq %rsp, %rbp
movw $26984, -16(%rbp)
movb $0, -14(%rbp)
movl $0, %eax
popq %rbp
ret
这取决于编译器和目标架构。
可能存在非常简单的目标架构,例如微控制器,它们没有支持复制内存块的指令。可能存在专门用于教学的非常简单的编译器,即使在支持更有效方法的架构上也会生成逐字节复制。
然而,在这种情况下,您可以假设生产级别的编译器会执行合理的操作,并为大多数流行的架构生成最快的代码,因此您不需要担心它。
不过,检查的最佳方法是阅读编译器生成的汇编代码。
以此测试代码(stack_array_init.c)为例:
#include <stdio.h>
int
main()
{
char a[]="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed\n"
"do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n";
printf("%s", a);
return 0;
}
使用gcc编译成汇编代码,并进行大小优化(以减少读取量),如下所示:
gcc -Os -S stack_array_init.c
.file "stack_array_init.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC1:
.string "%s"
.LC0:
.string "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed\ndo eiusmod tempor incididunt ut labore et dolore magna aliqua.\n"
.section .text.startup,"ax",@progbits
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
subq $136, %rsp
.cfi_def_cfa_offset 144
movl $.LC0, %esi
movl $126, %ecx
leaq 2(%rsp), %rdi
xorl %eax, %eax
rep movsb
leaq 2(%rsp), %rsi
movl $.LC1, %edi
call printf
xorl %eax, %eax
addq $136, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Debian 4.7.2-5) 4.7.2"
.section .note.GNU-stack,"",@progbits
这里,“rep movsb”是将字符串复制到堆栈的指令。
这是来自ARMv4汇编的摘录(可能更容易阅读):
main:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 128
@ frame_needed = 0, uses_anonymous_args = 0
str lr, [sp, #-4]!
sub sp, sp, #132
mov r2, #126
ldr r1, .L2
mov r0, sp
bl memcpy
mov r1, sp
ldr r0, .L2+4
bl printf
mov r0, #0
add sp, sp, #132
ldr lr, [sp], #4
bx lr
.L3:
.align 2
.L2:
.word .LC0
.word .LC1
.size main, .-main
.section .rodata.str1.4,"aMS",%progbits,1
.align 2
.LC1:
.ascii "%s\000"
.space 1
.LC0:
.ascii "Lorem ipsum dolor sit amet, consectetur adipisicing"
.ascii " elit, sed\012do eiusmod tempor incididunt ut labor"
.ascii "e et dolore magna aliqua.\012\000"
.ident "GCC: (Debian 4.6.3-14) 4.6.3"
.section .note.GNU-stack,"",%progbits
我不确定你在“逐个字符”和“整个字符串”复制方法之间的区别上的意思。字符串通常不是机器级实体,这意味着它不可能被复制为“整个字符串”。你希望如何实现这一点?
字符串始终会被“逐个字符”复制,至少在概念上是这样。现在,当涉及到复制扩展内存区域时,编译器可以通过尽可能执行按字(而不是按字节)复制来优化复制过程。类似的优化可能会在处理器微架构层面上实现。
但无论如何,在一般情况下,复制都是作为一个“迭代”过程实现的,而不是作为“整个字符串”的某个原子操作。
此外,聪明的编译器可能会意识到,在某些情况下根本不需要复制。例如,如果您的代码不修改array
对象并且不依赖于其地址标识,则编译器可能会简单地决定直接使用原始字符串文字,而不进行任何复制(即基本上将您的char array [] =“string”
静默替换为const char *array =“string”
)。