40

When I was first introduced to C I was told to always declare my variables at the top of the function. Now that I have a strong grasp of the language I am focusing my efforts on coding style, particularly limiting the scope of my variables. I have read about the benefits to limiting the scope and I came across an interesting example. Apparently, C99 allows you to do this...

for (int i = 0; i < 10; i++)
{
   puts("hello");
}

I had thought that a variables scope was limited by the inner-most surrounding curly braces { }, but in the above example int i appears to be limited in scope by the curly braces of the for-loop even though it is declared outside of them.

I tried to extend the above example with fgets() to do what I thought was something similar but both of these gave me a syntax error.

fgets(char fpath[80], 80, stdin); *See Note**

fgets(char* fpath = malloc(80), 80, stdin);

So, just where exactly is it legal to declare variables in C99? Was the for-loop example an exception to the rule? Does this apply to while and do while loops as well?

*Note**: I'm not even sure this would be syntactically correct even if I could declare the char array there since fgets() is looking for pointer to char not pointer to array 80 of char. This is why I tried the malloc() version.

jefe2000
  • 406
  • 6
  • 13
SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • 2
    Regardless of whether or not it is legal, you should never call malloc like that because you are not checking whether it returns NULL or not, nor can you free the heap memory you are allocating, because fpath immediately goes out of scope. – Jeff Hammond Jan 01 '15 at 22:06
  • At least on linux malloc rarely returns null. Not saying you should not check, but it's useful to keep in mind that the fact that you got a pointer from malloc does not mean there is actually enough free memory to use. – graywolf Apr 17 '19 at 16:56
  • Nearly identical question at https://stackoverflow.com/questions/288441/variable-declaration-placement-in-c – MarcH Jul 10 '21 at 01:57

3 Answers3

44

In C99, you can declare your variables where you need them, just like C++ allows you to do that.

void somefunc(char *arg)
{
    char *ptr = "xyz";
    if (strcmp(arg, ptr) == 0)
    {
        int abc = 0;    /* Always could declare variables at a block start */

        somefunc(arg, &ptr, &abc);

        int def = another_func(abc, arg);   /* New in C99 */
        ...other code using def, presumably...
    }
}
  • You can declare a variable in the control part of a 'for' loop:

      for (int x = 0; x < 10; x++)    /* New in C99 */
    
  • You cannot declare a variable in the control part of a 'while' loop or an 'if' statement.

  • You cannot declare a variable in a function call.

  • Obviously, you can (and always could) declare variables in the block after any loop or an 'if' statement.

The C99 standard says:

6.8.5.3 The for statement

The statement

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

behaves as follows: The expression expression-2 is the controlling expression that is evaluated before each execution of the loop body. The expression expression-3 is evaluated as a void expression after each execution of the loop body. If clause-1 is a declaration, the scope of any variables it declares is the remainder of the declaration and the entire loop, including the other two expressions; it is reached in the order of execution before the first evaluation of the controlling expression. If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Thank you for the detailed comment and referencing the standard – SiegeX Feb 23 '10 at 21:51
  • §6.8.5.3 was easy to find, but I did not find in which § it is allowed to declare a variable everywhere. Any clue? – nowox May 26 '20 at 13:55
  • 1
    @nowox — C11 [§6.8.3 Compound statement](http://port70.net/~nsz/c/c11/n1570.html#6.8.2): the syntax lists `statement` and `declaration` as equal alternatives in the `block-item` which appears in a `block-item-list`. Function bodies are a compound statement, of course. – Jonathan Leffler May 26 '20 at 15:26
  • 1
    @nowox — For contrast, the C90 §6.6.2 "Compound statement or block" listed a compound statement as `{ declaration-list(opt) statement-list(opt) }`, so the declarations had to precede all the statements in a block. – Jonathan Leffler May 26 '20 at 16:09
9

The first thing I'd note is that you shouldn't confuse

for (int i = 0; i < 10; i++) {
    puts("hello");
}

and

fgets(char* fpath = malloc(80), 80, stdin);

The first is a control structure while the second is a function call. The control structure evaluates the text inside it's parens() in a very different way from how a function call does.

The second thing is... I don't understand what you're trying to say by:

the compiler will promptly give you an error if you try to use i inside the for-loop body.

The code you listed for the for loop is a very common structure in C and the variable "i" should, indeed, be available inside the for loop body. Ie, the following should work:

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

Am I misreading what you're saying?

RHSeeger
  • 16,034
  • 7
  • 51
  • 41
  • No, you weren't misreading but apparently I read some incorrect info on the SO link I posted in my question which said it wouldn't compile. Apparently, the more correct statement should have been it would fail to compile without passing -std=c99 to `gcc`. Could you provide a bit more insight on the evaluation of text within a control structure vs a function call as I think this is definitely the root of what my question was trying to get at. – SiegeX Feb 23 '10 at 21:38
  • The question you linked to posted an example of a for loop with a logical error in it, (notice the semicolon at the end of the for loop) that would fail to compile, because of that logic error. A common enough error that there's many questions about on SO. – nos May 30 '14 at 15:41
3

The bottom line with respect to your for/fgets confusion is that while "enclosing braces control scope" is the correct rule in C most of the time, there is another rule regarding scope in C99 (borrowed from C++) that says that a variable declared in the prologue of a control structure (i.e. for, while, if) is in scope in the body of the structure (and is not in scope outside the body).

Tyler McHenry
  • 74,820
  • 18
  • 121
  • 166