Строковый буквальный адрес в единицах перевода

Я хотел бы спросить, переносимо ли полагаться на строковый буквальный адрес в единицах перевода? То есть:

Данный файл foo.c имеет ссылку на строковый литерал "I'm a literal!", правильно ли и переносимо полагать, что в другом заданном файле, например bar.c, тот же строковый литерал "I'm a literal!" будет иметь то же самое адрес памяти? Учитывая, что каждый файл будет переведен в отдельный .o файл.

Для лучшей иллюстрации приведен пример кода:

# File foo.c
/* ... */
const char * x = "I'm a literal!"

# File bar.c
/* ... */
const char * y = "I'm a literal!"

# File test.c
/* ... */
extern const char * x;
extern const char * y;
assert (x == y); //Is this assertion going to fail?

И пример командной строки gcc:

gcc -c -o foo.o -Wall foo.c
gcc -c -o bar.o -Wall bar.c
gcc -c -o test.o -Wall test.c
gcc -o test foo.o bar.o test.o

А как насчет той же единицы перевода? Будет ли это надежным, если строковые литералы находятся в той же единице перевода?


person Felipe Lavratti    schedule 09.10.2014    source источник
comment
Нет, на это нельзя полагаться.   -  person Neil Kirk    schedule 09.10.2014
comment
@NeilKirk Спасибо, Нил, я отредактировал и добавил второстепенный вопрос. Любое обоснование этого было бы большим просветлением.   -  person Felipe Lavratti    schedule 09.10.2014
comment
@fanl - Набор компиляторов Visual C ++ имеет возможность объединения строк, при котором один и тот же литерал будет иметь одинаковое значение указателя. Ключевое слово в последнем предложении - option. Это означает, что нельзя полагаться на равенство значений указателей.   -  person PaulMcKenzie    schedule 09.10.2014
comment
Я полагаю, что рациональным будет позволить более быструю компиляцию. Объединение строк имеет свою стоимость.   -  person Neil Kirk    schedule 09.10.2014
comment
@PaulMcKenzie Я ожидал, что это будет работать только с const строками.   -  person Alnitak    schedule 09.10.2014
comment
@Alnitak Строковый литерал не является константной строкой?   -  person Neil Kirk    schedule 09.10.2014
comment
@NeilKirk Это зависит от языка. В C ++ строковый литерал имеет тип char const[] и является константным объектом. В C строковый литерал имеет тип char[]. Но есть особые правила, согласно которым вы не можете его изменять, иначе это приведет к неопределенному поведению.   -  person James Kanze    schedule 09.10.2014
comment
Я забываю, разрешено это или нет, но со строкой, отличной от const, я считаю возможным (но может привести к неопределенному поведению) для программиста перезаписать содержимое строки, что, если оно объединено в пул, не сработает.   -  person Alnitak    schedule 09.10.2014
comment
Что касается того, почему объединение не требуется: ранние компиляторы C не объединяли в пул, и действительно позволяли изменять строковый литерал, а стандарт C не хотел заставлять разработчиков этих компиляторов нарушать существующие код.   -  person James Kanze    schedule 09.10.2014
comment
@Alnitak Ага, мои комментарии относятся только к C ++, а не C. Совершенно не разрешается перезаписывать строковый литерал в C ++.   -  person Neil Kirk    schedule 09.10.2014
comment
Обратите внимание, что у вас может быть static char buf[], чтобы принудительно иметь тот же адрес. Итак, с C ++ следующее может помочь Выравнивание статических строковых литералов (и вы можете забыть о выравнивании).   -  person Jarod42    schedule 09.10.2014


Ответы (2)


Вы не можете полагаться на идентичные строковые литералы, имеющие одно и то же место в памяти, это решение реализации. В проекте стандарта C99 говорится, что это не указано, являются ли одни и те же строковые литералы разными, из раздела 6.4.5 Строковые литералы:

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

Для C ++ это описано в черновом стандартном разделе 2.14.5 Строковые литералы, в котором говорится:

Все ли строковые литералы различны (то есть хранятся в неперекрывающихся объектах) определяется реализацией. Эффект от попытки изменить строковый литерал не определен.

Компилятору разрешено объединять строковые литералы, но вы должны понимать, как это работает от компилятора к компилятору, поэтому это не будет переносимым и потенциально может измениться. Visual Studio включает параметр для объединения строковых литералов

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

Обратите внимание, что это соответствует требованиям В некоторых случаях.

gcc поддерживает объединение в пул и между модулями компиляции, и вы можете включить его с помощью - fmerge-константы:

Попытка объединить идентичные константы (строковые константы и константы с плавающей запятой) в единицах компиляции.

Этот параметр используется по умолчанию для оптимизированной компиляции, если ассемблер и компоновщик поддерживают его. Используйте -fno-merge-constants, чтобы запретить такое поведение.

обратите внимание, использование попытки и если ... поддерживает его.

Что касается обоснования, по крайней мере для C, не требующего объединения строковых литералов в пул, мы можем видеть из этого заархивировал обсуждение строковых литералов на comp.std.c, которое объяснялось широким разнообразием реализаций в то время:

GCC мог служить примером, но не мотивацией. Частично желание иметь строковые литералы в данных, преобразовываемых в ПЗУ, было связано с поддержкой, например, преобразования в ПЗУ. Я смутно припоминаю, что использовал пару реализаций C (до того, как было принято решение X3J11), где строковые литералы либо автоматически объединялись в пул, либо сохранялись в разделе программы с постоянными данными. Учитывая существующее разнообразие практик и доступность простого обходного решения, когда требуются исходные свойства UNIX, казалось лучше не пытаться гарантировать уникальность и возможность записи строковых литералов.

person Shafik Yaghmour    schedule 09.10.2014
comment
Вы имеете в виду, что и то, и другое, ожидая, что адрес будет одинаковым во всех единицах перевода и внутри них, ненадежен? - person Felipe Lavratti; 09.10.2014
comment
@fanl Так он сказал. В исходном K&R C каждый строковый литерал гарантированно был уникальным с другим адресом (и вам разрешалось изменять строковый литерал); исходный стандарт C изменил это, и разрешил одинаковые строковые литералы иметь один и тот же адрес. - person James Kanze; 09.10.2014
comment
@fanl это ненадежно во всех случаях, хотя компиляторы поддерживают объединение, и gcc, и Visual Studio указывают в своих документах, что это применимо только в некоторых случаях. - person Shafik Yaghmour; 15.10.2014

Нет, вы не можете рассчитывать на тот же адрес. Если это случится, то случится. Но его ничто не заставляет.

§ 2.14.5/p12

Все ли строковые литералы различны (то есть хранятся в неперекрывающихся объектах) определяется реализацией. Эффект от попытки изменить строковый литерал не определен.

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

В MSVC, например, адреса полностью различаются в обоих случаях, но опять же: ничто не мешает компилятору объединить значения указателей (даже where, поскольку ограничение раздела только для чтения обязан).

person Marco A.    schedule 09.10.2014