5

C89 (C90, ANSI-C) does not allow intermixing variables declaration with code. I wonder to what extent a variable initialization is considered "code".

Perhaps it's only valid to initialize with constant expressions?

Specifically, if I'm writing C code and I want to play safe (maximize compatibility with ANSI-C compilers), should the following be considered safe?

  void f1(void) { 
      int x = 30;
      int y = 40;
      int z;
      /* ... */
  }

  void f2(void) { 
      int x = 30, y = 40;
      int z;
      /* ... */
  }

  #define MYCONST (90)
  void f3(void) { 
      int x = 3; 
      int y = 4 + MYCONST;
      int z;
      /* ... */
  }

  void f4(void) { 
      int x = 3;
      int y = time(NULL);
      int z = 10 + x;
      /* ... */
  }
leonbloy
  • 73,180
  • 20
  • 142
  • 190
  • 1
    Isn't ANSI-C also updated to C99 already? – user3528438 Sep 25 '15 at 16:30
  • 1
    @user3528438 ANSI-C refers to the only specification that was released by the ANSI, aka C89. C90 is C89 as adopted by ISO the following year and all the subsequent updates (C99 and C11) are ISO C. – Stefano Sanfilippo Sep 25 '15 at 16:37
  • As it stands now, your code does not compile. Did you mean to put semicolons instead of commas after the first declarations in functions `f3` and `f4`? – PC Luddite Sep 25 '15 at 16:57
  • @PCLuddite Yes, of course. fixed - thanks – leonbloy Sep 25 '15 at 16:59
  • @StefanoSanfilippo: I agree about the common usage (ANSI-C for C89 only (C90 is the same)), but I'm quite sure the ANSI does adopt the ISO-standards more or less automatically, much like other national standardization organizations, e.g German DIN (German Institute of Standards). So strictly speaking user35... is right. – too honest for this site Sep 25 '15 at 18:11
  • 3
    @Olaf: user3528438 is *almost* right; ANSI has adopted ISO C11, and C99 is officially obsolete as far as both ISO and ANSI are concerned. (And yet the phrase "ANSI C" usually refers to C89/C90 -- which is why I try to avoid using that particular term.) – Keith Thompson Sep 25 '15 at 18:54
  • The answer to the question actually asked is that a variable declaration is still a variable declaration even if the initialization value is non-constant. In those versions of C which required declarations to precede any other kind of statement in a block, the requirement was syntactic rather than semantic; it simply meant that declarations had to precede any other kind of statement in a block. (IMHO there was never a good reason for this restriction, so it was eventually dropped without regrets.) – rici Sep 25 '15 at 19:06
  • @rici: As an "old" Modula-2/Pascal Programmer, I disagree - for C. There is no reason in C to have a variable definition mixed with the code. Having them at the beginning of a block only helps to find their definititions (or declarations). Just try to debug foreign code; it can already be confusing if the original developer defined variables in every block. (Admittedly, I take the human right to be inconsequent sometimes myself). There are quite some coding standards which disallow declarations mixed with statements. – too honest for this site Sep 25 '15 at 19:37
  • @olaf: there may be reasons why a particular programmer or group of programmers might want to avoid a particular construct. (`goto`, for example.) But that doesn't make a reason to *ban the construct* from the language. Intermingling declarations doesn't significantly complicate compiler design, nor does it have have a cost on code which doesn't intermingle, nor does it make optimization more difficult. – rici Sep 25 '15 at 20:07
  • @rici: There are very well language constructs to enforce better code quality and maintainablity (e.g I more and more think Guido van Rossum is right to use indentation as syntax element). However, this is nothing to discuss here. – too honest for this site Sep 25 '15 at 20:41
  • @rici: Allowing execution to branch from a location following a declaration to a location before it, without leaving the scope thereof, leads to some ugly corner cases in a language. I think the purpose of the original rule was to avoid requiring that compilers handle backward-branch targets that are between the first and last declarations in a block. – supercat May 12 '21 at 19:55

5 Answers5

4

should the following be considered safe?

All of your posted code is safe.

  1. You may have any number of variable declarations in any scope.
  2. The code to initialize the variables may use any method the language provides.

However, it is not legal to declare a variable after code that is not variable declaration.

void foo()
{
   int i = 0;
   i = 2;      // Does not declare a variable.
   int j = 10; // Not legal.
}

The above code works with gcc. However, if you use the -pedantic flag, you will see a warning message that will look something like:

soc.c:5:4: warning: ISO C90 forbids mixed declarations and code [-Wpedantic]
    int j = 10;
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    Are you confusing _declaration_ and _definition_? – too honest for this site Sep 25 '15 at 17:54
  • 1
    The beginning part of this answer is off-topic. The question doesn't ask about it. – PC Luddite Sep 25 '15 at 17:58
  • 1
    @Olaf, No, I am not confusing between declaration and definition. You will see the same warning if you replace `int j = 10;` by `extern int j;`. – R Sahu Sep 25 '15 at 18:03
  • @PCLuddite, the purpose of my example function was to demonstrate illegal code, as a contrast to the legal code from the OP's examples. – R Sahu Sep 25 '15 at 18:08
  • @RSahu I would recommend putting your last two sentences first: "The code to initialize the variables can use any method the language provides. All your examples are fine." *Then* you can go into what isn't legal for contrast. As-is, the first impression upon starting to read the answer is "that's not even what the question is about? did this answer even understand the question?" – mtraceur Feb 23 '21 at 07:41
3

C89 does not allows mixing declarations and statements in the same scope. We are referring here to declarations and statements inside functions as statements are not allowed outside functions in C.

int a = foo(a, b);  /* declared at block-scope */

The line above is a (valid C89) declaration, not a statement so all your functions f1, f2, f3, and f4 are valid C89.

However you are allowed to mix declarations and statements in different scopes:

void bar(void)
{
    int i = 0;  /* declaration */
    i++;  /* statement */
    {
        int j = 0;  /* declaration */
        j++;  /* statement */
     }
 }

The function above is valid C89.

ouah
  • 142,963
  • 15
  • 272
  • 331
3

The Foreword to the C99 standard itself mentions "mixed declarations and code" as one of the changes from C90. This was IMHO a poor choice of words, since it's not at all clear what "code" means. It can easily refer to everything that can appear in a C source file.

The actual change that was made by C99 was to permit mixing of declarations and statements within a block. The distinction between a declaration and a statement is unambiguously defined by the language syntax.

The line from one of your examples:

int y = time(NULL);

is a declaration, not a statement, even though it results in the execution of some code at run time. The presence or absence of an initializer, and whether that initializer is a constant expression or not, does not affect whether something is considered to be a declaration.

All four of the examples in your question are valid in C89/C90, in C99, and in C11. In each case, the block contains just declarations, no statements.

If you want to mix declarations and statements in C90, you do so by introducing nested blocks:

void func(void) {
    int x = 1; /* a declaration */
    x = 2;     /* a statement; no declarations may follow in C90 */
    {
        int y = 3; /* a declaration */
        y = 4;     /* a statement */
    }
}

The inner block it itself a statement. Because it's a statement, it can appear in that context. Because it's a compound statement, it can itself contain a sequence of declarations followed by a sequence of statements.

Even in C99 or C11, it can be advantageous to introduce nested blocks like this. The scope and lifetime of y end at the closing } of the block that contains its declaration. Restricting the scope of a declaration can make code easier to understand.

(A bit of background: C89 was the standard released by ANSI in 1989. ISO adopted it, with some changes in the document but not in the language it describes, as C90. ISO published an updated standard, C99, which was then adopted by ISO. ISO published another updated standard, C11, which was also adopted by ANSI. According to both ANSI and ISO, the 2011 standard is the current one, and all earlier editions are obsolete. But for historical reasons, the phrase "ANSI C" usually refers to the language described by the 1989 and 1990 editions. I usually try to avoid the phrase "ANSI C", referring instead to "ISO C", plus the publication year if it's relevant.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
1

All of your examples are legal and can safely be used with conforming ANSI-C compilers.

Compiling your code in gcc with -std=c89 and -pedantic generates no warnings.

PC Luddite
  • 5,883
  • 6
  • 23
  • 39
-2

F1, f2, f3 are valid but f4 is not since the declaration and definition are mixed

  • This doesn't make sense. All of his functions had combined declaration and definition. Why is `f4` special? – PC Luddite Sep 25 '15 at 17:58