将字符串文字分配给char数组,字符串文字如何复制到堆栈中?

时间:2013-04-22 17:41:25

标签: c string stack literals

我理解当你执行char array [] =“string”时,字符串文字“string”会从数据段复制到堆栈。字符串文字是否逐字符复制?或者编译器获取字符串文字的起始和结束地址并一次将整个字符串复制到堆栈中?

感谢

4 个答案:

答案 0 :(得分:5)

只要观察到的结果相同,编译器就会执行“想要”执行的任何操作。有时根本没有副本。

C标准没有指定副本的完成方式,因此C实现可以通过任何方式自由地实现结果。 C标准强加的唯一要求是可观察的结果(例如写入标准输出的文本)必须按照定义。

当工程师设计高质量的C实现时,他们会花一些时间考虑在这种情况下复制字符串的最佳方法,并且他们将寻求设计一个在每种情况下选择最佳方式的编译器。可以使用“立即移动值”指令构建一个短字符串。可以通过调用memcpy来复制长字符串。可以通过对memcpy的内联调用来复制中间字符串,实际上是一些每个移动几个字节的指令。

当工程师设计一个廉价的C实现时,只需完成工作就可以将代码移植到机器上但不需要快速,他们会为他们做最简单的事情。

有时编译器根本不会复制字符串:如果编译器可以告诉您不需要副本,则没有理由进行复制。例如,如果编译器发现您只是将字符串传递给printf而根本不修改它,那么编译器会通过将原始文件传递给printf来获得相同的结果而无需复制。< / p>

答案 1 :(得分:0)

我不确定你区分&#34;字符和#34;和&#34;整个字符串&#34;复制方法。字符串通常不是机器级实体,这意味着它不可能被复制为&#34;整个字符串&#34;。您如何期待这种情况发生?

字符串将始终按字符“#34;”进行复制,至少在概念上是这样。现在,在复制扩展内存区域时,编译器可以通过逐个字(而不是逐字节)复制来优化复制过程。可以在处理器微架构级别实现类似的优化。

但无论如何,在一般情况下,复制是作为迭代进程实现的,而不是作为&#34;整个字符串&#34;的一些原子操作。

最重要的是,智能编译器可能会意识到在某些情况下根本不需要复制。例如,如果您的代码不修改array对象并且不依赖于其地址标识,则编译器可能只是决定直接使用原始字符串文字,而根本不进行任何复制(即基本上安静地替换您的char array[] = "string"const char *array = "string"

答案 2 :(得分:0)

没有理由认为有副本。

以下面的代码为例。

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

通过将数组的值设置为值26984来初始化数组的内存。该值恰好由两个字节0x68和0x69表示,它们是'h'和'i'的ascii值。根本没有字符串的数据段表示,并且不会通过逐个字符地复制任何内容或通过任何其他聪明的复制方法来初始化数组。

当然这只是一个编译器的实现(g ++ 4.8),只要符合语言规范,其他编译器就可以做任何他们想做的事。

答案 3 :(得分:0)

这取决于编译器和目标体系结构。

可能有非常简单的目标架构,如微控制器,其中 没有指示支持复制内存块。可能有 存在非常简单的用于教学的编译器,它们逐字节地生成 甚至在支持更有效方法的架构上进行复制。

但是,您可以假设生产级编译器可以执行此操作 合理的事情,并为最流行的产生最快的代码 在这种情况下,你不需要担心它。

但是,最好的检查方法是阅读编译器的程序集 产生。

拿这个测试代码(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

以下是x86-64的输出:

        .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

根据我对ARM程序集的理解,这看起来就像代码一样 调用memcpy将字符串复制到堆栈数组中。虽然这没有 显示memcpy的程序集,我希望它使用最快的一个 可用的方法。