我写了一个小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
我不懂汇编代码,但我看不到字符串消息。那么可执行文件如何知道要打印什么?
答案 0 :(得分: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
之前生成内联指令以生成文字字符串常量。当然在其他情况下它可能不会这样做,但可能会将字符串常量存储在内存的另一部分中。结论:您无法对编译器生成和存储字符串文字的方式或位置做出任何假设。
答案 1 :(得分:3)
字符串在这里:
movl $1819043144, -32(%rbp)
movl $1998597231, -28(%rbp)
movl $1684828783, -24(%rbp)
这会将一堆值复制到堆栈中。这些值恰好是你的字符串。
答案 2 :(得分:1)
string 常量存储在应用程序的二进制文件中。具体取决于您的编译器。
答案 3 :(得分:1)
程序集没有“字符串”概念。因此,“字符串”实际上是一块内存。字符串存储在内存中的某个位置(直到编译器),然后您可以使用其内存地址(指针)来操作此数据块。
如果您的字符串常量,编译器可能想要将其用作常量而不是将其存储到内存中,这样会更快。正如Paul R所指出的那样,这是你的情况:
movl $1819043144, -32(%rbp)
movl $1998597231, -28(%rbp)
movl $1684828783, -24(%rbp)
您不能对编译器如何处理字符串做出假设。
答案 4 :(得分:0)
除了上面的内容之外,编译器还可以看到你的字符串文字不能直接引用(即你的字符串不能有任何有效的指针),这就是它可以直接复制它的原因。但是,如果你指定了一个字符指针,即
char *s = "Hello, world!";
编译器会在内存中的某个位置初始化一个字符串文字,因为你现在可以指向它。这个修改在我的机器上产生:
.LC0:
.string "Hello, world!"
.text
.globl main
.type main, @function
可以对字符串文字做出一个假设:如果指针初始化为文字,它将指向内存中某处的静态字符数组。结果,指针在程序的任何部分都是有效的,例如,你可以返回一个指向函数中初始化的字符串文字的指针,它仍然有效。