C89, Смешивание объявлений переменных и кода - PullRequest
5 голосов
/ 27 июня 2011

Мне очень любопытно узнать, почему именно компиляторы C89 будут сбрасывать на вас данные, когда вы пытаетесь смешать объявления переменных и код, например, вот так:

rutski@imac:~$ cat test.c
#include <stdio.h>

int
main(void)
{
    printf("Hello World!\n");
    int x = 7;
    printf("%d!\n", x);
    return 0;
}
rutski@imac:~$ gcc -std=c89 -pedantic test.c
test.c: In function ‘main’:
test.c:7: warning: ISO C90 forbids mixed declarations and code
rutski@imac:~$ 

Да, вы можете избежать подобных вещей, избегая -педантики. Но тогда ваш код больше не соответствует стандартам. И как любой, кто способен ответить на этот пост, вероятно, уже знает, это не просто теоретическая проблема. Такие платформы, как компилятор C от Microsoft, применяют этот быстрый стандарт в любых обстоятельствах.

Учитывая, как древний C, я мог бы предположить, что эта особенность связана с некоторой исторической проблемой, восходящей к чрезвычайным аппаратным ограничениям 70-х, но я не знаю деталей. Или я совершенно не прав?

Ответы [ 4 ]

7 голосов
/ 27 июня 2011

В стандарте C написано «ты не будешь», потому что это не разрешалось в более ранних компиляторах C, которые стандартизировал стандарт C89.Это был достаточно радикальный шаг для создания языка, который можно было бы использовать для написания операционной системы и ее утилит.Концепция, вероятно, просто не рассматривалась - никакой другой язык в то время не позволял это (Паскаль, Алгол, PL / 1, Фортран, КОБОЛ), поэтому Си тоже не нуждался в этом.И это, вероятно, делает компилятор немного сложнее в обращении (больше), а исходные компиляторы были ограничены в пространстве из-за кода 64 КиБ и пространства данных 64 КиБ, разрешенного в серии PDP 11 (большие машины;Меньшие из них позволили только 64 КиБ для кода и данных, AFAIK).Таким образом, дополнительная сложность не была хорошей идеей.

Это был C ++, который позволял чередовать объявления и переменные, но C ++ нет, и никогда не был, C. Однако C99 наконец догнал C ++ (этополезная функция).К сожалению, Microsoft никогда не внедряла C99.

4 голосов
/ 27 июня 2011

Вероятно, он никогда не был реализован таким образом, потому что он никогда не был нужен.

Предположим, вы хотите написать что-то вроде этого на простом C:

int myfunction(int value)
   {
   if (value==0)
      return 0;
   int result = value * 2;
   return result;
   }

Тогда вы можете легко переписать это в действительный C, например:

int myfunction(int value)
   {
   int result;
   if (value==0)
      return 0;
   result = value * 2;
   return result;
   }

Нет абсолютно никакого влияния на производительность, если сначала объявить переменную, а затем задать ее значение.

Однако в C ++ это уже не так. В следующем примере function2 будет медленнее, чем function1:

double function1(const Factory &factory)
   {
   if (!factory.isWorking())
      return 0;
   Product product(factory.makeProduct());
   return product.getQuantity();
   }

double function2(const Factory &factory)
   {
   Product product;
   if (!factory.isWorking())
      return 0;
   product = factory.makeProduct();
   return product.getQuantity();
   }

В функции 2 необходимо создать переменную продукта, даже если фабрика не работает. Позже фабрика производит продукт, а затем оператор присваивания должен скопировать продукт (из возвращаемого значения makeProduct в переменную продукта). В функции 1 продукт создается только во время работы фабрики, и даже тогда вызывается конструктор копирования, а не обычный конструктор и оператор присваивания.

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

Второй пример:

double function1(const Factory &factory)
   {
   if (!factory.isWorking())
      return 0;
   Product &product = factory.getProduct();
   return product.getQuantity();
   }

double function2(const Factory &factory)
   {
   Product &product;
   if (!factory.isWorking())
      return 0;
   product = factory.getProduct();    // Invalid.  You can't assign to a reference.
   return product.getQuantity();
   }

В этом примере function2 просто недействительна. Ссылкам можно присвоить значение только во время объявления, но не позже. Это означает, что в этом примере единственный способ написать правильный код - это написать объявление в тот момент, когда переменная действительно инициализирована. Не раньше.

Оба примера показывают, почему в C ++ действительно было необходимо разрешать объявления переменных после других исполняемых операторов, а не в начале блока, как в C. Это объясняет, почему это было добавлено в C ++, а не в C (и другие языки), где это действительно не нужно.

2 голосов
/ 27 июня 2011

Это похоже на требование объявления функций перед их использованием - это позволяет простому компилятору работать за один проход, сверху вниз, испуская объектный код по ходу работы.

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

0 голосов
/ 27 июня 2011

Гораздо проще написать компилятор для языка, который требует объявления всех переменных в начале функции.В некоторых языках даже требуется, чтобы переменные объявлялись в определенном предложении вне кода функции (на ум приходят Pascal и Smalltalk).

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

Любые другие операторы (особенно вызовы функций) могут изменять стек / регистры, делая отображение переменных более сложным.

...