6

我写了一个小c程序:

#include <stdio.h>

int main()
{
    char s[] = "Hello, world!";
    printf("%s\n", s);
    return 0; 
}

编译为(在我的 linux 机器上):

    .file   "hello.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $32, %rsp
    movq    %fs:40, %rax
    movq    %rax, -8(%rbp)
    xorl    %eax, %eax
    movl    $1819043144, -32(%rbp)
    movl    $1998597231, -28(%rbp)
    movl    $1684828783, -24(%rbp)
    movw    $33, -20(%rbp)
    leaq    -32(%rbp), %rax
    movq    %rax, %rdi
    call    puts
    movl    $0, %eax
    movq    -8(%rbp), %rdx
    xorq    %fs:40, %rdx
    je  .L3
    call    __stack_chk_fail
.L3:
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2"
    .section    .note.GNU-stack,"",@progbits

我不明白汇编代码,但我在任何地方都看不到字符串消息。那么可执行文件如何知道要打印什么?

4

5 回答 5

12

它在这里:

movl    $1819043144, -32(%rbp) ; 1819043144 = 0x6C6C6548 = "lleH"
movl    $1998597231, -28(%rbp) ; 1998597231 = 0x77202C6F = "w ,o"
movl    $1684828783, -24(%rbp) ; 1684828783 = 0x646C726F = "dlro"
movw    $33, -20(%rbp)         ;         33 =     0x0021 = "\0!"

在这种特殊情况下,编译器会生成内联指令以在调用printf. 当然,在其他情况下,它可能不会这样做,而是将字符串常量存储在另一段内存中。底线:您不能对编译器将如何或在何处生成和存储字符串文字做出任何假设。

于 2013-03-05T12:16:08.673 回答
3

字符串在这里:

movl    $1819043144, -32(%rbp)
movl    $1998597231, -28(%rbp)
movl    $1684828783, -24(%rbp)

这会将一堆值复制到堆栈中。这些值恰好是您的字符串。

于 2013-03-05T12:16:27.800 回答
1

字符串常量存储在应用程序的二进制文件中。究竟在哪里取决于您的编译器。

于 2013-03-05T12:16:38.860 回答
1

程序集没有“字符串”概念。因此,“字符串”实际上是一块内存。该字符串存储在内存中的某个位置(直到编译器),然后您可以使用它的内存地址(指针)来操作这块数据。

如果您的字符串是常量,编译器可能希望将其用作常量而不是将其存储到内存中,这样会更快。正如 Paul R 所指出的,这是您的情况:

movl    $1819043144, -32(%rbp)
movl    $1998597231, -28(%rbp)
movl    $1684828783, -24(%rbp)

您不能假设编译器将如何处理您的字符串。

于 2013-03-05T12:18:12.337 回答
0

除了上述之外,编译器可以看到您的字符串文字不能被直接引用(即不能有任何有效的指向您的字符串的指针),这就是它可以直接复制它的原因。但是,如果您改为分配字符指针,即

char *s = "Hello, world!";

编译器将在内存中的某处初始化字符串文字,因为您现在当然可以指向它。此修改在我的机器上产生:

.LC0:
    .string "Hello, world!"
    .text
    .globl  main
    .type   main, @function

可以对字符串文字做出一个假设:如果将指针初始化为文字,它将指向内存中某处保存的静态 char 数组。因此,指针在程序的任何部分都有效,例如,您可以返回指向在函数中初始化的字符串字面量的指针,它仍然有效。

于 2013-03-05T13:54:37.823 回答