Инициализация массива С++

Возможный дубликат:
Как инициализировать массив в C
инициализация массива, ссылается на предыдущий элемент ok?

Интересно, насколько безопасно выполнить такую ​​инициализацию в стандарте c/С++:

int a = 5;
int tab[] = { a , tab[0] + 1 , tab[1] };

Он успешно компилируется и выполняется с помощью gcc 4.5 и clang 2.9, но всегда ли это будет true?


Печать этой таблицы дает 5 6 6. Его инициализируется в глобальном масштабе.


Как правило, это интересно в обоих, c и С++, но я хочу использовать его в С++:)

Ответы

Ответ 1

В стандарте C99 кажется, что порядок инициализации членов гарантирован:

§6.7.8/17: Каждый список инициализаторов, заключенных в фигурную скобку, имеет связанный текущий объект. Если не указано никаких обозначений, подобъекты текущего объекта инициализируются в соответствии с типом текущего объекта: элементы массива в возрастающем порядке подстроки, члены структуры в порядке объявления и первый именованный элемент союз. Напротив, обозначение заставляет следующий инициализатор начинать инициализацию подобъекта, описанного указателем. Инициализация затем продолжается в порядке, начиная со следующего подобъекта после того, как описана обозначением.

Но поскольку @Tomalak упоминает в комментарии, это не дает полной гарантии для операции, поскольку компилятор сначала оценит все аргументы, а затем применит результаты в предыдущем порядке. То есть предыдущая цитата не налагает порядок между инициализацией tab[0] и оценкой выражения tab[0]+1, который используется для инициализации tab[1] (он налагает только порядок между инициализацией tab[0] и tab[1])

Как и в стандарте С++, ни в текущем стандарте, ни в FDIS будущего С++ 0x, похоже, не существует конкретного предложения, определяющего порядок, в котором выполняется инициализация. Единственное упоминание о заказе происходит от

§8.5.1/2 Когда агрегат инициализируется, инициализатор может содержать предложение инициализатора, состоящее из списка разделенных запятыми списков инициализатор-предложений для членов агрегата, записанных при увеличении индекса или порядка членов.

Но это относится только к порядку записи записей в инициализаторе, а не к тому, как он фактически оценивается.

Ответ 2

С++ 03/С++ 11 answer


Нет, не будет.

В правой части =, tab существует 1 но — если он имеет автоматическую продолжительность хранения -— он еще не инициализирован, поэтому использование tab[0] и tab[1] использует неинициализированную переменную.

Если tab находится в области пространства имен (и, следовательно, имеет статическую продолжительность хранения и инициализируется нулем), тогда это "безопасно", но использование tab[0] не даст вам 5.

Трудно предоставить стандартные ссылки для этого, кроме как сказать, что в 8.5 "Initializers" нет ничего, что явно делает это возможным, а правила в других местах заполняют остальные.


1[n3290: 3.3.2/1]: Точка объявления для имени сразу после его полного объявления (раздел 8) и перед его инициализатором (если есть) [..]

Ответ 3

int a =5;
int tab[] = { a , tab[0] + 1 , tab[1] };

Если эти переменные объявлены в области пространства имен, то они в порядке, так как при переменных пространства имен переменные нуль-инициализированы (из-за статической инициализации - читайте this для подробно).

Но если они объявлены в области функций, вторая строка вызывает поведение undefined, поскольку локальные переменные не статически инициализируются, это означает, что tab[0] и tab[1] не инициализированы, которые вы используете для инициализации массив. Чтение неинициализированных переменных вызывает поведение undefined.

Ответ 4

Итак, теперь я проверил пару тестов, касающихся вашей проблемы.

Все компиляции были выполнены с приведенным выше примером кода, используя следующую схему:

$(GCC) -o a.out test.c -Wall -Wextra -pedantic -std=$(STD)

Это дало следующие результаты:

для GCC = gcc, стандарты -std=c89; -std=iso9899:1990; -std=iso9899:199409; -std=gnu89 привели к появлению предупреждения: initializer element is not computable at load time и undefined поведение во время выполнения, что означает, что второе и третье значение массива были случайным мусором.

стандарты -std=c99; std=iso9899:1999; -std=gnu99 не дали этого предупреждения, но также показали поведение undefined во время выполнения.

для GCC = g++, стандарты -std=c++98; -std=gnu++98; -std=c++0x не выдали никаких предупреждений, и код работал так, как вы ожидали, в результате получился массив, содержащий значения {5, 6, 6}.

Однако, поскольку большинство людей советовали, это может быть неразумно использовать это, поскольку ваш код может вести себя по-другому на других компиляторах или, возможно, даже на других версиях одного и того же компилятора, что обычно плохо:)

надеюсь, что это помогло.

Ответ 5

Да - он, вероятно, будет работать так, как вы ожидаете. Нет - (вы не спрашивали, но) Не ​​используйте его, он не делает логики, и это плохая практика.