Объявление переменных в аргументах функции в 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;
}