Подтвердить что ты не робот

Может ли статическая строка const быть выделена в стеке?

const char * foo()
{
    return "abcdef";
}

int main()
{
    printf("%s", foo());
}

Может ли соответствующий компилятор решить выделить "abcdef" в стеке? То есть что в стандарте заставляет компилятор выделить его в разделе .data?

4b9b3361

Ответ 1

Из спецификации С++ § 2.14.5/8 для строковых литералов;

Обычные строковые литералы и строковые литералы UTF-8 также называются узкими строковыми литералами. Узкий строковый литерал имеет тип "массив n const char", где n - размер строки, как определено ниже, и имеет статическую продолжительность хранения (3.7).

Также стоит упомянуть об этом, статическая продолжительность хранения, применяется ко всем строковым литералам; следовательно, L"", u"", u"" и т.д.; § 2.14.5/10-12.

В свою очередь, для статической продолжительности хранения § 3.7.1/1;

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

Следовательно, ваша строка "abcdef" должна существовать на протяжении всей программы. Компилятор может выбрать, где его хранить (и это может быть системное ограничение), но он должен оставаться в силе.

Для спецификации языка C (C11 draft n1570) строковые литералы § 6.4.5/6;

В фазу 7 перевода байта или код нулевого значения добавляется к каждой многобайтовой последовательности символов, которая получается из строкового литерала или литералов. Последовательность многобайтовых символов затем используется для инициализации массива статической продолжительности хранения и длины, достаточной для того, чтобы содержать последовательность. Для символьных строковых литералов элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов.

И статическая продолжительность хранения § 6.2.4/3;

Объект, идентификатор которого объявлен без спецификатора класса хранения _Thread_local, а также с внешней или внутренней связью или со спецификатором класса хранения static, имеет статическую продолжительность хранения. Его время жизни - это полное выполнение программы, и ее сохраненное значение инициализируется только один раз, до запуска программы.

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

Ответ 2

что в стандарте заставляет компилятор выделить его на .data раздел?

Ничего. Но это, безусловно, не может быть в стеке, поскольку указатели на строковый литерал никогда не должны быть признаны недействительными (поскольку литерал имеет статическую продолжительность хранения 1), а значения в стеке перезаписываются другими кадрами на некоторых точка. И объекты со статической продолжительностью хранения обычно лежат на секции, посвященной этому - разделе .data.

В соответствии с правилом as-if он может поместить его в стек, если наблюдаемое поведение программы не изменится; Это очень маловероятно, хотя, так как это никоим образом не повлияет на производительность программы (и ненужные релевантные компиляторы еще не написаны).


1) [lex.string]/8:

Также указываются обычные строковые литералы и строковые литералы UTF-8 как узкие строковые литералы. Узкий строковый литерал имеет тип "массив" n const char ", где n - размер строки, как определено ниже, и имеет статическую продолжительность хранения (3.7).

Ответ 3

Ссылаясь на N1570 (проект C11) 6.4.5/6 Строковые литералы (выделение в будущем):

В фазе 7 перевода байт или код нулевого значения добавляется к каждому мультибайту последовательность символов, которая получается из строкового литерала или литералов. 78)Последовательность многобайтовых символов затем используется для инициализации массива продолжительности статического хранения и длины, достаточной для того, чтобы содержать последовательность. Для символьных строковых литералов элементы массива имеют тип char и инициализируются отдельными байтами мультибайта символьная последовательность.

Это означает, что строковые литералы имеют срок службы всего исполнения программы, как указано в 6.2.4/3 Длительность хранения объектов:

Объект, идентификатор которого объявлен без класса хранения спецификатор _Thread_local, а также с внешней или внутренней связью или с спецификатором класса хранения static, имеет статическую продолжительность хранения. это lifetime - это выполнение цели программы и ее сохраненное значение инициализируется только один раз, до запуска программы.

Маловероятно, что компилятор помещает их в стек из-за своей природы (подсказка: сохранение между вызовами функций).

Обратите внимание, что C Standard не запрещает прямое размещение строковых литералов в стеке. На самом деле он даже не определяет такой термин, как стек или .data. Это для компилятора, чтобы выбрать любое размещение данных, соответствующее стандарту.

Ответ 4

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

Вы можете скопировать эту литеральную строку из раздела RO-data в стек каждый раз:

const char* foo()
{
    const char str[] = "abcdef";
    return str;
}

Но эта функция возвращает указатель.

И вы, безусловно, не хотите, чтобы этот указатель содержал адрес в стеке.

Поэтому нет смысла иметь эту строковую строку, выделенную в стеке.

Ответ 5

Все статические значения должны оставаться выделенными до выхода программы; такие вещи можно было бы расположить в стеке, но только если они выделены до того, как будет выполнен какой-либо код пользователя. Такая конструкция была бы необычной, но могла бы быть предпочтительной, например, архитектуру подключаемого модуля, в которой было желательно, чтобы несколько потоков запускали подключаемый модуль одновременно и каждый экземпляр потока вел себя полностью независимо. Если в архитектуре все экземпляры подключаемого модуля будут иметь одни и те же статические данные, то, по крайней мере, с точки зрения архитектуры подключаемого модуля данные, которые не должны использоваться совместно, не должны быть статическими. Хотя может быть лучше, чтобы каждый экземпляр хранил свои статические данные в блоке пространства, запрошенном из кучи, что потребовало бы, чтобы каждый экземпляр освободил этот блок пространства, когда это было сделано. Каждый экземпляр подключаемого модуля выделяет все свои данные в своем стеке (включая подходящий размер char[], который будет разделен для удовлетворения запросов malloc() или new) до запуска кода пользователя, гарантируя, что уничтожение связанного потока с экземпляром высвободит связанное с ним хранилище.