Почему эта статическая константа char* зависит от архитектуры инициализации?

Этот вопрос относится к Почему постоянная инициализация нужен статический символ*, но не статический символ**

Ответ на отмеченный вопрос научил меня тому, что инициализация static const char* должна выполняться по адресу, разрешаемому во время компиляции. В том же ответе указывалось, что указатель должен быть const, а не (обязательно) контент, на который указывает указатель. Это отражено в приведенном ниже коде.

Почему тогда следующий код зависит от целевой архитектуры? (т. е. почему этот код генерирует ошибку компиляции с некоторыми, но не со всеми кросс-компиляторами?)

// main.c

static const char* const text = "foo";
static const char* tmp = text;

int main( int argc, char* argv[] ) { return 0; }

Успешная компиляция:

$ /path/to/tgt1-linux-gnu-gcc --version && /path/to/tgt1-linux-gnu-gcc ./main.c && echo $?
tgt1-linux-gnu-gcc (crosstool-NG 1.23.0.485-ee829 - next) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

0

Неудачная компиляция:

$ /path/to/tgt2-linux-gnueabi-gcc --version && /path/to/tgt2-linux-gnueabi-gcc ./main.c
tgt2-linux-gnueabi-gcc (crosstool-NG 1.18.0) 4.7.3 20130102 (prerelease)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

./main.c:4:1: error: initializer element is not constant

person StoneThrow    schedule 21.10.2019    source источник
comment
Видите номера версий? Различные стандарты C по умолчанию.   -  person S.S. Anne    schedule 22.10.2019
comment
Вероятно, вы хотели спросить о различиях между версиями компилятора...? простой - версия 8.2.0 преобразует вашу инициализацию в константу времени компиляции, а версия 4.7.3 - нет. Может быть связано с реализуемой стандартной версией C (или с расширениями GNU).   -  person Myst    schedule 22.10.2019
comment
@Myst - мне не приходило в голову, что это может зависеть от версии компилятора, но я понимаю, к чему вы клоните. Если у вас есть пропускная способность для полного ответа, я был бы признателен. Я задаюсь вопросом, есть ли в языке C конструкция, отличная от использования #define, которую компилятор 4.7.3 интерпретировал бы как константу времени компиляции...?   -  person StoneThrow    schedule 22.10.2019
comment
инициализация должна быть по адресу, разрешаемому во время компиляции, является приближением к фактическому требованию. Утверждение верно, но не является полной характеристикой требования.   -  person John Bollinger    schedule 22.10.2019
comment
Вы уверены, что разница здесь только в версиях компилятора, и одна из них на самом деле не является компилятором C++? Я никогда не слышал о tgt1 и подозреваю, что это поддельный пример, который вы вручную отредактировали, чтобы он выглядел как настоящий инструментальный вывод.   -  person R.. GitHub STOP HELPING ICE    schedule 22.10.2019


Ответы (2)


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

Как уже заметил @JL2210, вы фактически используете два разных компилятора, хотя оба являются версиями GCC. Эти конкретные версии по умолчанию соответствуют совершенно разным языковым стандартам — GCC 4 по умолчанию использует C89, тогда как GCC 8 по умолчанию использует C2011 — но проблема не в этом. Все версии стандарта C имеют очень похожие формулировки в применимых областях, и незначительные различия не меняют того, как они применяются к вашему коду.

Что касается инициализаторов, C89 содержит это языковое ограничение:

Все выражения в инициализаторе для объекта со статической продолжительностью хранения [...] должны быть постоянными выражениями.

тогда как аналогичное ограничение в C11 есть

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

Прежде чем вы будете в восторге от добавления «строковых литералов» в C11, я отмечу, что рассматриваемое выражение инициализации, text, однозначно не является строковым литералом, несмотря на то, как инициализируется сама задействованная переменная. Таким образом, вопрос сводится к тому, является ли это «постоянным выражением». Однако, несмотря на всю const-ность в своем типе, это не так.

Константное выражение — это определенный термин в стандарте. Существует несколько разновидностей, но выражение здесь не является константой нулевого указателя, необходимая здесь альтернатива сводится к адресной константе или такой константе плюс или минус целочисленное константное выражение. Здесь не задействовано целочисленное константное выражение, так что сама адресная константа является оставшимся вариантом.

В C89 это соответствующее определение:

Адресная константа — это указатель на lvalue, обозначающий объект статической длительности хранения, или на указатель функции; он должен быть создан явно, с помощью унарного оператора &, или неявно, с использованием выражения типа массива или функции. Подстрочный индекс массива [] и доступ к члену. операторы и ->, адрес & и косвенность * унарные операторы и приведения указателей могут использоваться при создании адресной константы, но доступ к значению объекта с помощью этих операторов невозможен.

C11 говорит об этом так:

Адресная константа — это нулевой указатель, указатель на lvalue, обозначающий объект со статической продолжительностью хранения, или указатель на указатель функции; он должен быть создан явно с помощью унарного оператора & или целочисленной константы, приведенной к типу указателя, или неявно с использованием выражения типа массива или функции. Подстрочный индекс массива [] и доступ к члену. операторы и ->, адреса & и косвенность * унарные операторы и приведения указателей могут использоваться при создании адресной константы, но доступ к значению объекта с использованием этих операторов невозможен.

(Подчеркнуто в обоих случаях.)

Выражение text не является явным выражением в унарном операторе &. Это не целочисленная константа, приведенная к типу указателя. Это не выражение типа массива (и не типа функции).

В любом случае ваш код не только не соответствует требованиям, но и нарушает языковые ограничения. Поэтому соответствующий компилятор обязан выдать диагностику. GCC не обязан отклонять код, но это одна из подходящих альтернатив. При данных обстоятельствах вполне разумно, что версия 8 понимает, что инициализатор действительно является константой, несмотря на то, что технически это не постоянное выражение. Однако для того, чтобы он действительно соответствовал требованиям диагностики, вам может потребоваться указать параметр -pedantic. Выдача всей такой необходимой диагностики как раз и является целью этой опции.

person John Bollinger    schedule 22.10.2019

Это не зависит от архитектуры. Версии вашего компилятора отличаются, как и доступные оптимизации.

Уведомление:

8.2.0

vs.

4.7.3 20130102 (prerelease)

Вероятно, вы можете обойти это (не проверено) на tgt2 GCC, скомпилировав с -O2.

person S.S. Anne    schedule 21.10.2019