2016-10-02 4 views
2

После разборки следующий код:Почему локальные строки, объявленные в основной функции, являются частью памяти сегмента Rodata?

#include <stdio.h> 

static const char HELLO1[] = "Howdy"; 

int main(){ 
char hello2[6]="hello\0"; 
printf("%s",string); 
} 

можно видеть, что строки hello1 объявляется в сегменте .rodata, что вполне понятно, как постоянные переменные объявляются в этом сегменте.

Однако hello2 также объявлены в сегменте .rodata. Но локальные переменные объявляются в Stack прямо вправо?

Можете ли вы объяснить мне, почему эта строка объявлена ​​в этот сегмент?

+1

Ответ на вопрос о совместимости/платформе - которые отсутствуют. – chux

+1

Duke, какой флаг компиляции вы используете? 'Hello2' не создается в стеке в соответствии с примечанием @NulledPointer ниже; они могли бы испускать сложную строку. Я заметил, что «clang» также будет делать это вместе с совокупными типами данных. –

ответ

1

программы C часто использует тетср и MemSet внутренне построить содержимое стеки во время выполнения. Несмотря на то, что это платформа зависит, это общие для кода, как ваша, чтобы расширить на что-то вроде:

#include <stdio.h> 

static const char HELLO1[] = "Howdy"; 

int main(){ 
    // hidden 
    static const char *__temporary_main_hello2 = "hello\0"; 
    char hello2[6]; 
    // hidden 
    memcpy(hello2, __temporary_main_hello2, sizeof(hello2)); 
    printf("%s",string); 
} 

Вы можете видеть теперь, почему привет также в .rodata. Исходные данные для строки исходят из статичности в .rodata, но хранилище для массива hello2 находится в стеке. Компилятор нуждается в способе заполнения hello2 при вызове main. Если платформа имеет удобный способ представления данных непосредственно, это может быть сделано таким образом, но для этого обычно удобнее использовать хорошо оптимизированную memcpy.

+1

В x86 для данных инициализатора чаще используется переход к непосредственным константам в потоке команд, а не копирование из статических данных. См. Мой комментарий к другому ответу. Но, может быть, OP компилируется для ARM? (Где требуется, по крайней мере, три инструкции для хранения 32 бит данных в память.) У Godbolt есть компиляторы ARM тоже: и действительно инициализатор копируется из '.LC0' (после двух инструкций, чтобы получить адрес .LC0 в регистр). https://godbolt.org/g/b7RbW0 –

+0

Извините за мой поздний комментарий. Я использую GCC для x86. Можете ли вы сказать, почему ваш ответ и NulledPointer не совпадают? Является ли ваш ответ объяснением реализации статического хранилища GCC (используя memcpy для создания содержимого стека во время выполнения)? –

+0

Оба ответа приходят к одному и тому же выводу, но я думал, что легче увидеть, что происходит практически с конкретным примером. –

3

String literals существует для жизни программы

Строковые литералы имеют static storage duration, и, таким образом, существуют в памяти для жизни программы.

Static storage duration.

Хранение объекта назначается, когда программа начинается, и освобождается при завершении программы. Существует только один экземпляр объекта . Все объекты, объявленные в области пространства имен (включая глобальное пространство имен ), имеют такую ​​длительность хранения, плюс те, которые объявлены со статическими или extern.

Так gcc реализовать static storage в .RODATA в ELF двоичная

Для дальнейшей разработки ...

char a[] = "Hello world A"; 
char* p = "Hello world P"; 

Для обоих a и p, их строковые литералы имеют статическую продолжительность хранения (это означает, что они оба сохранены в .RODATA) с разницей в том, что для a строковый литерал копируется в этой переменной стека, а p просто указывает на .RODATA память. Именно поэтому вы можете изменить a но не p

Примечания: Знайте, что ссылки выше от c++ синтаксиса, но причина c таких же

+0

Это хороший ответ, однако 'char hello2 [6] =" hello \ 0 ";' не является '' hello \ 0 "' as _string literal_. Инициализатор '' hello \ 0 "' служит синтактическим сахаром для '{'h', 'e', ​​'l', 'l', 'o', '\ 0'}' _array initializer_. –

+0

@GrzegorzSzpetkowski обновленный ответ для вашего замешательства. – PnotNP

+0

C11 6.7.9/p14: 'Массив типа символа может быть инициализирован литералом ** символьной строки ** или строкой UTF-8 литерал, необязательно заключенный в фигурные скобки.'. Я также проверил «char hello2 [6] =« hel »" lo \ 0 ";', который является действительной конструкцией и доказывает вашу точку. –

 Смежные вопросы

  • Нет связанных вопросов^_^