Где хранятся строковые данные?

Я написал небольшую программу на C:

#include <stdio.h>

int main()
{
    char s[] = "Hello, world!";
    printf("%sn", 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    , %rsp
    movq    %fs:40, %rax
    movq    %rax, -8(%rbp)
    xorl    %eax, %eax
    movl    19043144, -32(%rbp)
    movl    98597231, -28(%rbp)
    movl    84828783, -24(%rbp)
    movw    , -20(%rbp)
    leaq    -32(%rbp), %rax
    movq    %rax, %rdi
    call    puts
    movl    , %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

Я не понимаю код сборки, но я не вижу нигде строковое сообщение. Итак, как исполняемый файл знает, что печатать?

5 ответов


это здесь:

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

в этом конкретном случае компилятор генерирует встроенные инструкции для генерации литеральной Строковой константы перед вызовом printf. Конечно, в других ситуациях он может этого не делать, но вместо этого может хранить строковую константу в другом разделе памяти. Итог: вы не можете делать никаких предположений о том, как и где компилятор будет генерировать и хранить строковые литералы.


строка здесь:

movl    19043144, -32(%rbp)
movl    98597231, -28(%rbp)
movl    84828783, -24(%rbp)

Это копирует кучу значений в стек. Эти значения являются вашей строкой.


строка константы хранятся в двоичном приложения. Где именно находится ваш компилятор.


сборка не имеет понятия "строка". Таким образом," строка " на самом деле является куском памяти. Строка хранится где-то в памяти (до компилятора), тогда вы можете манипулировать этим куском данных, используя его адрес памяти (указатель).

если строка постоянный, компилятор может хотите использовать его как константы, а не хранить его в памяти, что быстрее. Это ваш случай, как указал Павел R:

movl    19043144, -32(%rbp)
movl    98597231, -28(%rbp)
movl    84828783, -24(%rbp)

вы не можете сделать предположения о том, как компилятор будет обрабатывать вашей строке.


в дополнение к вышесказанному компилятор может видеть, что на ваш строковый литерал нельзя ссылаться напрямую (т. е. не может быть никаких допустимых указателей на вашу строку), поэтому он может просто скопировать его в строку. Если, однако, вы назначаете указатель символа вместо этого, т. е.

char *s = "Hello, world!";

компилятор инициализирует строковый литерал где-то в памяти, так как теперь вы можете указать на него. Эта модификация производит на моей машине:

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

один можно сделать предположение о строковых литералах: если указатель инициализирован литералом, он будет указывать на статический массив символов, хранящийся где-то в памяти. В результате указатель действителен в любой части программы, например, вы можете вернуть указатель на строковый литерал инициализирован в функции, и он будет действителен.