В чем разница между этими двумя типами кода? - PullRequest
0 голосов
/ 12 ноября 2010

Вот 2 типа фрагментов кода, которые имеют одинаковые выходы.

char *p = "abc";
::printf("%s",p);

И

::printf("%s","abc");

Есть ли разница в том, где в памяти хранится строка "abc"?

Однажды я услышал, что во втором коде строка "abc" помещается компилятором в постоянную память (часть .text?)

Как отличить эту разницу от кода, если есть?

Большое спасибо.

Обновление

Мое нынешнее понимание:

когда мы пишем:

char *p="abc"

Хотя это, кажется, только декларативный оператор , но на самом деле компилятор сгенерирует для него много обязательных инструкций . Эти инструкции будут выделять правильное пространство в кадре стека содержащего метода, это может выглядеть так:

subl %esp, $4

тогда адрес строки «abc» перемещается в это выделенное пространство, это может быть так:

movl $abc_string_address, -4(%ebp)

Строка "abc" сохраняется в образе исполняемого файла. Но где в памяти это ( я имею в виду строку ) будет загружено полностью, зависит от реализации компилятора / компоновщика, если он загружен в доступную только для чтения часть адресного пространства процесса (т.е. бит защиты страницы памяти помечается как «только для чтения»), тогда p является указателем только для чтения, если он загружен в часть «r / w», то p доступен для записи.

Поправь меня, если я ошибаюсь. Теперь я просматриваю код сборки, сгенерированный gcc, чтобы получить подтверждение моего понимания. Я скоро обновлю эту тему.

Ответы [ 4 ]

3 голосов
/ 12 ноября 2010

Есть ли разница в том, где строка "abc" хранится в памяти?

Нет, и это верно для обоих. Строковые литералы хранятся в сегменте только для чтения. Однако, если вы объявите вашу переменную как char [], она будет скопирована в стек, т.е. не только для чтения.

3 голосов
/ 12 ноября 2010

Нет разницы в том, где хранится строковый литерал.Единственное отличие состоит в том, что первый также выделяет место в стеке для переменной для хранения указателя.

1 голос
/ 12 ноября 2010

Да, он не будет храниться в разных местах, все они являются переменными, известными во время компиляции, поэтому компилятор сгенерирует код сборки, и строка "abc" будет находиться в сегменте данных, который является инициализированными данными.,Раздел .bss предназначен для унифицированных данных.

Попробуйте скомпилировать с помощью gcc и опции -s.Он сгенерирует файл .s, который является кодом сборки.Переменная «abc» будет находиться в сегменте .rodata, так же, как и .data для сборки NASM.

Вот код сборки, если вы не хотите выполнять работу:

Этодля char * c = "abc";printf ("% s \ n", c);Обратите внимание, что в этом файле больше строк кода, чем в другом, так как этот код выделяет переменную-указатель, и выведите эту переменную, другое решение не использует переменную, оно просто ссылается на статический адрес памяти.

    .file   "test.c"
    .section    .rodata
.LC0:
    .string "abc"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $.LC0, 28(%esp)
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    puts
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

И это для printf ("abc \ n");

    .file   "test2.c"
    .section    .rodata
.LC0:
    .string "abc"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    movl    $.LC0, (%esp)
    call    puts
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

Для вашего редактирования:

Я не думаю, что какой-либо компилятор будетпоместите p в постоянную память, поскольку вы объявили ее как переменную в коде, это не скрытая / защищенная переменная, сгенерированная компилятором, это переменная, которую вы можете использовать, когда захотите.

Если вы сделаете char* p = "abc";, то компилятор подставит размер указателя в стек, и в более поздней инструкции он вставит адрес памяти строки «abc» (теперь она помещена только для чтения) в регистр, и есликомпилятору нужен регистр, сохраните его значение в стеке.

Если вы сделаете printf("abc");, никакая переменная не будет размещена, так как компилятор знает значение строки во время компиляции, поэтому он просто вставляет туда числоотносительный к началу исполняемого файла), и он может читать содержимое этой части памяти.В этом варианте вы можете скомпилировать его, сгенерировать .exe, затем использовать редактор HEX, найти строку «abc» и изменить ее на «cba» или любую другую (возможно, это будет одна из первых строк или однаиз последних), если компилятор генерирует простой .exe-файл, подобный этому, что вероятно.

1 голос
/ 12 ноября 2010

Без разницы, кроме указателя на символ, выделенного в стеке для первого.

Они оба используют строковый литерал, разделенный двойными кавычками.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...