Где вы можете и не можете объявить новые переменные в C? - PullRequest
72 голосов
/ 12 декабря 2011

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

Но потом я читал K & R иЯ наткнулся на это предложение: «Объявления переменных (включая инициализации) могут следовать за левой скобкой, которая вводит любое составное утверждение, а не только то, которое начинает функцию».Далее следует пример:

if (n > 0){
    int i;
    for (i=0;i<n;i++)
    ...
}

Я немного поиграл с концепцией, и она работает даже с массивами.Например:

int main(){
    int x = 0 ;

    while (x<10){
        if (x>5){
            int y[x];
            y[0] = 10;
            printf("%d %d\n",y[0],y[4]);
        }
        x++;
    }
}

Так когда именно мне не разрешено объявлять переменные?Например, что если мое объявление переменной не сразу после открывающей скобки?Как здесь:

int main(){
    int x = 10;

    x++;
    printf("%d\n",x);

    int z = 6;
    printf("%d\n",z);
}

Может ли это вызвать проблемы в зависимости от программы / машины?

Ответы [ 6 ]

110 голосов
/ 12 декабря 2011

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

В то время как все версии C допускают лексический блокобласть действия, в которой вы можете объявить переменные, зависит от версии стандарта C, на которую вы нацелены:

C99 или C ++

Современные компиляторы C, такие как gcc и clang, поддерживают Стандарты C99 и C11 , которые позволяют объявлять переменную в любом месте, куда может идти оператор.Область действия переменной начинается с точки объявления до конца блока (следующая закрывающая скобка).

if( x < 10 ){
   printf("%d", 17);  // z is not in scope in this line
   int z = 42;
   printf("%d", z);   // z is in scope in this line
}

Вы также можете объявить переменные внутри для инициализаторов цикла.Переменная будет существовать только внутри цикла.

for(int i=0; i<10; i++){
    printf("%d", i);
}

ANSI C (C90)

Если вы ориентируетесь на более старый стандарт ANSI C , то вы ограниченыобъявлять переменные сразу после открывающей фигурной скобки 1 .

Это не означает, что вы должны объявлять все свои переменные в верхней части ваших функций.В C вы можете поместить блок с разделителями в фигурные скобки везде, где может идти оператор (не только после таких вещей, как if или for), и вы можете использовать это для введения новых областей видимости переменных.Ниже приведена версия ANSI C предыдущих примеров C99:

if( x < 10 ){
   printf("%d", 17);  // z is not in scope in this line

   {
       int z = 42;
       printf("%d", z);   // z is in scope in this line
   }
}

{int i; for(i=0; i<10; i++){
    printf("%d", i);
}}

1 Обратите внимание, что если вы используете gcc, вам нужно передать флаг --pedantic, чтобы сделатьон фактически соблюдает стандарт C90 и жалуется, что переменные объявлены не в том месте.Если вы просто используете -std=c90, это заставит gcc принять расширенный набор C90, который также позволяет более гибкие объявления переменных C99.

3 голосов
/ 12 декабря 2011

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

Возьмем следующий код в качестве примера.

#include <stdio.h>

int main() {
    int i, j;
    i = 20;
    j = 30;

    printf("(1) i: %d, j: %d\n", i, j);

    {
        int i;
        i = 88;
        j = 99;
        printf("(2) i: %d, j: %d\n", i, j);
    }

    printf("(3) i: %d, j: %d\n", i, j);

    return 0;
}

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

(1) i: 20, j: 30
(2) i: 88, j: 99
(3) i: 20, j: 99

Сначала мы назначим 20 и 30 для i и j соответственно.Затем внутри фигурных скобок мы присваиваем 88 и 99. Итак, почему тогда j сохраняет свое значение, но i снова возвращается к 20?Это из-за двух разных переменных i.

Между внутренним набором фигурных скобок переменная i со значением 20 скрыта и недоступна, но так как мы не объявили новый j,мы все еще используем j из внешней области видимости.Когда мы оставляем внутренний набор фигурных скобок, i, содержащий значение 88, исчезает, и у нас снова появляется доступ к i со значением 20.

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

1 голос
/ 21 февраля 2015

Сообщение показывает следующий код:

//C99
printf("%d", 17);
int z=42;
printf("%d", z);

//ANSI C
printf("%d", 17);
{
    int z=42;
    printf("%d", z);
}

и я думаю, что это означает, что они эквивалентны. Они не. Если int z помещается внизу этого фрагмента кода, это вызывает ошибку переопределения первого определения z, но не второго.

Однако несколько строк:

//C99
for(int i=0; i<10; i++){}

работает. Показаны тонкость этого правила C99.

Лично я страстно избегаю этой функции C99.

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

1 голос
/ 12 декабря 2011

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

0 голосов
/ 22 октября 2018

С clang и gcc я столкнулся с серьезными проблемами со следующим.gcc версия 8.2.1 20181011 clang версия 6.0.1

  {
    char f1[]="This_is_part1 This_is_part2";
    char f2[64]; char f3[64];
    sscanf(f1,"%s %s",f2,f3);      //split part1 to f2, part2 to f3 
  }

ни одному из компиляторов не понравилось, что f1, f2 или f3 находятся внутри блока.Мне пришлось переместить f1, f2, f3 в область определения функции.компилятор не возражал против определения целого числа с блоком.

0 голосов
/ 20 сентября 2017

В соответствии с языком программирования C By K & R -

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

Здесь вы можете увидеть слово, обычно это не обязательно ..

...