简体   繁体   中英

Declaring variables in the arguments to a function in C

I have a sort of bizarre wish; I don't know if any compiler or language extension out there allows this.

I want to be able to declare variables inside a function invocation, like this:

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;
}

because then the scoping of var follows the scoping of ret. For another example (from a project I'm working on now), I have

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 */
}

and I would prefer to write

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 */
}

My question is, does any compiler support this? Does gcc support it if I rub it the right way? Is there a C or C++ (draft) spec that has this?

Edit: just realized that in my first code example, my declaration of int ret is also no good in C99; I guess I'm spoiled by for loops. I want that feature too; imagine

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

or something like that.

Besides block scope declarations, in C99 there are basically two other ways to declare variables that are by definition restricted to the statement in which they occur:

  • Compound literals are of the form (type name){ initializers } and declare a local variable that lives in the current block. Eg for a function call you could use test(&(int){ 0 }) .
  • for scope variables only have the scope of the for statement itself and the depending statement or block.

Your if expression with local variable you could do something weird like

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

Be careful, such things quickly become unreadable. On the other hand, optimizers reduce such code quite efficiently to the essential and easily find out that test and the inner side of the for block are only evaluated once.

No compiler supports this. I fail to see where this would make sense.

To reduce the number of source code lines does not necessarily lead to a more efficient program, this is a common misunderstanding. In 99% of the cases in C, it does not make sense to rewrite a statement like this to a more compact one. It only leads to less readable code, you would get the very same machine code in the end.

What you should do is this:

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
  }
}

What you should not do is this:

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
    }
  }
}

The good example and the bad example will yield exactly the same machine code . This is very important to understand!

First of all, the reduced scope of the variables in the bad example will not lead to a more effective program: the compiler is very capable of knowing when a variable is used for the first time and when it is not used any longer. In both examples the compiler will store the variables ret and var in CPU registers or on the stack.

Also note that whether the variables are declared in the middle of the function (C99/C11 only) or at the beginning (C90 or C99/C11) doesn't matter what-so-ever for efficiency. Declaring variables in the middle of a scope is just a programming style feature: you are telling the reader of the code that this variable starts to matter from this point on. The compiler, as opposed to the human reading the code, doesn't care where you wrote the declaration.

Had we omitted ret and just checked the result of test(), that wouldn't have made any difference either - the result of the function must still be saved somewhere , either in a variable explicitly declared by the programmer or in a temporary variable implicitly created by the compiler. The machine code will be the same, just harder to debug if there is no ret variable.

The main difference between the examples is that the bad one contains widely-recognized bad programming practice, such as assignment inside condition (MISRA-C:2004 13.1) and implicit test against zero for non-boolean variables (MISRA-C:2004 13.2). Add the needless, obscure, additional local scope to that.

So my advice is to study some assembler (or disassembly) and learn how C code is actually translated to machine code. When you know that, you won't feel the need to needlessly obfuscate code with the false assumption that you are improving it.

Use an empty scope, like this:

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;
}
while(int condition = check_condition()) {
    switch(condition) {
        ...
    }
}

or

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

My question is, does any compiler support this? Does gcc support it if I rub it the right way? Is there a C or C++ (draft) spec that has this?

This is not C. The clause for an if statement or a while statement has to be an expression and cannot be a declaration.

You can only have a declaration since C99 for the for iteration statement in its first clause:

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

The clause-1 can be a declaration or an expression.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM