Объявление переменных в аргументах функции в C

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

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

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++ (черновик), которая имеет это?

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

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

или что-то типа того.

4 ответа

Помимо объявлений области видимости блока, в 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 Блок оценивается только один раз.

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

Уменьшение количества строк исходного кода не обязательно приводит к более эффективной программе, это распространенное недоразумение. В 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 переводится в машинный код. Когда вы это знаете, вы не почувствуете необходимости бесполезно запутывать код ложным предположением, что вы его улучшаете.

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 может быть объявлением или выражением.

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

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;
}
Другие вопросы по тегам