Инициализация объектов со статической продолжительностью хранения в C vs С++
Возможный дубликат:
Что делает основной возврат?
Например, следующий код компилируется без предупреждения:
#include <stdio.h>
int i = i + 1;
int main(int argc, char *argv[])
{
fprintf (stderr, "%d\n", i);
return 0;
}
Я думаю, что это незаконно в синтаксисе, потому что i
используется до его объявления, правильно ли это?
И, на мой взгляд, внешний вид int i = i + 1;
, несомненно, является ошибкой, почему компилятор не предупреждает об этом? Я использую gcc 4.5.1.
Ответы
Ответ 1
(извещение: я имею в виду текущий стандарт С++)
Я не уверен в этом, но если моя интерпретация стандарта верна, код должен быть точным, а не UB.
Первая инициализация этой переменной - это нулевая инициализация объектов со статической продолжительностью хранения, которая происходит до любого другого
выполняется инициализация (§3.6.2 ¶1).
Итак, прежде всего i
устанавливается в ноль.
Затем выполняется динамическая инициализация (т.е. ненулевая и непостоянная инициализация), поэтому она использует текущее значение i
(0) для фактической инициализации. В конце он должен оценить значение 1.
Это подтверждается §8.5 ¶6, в котором явно говорится:
Память, занятая любым объектом статической продолжительности хранения, должна быть инициализирована нулем при запуске программы до начала любой другой инициализации. [Примечание: в некоторых случаях дополнительная инициализация выполняется позже. ]
(Если вы обнаружите недостаток в анализе, просто скажите мне в комментариях, и я буду рад исправить/удалить ответ, это скользкий пол, и я это осознаю:))
Ответ 2
В С++ он синтаксически корректен. В C вы можете инициализировать глобальную переменную только с константой. Таким образом, ваш код не будет компилироваться в C.
В C это юридическое BTW
int main()
{
int i = i+1;
}
3.3.1/1 Точка декларации
Точка объявления для имени сразу после его полного объявления и перед его инициализатором (если есть).
Поведение четко определено как §3.6.2/1
, в котором говорится:
"Объекты со статической продолжительностью хранения (3.7.1) должны быть инициализированы нулями (8.5) до любой другой инициализации".
Ответ 3
Код является незаконным в C.
initializer element is not constant
C99 - 6.7.8 Инициализация
Все выражения в инициализаторе для объекта с длительностью статического хранения должны быть
константные выражения или строковые литералы.
Действителен в С++.
Стандартные состояния С++ в 3.6.2 Инициализация нелокальных объектов:
Объекты со статической продолжительностью хранения (3.7.1) должны быть инициализированы нулями (8.5), прежде чем произойдет любая другая инициализация.
Ответ 4
Ваш код не является законным.
Если ваш компилятор компилирует его без диагностики,
ваш компилятор не является компилятором C
Вы должны использовать константы для инициализации переменной.
В вашем коде выражение инициализатора (i + 1
) не является константой.
Это нарушает 6.7.8/4:
Все выражения в инициализаторе [...] должны быть постоянными выражениями или строковыми литералами.
Ответ 5
Вы не можете присвоить значение переменной, используя другую переменную вне любой функции. Оператор i + 1;
оценивается во время выполнения, а int i = i + 1;
находится за пределами любой функции, поэтому его нужно оценивать во время компиляции.
Ответ 6
Является ли его действительно синтаксически незаконным, я не уверен (он определенно был бы действительным внутри метода). Однако, как вы полагаете, это семантическая проблема, и компилятор должен выдать предупреждение, поскольку i
использовался без инициализации. IMO компилятор C/С++ вообще не предупреждает о такой вещи (Java, например, дал бы ошибку), хотя вы можете включить такое предупреждение, добавив параметр -Wall
в gcc.
Ответ 7
Поскольку компилятор принимает операторы и испускает низкоуровневый код для использования ЦП, вы должны разделить то, что на самом деле происходит здесь. Это будет выглядеть примерно так:
- Создайте слот для памяти "i" .
- Инициализировать память до нуля (обычное поведение по умолчанию).
- Прочитайте значение "i" (которое равно нулю).
- Добавить 1.
- Сохраните его в "i" .
Ответ 8
Я не буду повторять одни и те же вещи: это поведение undefined, вы не должны этого делать... но укажите пример использования (который является общей идиомой), который показывает, почему иногда интересно разрешить использование переменной там (в C):
int * p = malloc( 10 * sizeof *p );
Если использование p
в правой части было запрещено, это было бы ошибкой компилятора. Вы можете обойти его, явно указав тип в rhs:
int * p = malloc( 10 * sizeof(int) );
Но это склонно к тонким ошибкам, если позднее будет изменен тип, поскольку компилятор не обнаружит этот случай:
double * p = malloc( 10 * sizeof(int) ); // will compile and probably cause havoc later
Теперь, в С++, я могу только предположить, что он существует для обратной совместимости. Отметим также, что некоторые компиляторы смогут обнаружить это недопустимое использование и вызвать предупреждение из более общей группы неинициализированного использования переменной:
int i = i + 1;
// ^ uninitialized read
Однако существуют другие ситуации на С++, где вы можете передать ссылку/указатель на неинициализированные объекты, и это отлично. Рассмотрим:
template <typename T>
struct NullObjectPattern { // intentionally out of order:
T* ptr;
T null;
NullObjectPattern() : ptr( &null ), null() {}
T& operator*() { return *ptr; }
T* operator->() { return ptr; }
};
Пока null
еще не инициализирован, использование его в выражении, которое принимает только его адрес (но не разыгрывает его), четко определено: расположение памяти существует, оно было выделено и присутствует. Сам объект не был инициализирован, и в качестве такого разыменования он вызовет UB, но тот факт, что неинициализированный объект используется в выражении, не означает, что код действительно ошибочен.
Ответ 9
Чтобы ответить на ваш вопрос о "i
, используется до его объявления, правильно?"
Не в С++. [basic.scope.pdecl]
говорит
Точка объявления для имени сразу же после его полного объявления (раздел 8) и перед его инициализатором (если есть), за исключением случаев, указанных ниже. [Пример:
int x = 12;
{ int x = x; }
Здесь второй x
инициализируется собственным (неопределенным) значением. - конец примера]