Показать строки в скомпилированном двоичном

Предположим, что у меня есть простая программа C, и я скомпилирую ее с помощью gcc -o hello hello.c:

#include<stdio.h>

main()
{
    printf("hello");
}

Теперь я хочу отобразить "строки" с помощью утилиты strings:

$ strings hello
/lib64/ld-linux-x86-64.so.2
__gmon_start__
libc.so.6
printf
__libc_start_main
GLIBC_2.2.5
fffff.
l$ L
t$(L
|$0H
hello
;*3$"

и, как и ожидалось, я вижу строку "привет" в двоичном формате.

Однако, когда я изменяю свою программу C и ставил "привет" как константу:

#include<stdio.h>

char s[6] = {'h','e','l','l','o','\0' } ;

main()
{
    printf("%s\n", s);
}

Я больше не вижу строку "привет" в двоичном формате.

Может кто-нибудь объяснить, почему?

Ответы

Ответ 1

Из man 1 strings (внимание мое):

Для каждого указанного файла строки GNU печатают печатный символ        последовательности длиной не менее 4 символов (или число, указанное        с вариантами ниже), и за ними следует непечатаемый символ.         По умолчанию он только печатает строки из инициализированных и        загруженные разделы объектных файлов; для других типов файлов он печатает        строки из всего файла.

Язык C не определяет строки как первоклассные граждане. Они выражаются как строковые массивы, так и струнные литералы. Например, в такой базовой программе:

#include <stdio.h>

int main(void)
{
    char s[] = "my string";

    printf("%s\n", s);

    return 0;
}

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

Теперь, опираясь на ваш вопрос, оба основных объекта в ваших двух программах имеют одни и те же характеристики:

  • они имеют тип char[6] и имеют одинаковое содержимое (C11 §6.2.5/p20),
  • у них статическая продолжительность хранения, что означает, что они должны быть инициализированы концептуально до выполнения программы (C11 §5.1.2/p1).

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

C11 §6.2.5/p20 Типы:

Тип массива описывает смежно выделенный непустой набор объекты с определенным типом объекта-члена, называемые типом элемента.

C11 §5.1.2/p1 Условия выполнения:

Все объекты со статическим временем хранения должны быть инициализированы (установлено значение их начальные значения) до запуска программы.

Если смотреть на более практичную точку зрения, помимо этой команды strings вы также можете анализировать свои программы с помощью отладчика gdb, более конкретно, используя команду x/s. Вот основная иллюстрация:

$ gcc -g hello.c -o hello
$ gdb -q hello
Reading symbols from /home/grzegorz/hello...done.
(gdb) disas /m main
Dump of assembler code for function main:
6   {
   0x00000000004004c4 <+0>: push   %rbp
   0x00000000004004c5 <+1>: mov    %rsp,%rbp

7       printf("%s\n", s);
   0x00000000004004c8 <+4>: mov    $0x60086c,%edi
   0x00000000004004cd <+9>: callq  0x4003b8 <[email protected]>

8   }
   0x00000000004004d2 <+14>:    leaveq 
   0x00000000004004d3 <+15>:    retq   

End of assembler dump.
(gdb) x/s 0x60086c
0x60086c <s>:    "hello"

Вам может потребоваться сравнить результаты команды disas для ваших программ и посмотреть, есть ли какое-то несоответствие между ними.

Ответ 2

Я больше не вижу строку "привет" в двоичном формате.

Я бы рассмотрел допустимое поведение expected, так как вы больше не предоставляете литерал "hello", а вместо него 6 отдельных char.