Объявление переменных в аргументах функции в C - PullRequest
4 голосов
/ 27 февраля 2012

У меня странное желание; Я не знаю, позволяет ли это какой-либо компилятор или расширение языка.

Я хочу иметь возможность объявлять переменные внутри вызова функции, например:

int test(int *out_p) {
    *out_p = 5;
    return 1;
}

int main()
{
    if (int ret = test(int &var)) { // int var declared inside function invocation
        fprintf(stderr, "var = %d\n", var); // var in scope here
    }
    return 0;
}

потому что тогда область действия var следует за областью действия ret. Для другого примера (из проекта, над которым я сейчас работаю), у меня есть

cmd_s = readline();
int x, y, dX, dY, symA, symB;
if (sscanf(cmd_s, "placeDomino:%d %d atX:%d y:%d dX:%d dY:%d",
                           &symA, &symB, &x,  &y,   &dX,  &dY) == 6) {
    do_complicated_stuff(symA, symB, x, y, dX, dY);
} else if (sscanf(cmd_s, "placeAtX:%d y:%d dX:%d dY:%d", &x, &y, &dX, &dY) == 4) {
    do_stuff(x, y, dX, dY);
    /* symA, symB are in scope but uninitialized :-( so I can accidentally
     * use their values and the compiler will let me */
}

и я бы предпочел написать

cmd_s = readline();
if (sscanf(cmd_s, "placeDomino:%d %d atX:%d y:%d dX:%d dY:%d",
                    int &symA, int &symB, int &x, int &y, int &dX, int &dY) == 6) {
    do_complicated_stuff(symA, symB, x, y, dX, dY);
} else if (sscanf(cmd_s, "placeAtX:%d y:%d dX:%d dY:%d", int &x, int &y, int &dX, int &dY) == 4) {
    do_stuff(x, y, dX, dY);
    /* Now symA, symB are out of scope here and I can't
     * accidentally use their uninitialized values */
}

У меня вопрос, поддерживает ли это какой-либо компилятор? Поддерживает ли это gcc, если я правильно тереть? Есть спецификация C или C ++ (черновик), которая имеет это?

Edit: только что понял, что в моем первом примере кода мое объявление int ret также не годится в C99; Я думаю, что я избалован петлями. Я тоже хочу эту функцию; представь

while(int condition = check_condition()) {
    switch(condition) {
        ...
    }
}

или что-то в этом роде.

Ответы [ 4 ]

3 голосов
/ 27 февраля 2012

Помимо объявлений областей видимости, в C99 есть два основных способа объявления переменных, которые по определению ограничены оператором, в котором они встречаются:

  • Составные литералы имеют форму (type name){ initializers } и объявляют локальную переменную, которая находится в текущем блоке. Например, для вызова функции вы можете использовать test(&(int){ 0 }).
  • for переменные области видимости имеют только область действия самого оператора for и зависимого оператора или блока.

Ваше if выражение с локальной переменной, вы можете сделать что-то странное, как

for (bool cntrl = true; cntrl; cntrl = false)
   for (int ret = something; cntrl && test(&ret); cntrl = false) {
      // use ret inside here
   }

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

1 голос
/ 27 февраля 2012

Ни один компилятор не поддерживает это. Я не понимаю, где это имело бы смысл.

Сокращение количества строк исходного кода не обязательно приводит к более эффективной программе, это распространенное недоразумение. В 99% случаев в C не имеет смысла переписывать подобное утверждение в более компактное. Это приводит только к менее читаемому коду, в итоге вы получите тот же машинный код.

Что вы должны сделать, это:

void some_func (void) // good example
{
  ... lots of code here

  int ret;
  int var;

  ret = test(&var);
  if(ret == SOMETHING)
  {
    fprintf(stderr, "var = %d\n", var); // var in scope here
  }
}

То, что вы не должны делать, это:

void some_func (void) // bad example
{
  ... lots of code here

  {
    int ret;
    int var;

    if((ret = test(&var))
    {
      fprintf(stderr, "var = %d\n", var); // var in scope here
    }
  }
}

Хороший пример и плохой пример дадут точно такой же машинный код . Это очень важно понять!

Прежде всего, сокращение области видимости переменных в плохом примере не приведет к более эффективной программе: компилятор очень способен знать, когда переменная используется впервые, а когда она больше не используется. , В обоих примерах компилятор будет хранить переменные ret и var в регистрах ЦП или в стеке.

Также обратите внимание, что переменные, объявленные в середине функции (только C99 / C11) или в начале (C90 или C99 / C11), не имеют значения для эффективности. Объявление переменных в середине области видимости - это просто особенность стиля программирования: вы говорите читателю кода, что эта переменная начинает иметь значение с этого момента. Компилятору, в отличие от человека, читающего код, не важно, где вы написали объявление.

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

Основное различие между примерами состоит в том, что плохой содержит широко признанную плохую практику программирования, такую ​​как присвоение внутри условия (MISRA-C: 2004 13.1) и неявный тест против нуля для небулевых переменных (MISRA-C: 2004 13.2). Добавьте к этому ненужную, неясную, дополнительную локальную область.

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

0 голосов
/ 27 февраля 2012
while(int condition = check_condition()) {
    switch(condition) {
        ...
    }
}

или

if (int ret = test(int &var))

У меня вопрос, поддерживает ли это какой-либо компилятор? Поддерживает ли это gcc, если я правильно тереть? Есть спецификация C или C ++ (черновик), которая имеет это?

Это не C. Предложение для оператора if или оператора while должно быть выражением и не может быть объявлением.

Вы можете иметь только объявление с C99 для оператора итерации for в его первом предложении:

for (clause-1; expression-2; expression-3)

Предложение-1 может быть объявлением или выражением.

0 голосов
/ 27 февраля 2012

Используйте пустую область видимости, как это:

int test(int *out_p) {
    *out_p = 5;
    return 1;
}

int main()
{
    {
        int var, ret;
        if (ret = test(&var)) {
            fprintf(stderr, "var = %d\n", var); // var in scope here
        }
    }
    // var not in scope
    return 0;
}
...