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

アセンブリには「文字列」の概念がありません。したがって、「文字列」は実際にはメモリのチャンクです。文字列はメモリのどこかに(コンパイラまで)保存され、メモリアドレス(ポインタ)を使用してこのデータのチャンクを操作できます。

文字列がconstantの場合、コンパイラはそれをメモリに格納する代わりに定数として使用したい場合があります。これはより高速です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

文字列リテラルについて 1 つの仮定を立てることができます: ポインターがリテラルに初期化される場合、それはメモリ内のどこかに保持されている静的な char 配列を指します。その結果、ポインターはプログラムのどの部分でも有効です。たとえば、関数で初期化された文字列リテラルへのポインターを返すことができ、それは引き続き有効です。

于 2013-03-05T13:54:37.823 に答える