Инициализация объектов со статической продолжительностью хранения в 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 инициализируется собственным (неопределенным) значением. - конец примера]