Вопрос

Я долго думал, что в C все переменные нужно объявлять в начале функции.Я знаю, что в C99 правила такие же, как и в C++, но каковы правила размещения объявлений переменных в C89/ANSI C?

Следующий код успешно компилируется с помощью gcc -std=c89 и gcc -ansi:

#include <stdio.h>
int main() {
    int i;
    for (i = 0; i < 10; i++) {
        char c = (i % 95) + 32;
        printf("%i: %c\n", i, c);
        char *s;
        s = "some string";
        puts(s);
    }
    return 0;
}

Не должны ли декларации c и s вызвать ошибку в режиме C89/ANSI?

Это было полезно?

Решение

Он успешно компилируется, потому что GCC допускает его как расширение GNU, даже если он не является частью стандарта C89 или ANSI. Если вы хотите строго придерживаться этих стандартов, вы должны передать флаг -pedantic .

Другие советы

Для C89 вы должны объявить все свои переменные в начале блока области .

Итак, ваше объявление char c действительно, так как оно находится в верхней части блока области действия цикла for. Но объявление char * s должно быть ошибкой.

Группирование объявлений переменных в верхней части блока является наследием, вероятно, из-за ограничений старых, примитивных компиляторов C.Все современные языки рекомендуют, а иногда даже требуют объявления локальных переменных на самой последней стадии:где они впервые инициализируются.Потому что это избавляет от риска использования случайного значения по ошибке.Разделение объявления и инициализации также не позволяет вам использовать «const» (или «final»), когда это возможно.

C++, к сожалению, продолжает принимать старый способ объявления верхнего уровня для обратной совместимости с C (одна совместимость с C вытесняет многие другие...). Но C++ пытается отойти от него:

  • Конструкция ссылок C++ не допускает даже такой верхней группировки блоков.
  • Если вы разделите объявление и инициализацию локального C++ объект тогда вы платите стоимость дополнительного конструктора даром.Если конструктор без аргументов не существует, то вам даже не разрешается разделить оба!

C99 начинает перемещать C в том же направлении.

Если вы беспокоитесь о том, что не нашли, где объявлены локальные переменные, это означает, что у вас гораздо более серьезная проблема:окружающий блок слишком длинный и его следует разделить.

https://www.securecoding.cert.org/confluence/display/cplusplus/DCL19-CPP.+Initialize+automatic+local+variables+on+declaration

С точки зрения ремонтопригодности, а не синтаксической точки зрения, существует по крайней мере три направления мысли:

<Ол>
  • Объявите все переменные в начале функции, чтобы они были в одном месте, и вы сможете сразу увидеть полный список.

  • Объявите все переменные как можно ближе к месту их первого использования, чтобы вы знали, почему нужны все.

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

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

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

    Как отмечали другие, GCC разрешает в этом отношении (и, возможно, другие компиляторы, в зависимости от аргументов, с которыми они вызваны) даже в режиме «C89», если только вы не используете «педантичную» проверку. Если честно, не так много веских причин, чтобы не заниматься педантичностью; Качественный современный код должен всегда компилироваться без предупреждений (или очень немногие, если вы знаете, что делаете что-то конкретное, что является подозрительным для компилятора как возможная ошибка), поэтому, если вы не можете заставить свой код компилироваться с педантичной установкой, это, вероятно, требует некоторого внимания.

    C89 требует, чтобы переменные объявлялись перед любыми другими операторами в каждой области, более поздние стандарты разрешают объявление ближе к использованию (что может быть как более интуитивным, так и более эффективным), особенно одновременное объявление и инициализацию переменной управления цикла в 'for 'петли.

    Как уже отмечалось, есть две школы мысли об этом.

    1) Объявите все наверху функций, потому что это 1987 год.

    2) Объявляйте наиболее близким к первому использованию и в наименьшей возможной области.

    Мой ответ на этот вопрос - ОБА! Позвольте мне объяснить:

    Для длинных функций 1) делает рефакторинг очень сложным. Если вы работаете в кодовой базе, в которой разработчики против идеи подпрограмм, то в начале функции у вас будет 50 объявлений переменных, и некоторые из них могут быть просто " i " для цикла for, который находится в самом низу функции.

    Поэтому я разработал ПТСР «наверху» и попытался сделать вариант 2) неукоснительно.

    Я вернулся к первому варианту из-за одной вещи: короткие функции. Если ваши функции достаточно короткие, у вас будет мало локальных переменных, а поскольку функция короткая, если вы поместите их в верхнюю часть функции, они все равно будут близки к первому использованию.

    Кроме того, анти-шаблон " объявить и установить в NULL " когда вы хотите объявить сверху, но вы не сделали некоторые вычисления, необходимые для инициализации, это решается, потому что вещи, которые вам нужно инициализировать, скорее всего будут получены в качестве аргументов.

    Так что теперь я думаю, что вы должны объявить в верхней части функций и как можно ближе к первому использованию. Так ОБА! И способ сделать это с помощью хорошо разделенных подпрограмм.

    Но если вы работаете с длинной функцией, то поместите вещи ближе всего к первому использованию, потому что таким образом будет легче извлечь методы.

    Мой рецепт такой. Для всех локальных переменных возьмите переменную и переместите ее декларацию вниз, скомпилируйте, затем переместите декларацию непосредственно перед ошибкой компиляции. Это первое использование. Сделайте это для всех локальных переменных.

    int foo = 0;
    <code that uses foo>
    
    int bar = 1;
    <code that uses bar>
    
    <code that uses foo>
    

    Теперь определите блок области действия, который начинается перед объявлением, и перемещайте конец до тех пор, пока программа не скомпилируется

    {
        int foo = 0;
        <code that uses foo>
    }
    
    int bar = 1;
    <code that uses bar>
    
    >>> First compilation error here
    <code that uses foo>
    

    Это не компилируется, потому что есть еще немного кода, который использует foo. Мы можем заметить, что компилятор смог пройти через код, который использует bar, потому что он не использует foo. На данный момент, есть два варианта. Механический - просто переместить "} " вниз до тех пор, пока он не скомпилируется, и другой выбор - проверить код и определить, можно ли изменить порядок на:

    {
        int foo = 0;
        <code that uses foo>
    }
    
    <code that uses foo>
    
    int bar = 1;
    <code that uses bar>
    

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

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

    int i;
    
    for(i = 0; i < 8; ++i){
        ...
    }
    
    <some stuff>
    
    for(i = 3; i < 32; ++i){
        ...
    }
    

    Эти ситуации требуют больше, чем моя процедура. Разработчик должен будет проанализировать код, чтобы определить, что делать.

    Но первый шаг - это найти первое применение. Вы можете сделать это визуально, но иногда проще просто удалить объявление, попытаться скомпилировать и просто вернуть его выше первого использования. Если это первое использование внутри оператора if, поместите его туда и проверьте, компилируется ли оно. Затем компилятор определит другие варианты использования. Попробуйте создать блок области действия, который охватывает оба варианта использования.

    После выполнения этой механической части становится легче анализировать, где находятся данные. Если переменная используется в большом блоке контекста, проанализируйте ситуацию и посмотрите, используете ли вы одну и ту же переменную для двух разных вещей (например, «i», которое используется для двух для циклов for). Если использование не связано, создайте новые переменные для каждого из этих не связанных применений.

    Я приведу некоторые утверждения из руководства для gcc версии 4.7.0 для ясного объяснения.

    " Компилятор может принимать несколько базовых стандартов, таких как & # 8216; c90 & # 8217; или & # 8216; c ++ 98 & # 8217; и GNU-диалекты этих стандартов, такие как & # 8216; gnu90 & # 8217; или > gnu ++ 98 & # 8217 ;. Указывая базовый стандарт, компилятор будет принимать все программы, соответствующие этому стандарту, и программы, использующие расширения GNU, которые не противоречат ему. Например, & # 8216; -std = c90 & # 8217; отключает некоторые функции GCC, которые несовместимы с ISO C90, такие как ключевые слова asm и typeof, но не другие расширения GNU, которые не имеют значения в ISO C90, такие как пропуск среднего термина выражения?:. "

    Я думаю, что ключевой момент вашего вопроса в том, почему gcc не соответствует C89, даже если опция " -std = c89 " используется. Я не знаю версию вашего gcc, но думаю, что большой разницы не будет. Разработчик gcc сказал нам, что опция " -std = c89 " просто означает, что расширения, которые противоречат C89, отключены. Таким образом, это не имеет ничего общего с некоторыми расширениями, которые не имеют смысла в C89. И расширение, которое не ограничивает размещение объявления переменных, относится к расширениям, которые не противоречат C89.

    Честно говоря, каждый будет думать, что он должен полностью соответствовать C89 с первого взгляда на параметр " -std = c89 " ;. Но это не так. Что касается проблемы, которая объявляет все переменные в начале лучше или хуже, это просто вопрос привычки.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top