Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C89, Mixing Variable Declarations and Code

I'm very curious to know why exactly C89 compilers will dump on you when you try to mix variable declarations and code, like this for example:

rutski@imac:~$ cat test.c
#include <stdio.h>

int
main(void)
{
    printf("Hello World!\n");
    int x = 7;
    printf("%d!\n", x);
    return 0;
}
rutski@imac:~$ gcc -std=c89 -pedantic test.c
test.c: In function ‘main’:
test.c:7: warning: ISO C90 forbids mixed declarations and code
rutski@imac:~$ 

Yes, you can avoid this sort of thing by staying away from -pedantic. But then your code is no longer standards compliant. And as anybody capable of answering this post probably already knows, this is not just a theoretical concern. Platforms like Microsoft's C compiler enforce this quick in the standard under any and all circumstances.

Given how ancient C is, I would imagine that this feature is due to some historical issue dating back to the extraordinary hardware limitations of the 70's, but I don't know the details. Or am I totally wrong there?

like image 356
fieldtensor Avatar asked Jun 27 '11 04:06

fieldtensor


Video Answer


2 Answers

The C standard said "thou shalt not", because it was not allowed in the earlier C compilers which the C89 standard standardized. It was a radical enough step to create a language that could be used for writing an operating system and its utilities. The concept probably simply wasn't considered - no other language at the time allowed it (Pascal, Algol, PL/1, Fortran, COBOL), so C didn't need to either. And it probably makes the compiler slightly harder to handle (bigger), and the original compilers were space-constrained by the 64 KiB code and 64 KiB data space allowed in the PDP 11 series (the big machines; the littler ones only allowed 64 KiB for both code and data, AFAIK). So, extra complexity was not a good idea.

It was C++ that allowed declarations and variables to be interleaved, but C++ is not, and never has been, C. However, C99 finally caught up with C++ (it is a useful feature). Sadly, though, Microsoft never implemented C99.

like image 88
Jonathan Leffler Avatar answered Sep 21 '22 10:09

Jonathan Leffler


It was probably never implemented that way, because it was never needed.

Suppose you want to write something like this in plain C:

int myfunction(int value)
   {
   if (value==0)
      return 0;
   int result = value * 2;
   return result;
   }

Then you can easily rewrite this in valid C, like this:

int myfunction(int value)
   {
   int result;
   if (value==0)
      return 0;
   result = value * 2;
   return result;
   }

There is absolutely no performance impact by first declaring the variable, then setting its value.

However, in C++, this is not the case anymore. In the following example, function2 will be slower than function1:

double function1(const Factory &factory)
   {
   if (!factory.isWorking())
      return 0;
   Product product(factory.makeProduct());
   return product.getQuantity();
   }

double function2(const Factory &factory)
   {
   Product product;
   if (!factory.isWorking())
      return 0;
   product = factory.makeProduct();
   return product.getQuantity();
   }

In function2 the product variable needs to be constructed, even when the factory is not working. Later, the factory makes the product and then the assignment operator needs to copy the product (from the return value of makeProduct to the product variable). In function1, product is only constructed when the factory is working, and even then, the copy constructor is called, not the normal constructor and assignment operator.

However, I would expect nowadays that a good C++ compiler would optimize this code, but in the first C++ compilers this probably wasn't the case.

A second example is the following:

double function1(const Factory &factory)
   {
   if (!factory.isWorking())
      return 0;
   Product &product = factory.getProduct();
   return product.getQuantity();
   }

double function2(const Factory &factory)
   {
   Product &product;
   if (!factory.isWorking())
      return 0;
   product = factory.getProduct();    // Invalid.  You can't assign to a reference.
   return product.getQuantity();
   }

In this example, function2 is simply invalid. References can only be assigned a value at declaration time, not later. This means that in this example, the only way to write valid code is to write the declaration at the moment where the variable is really initialized. Not sooner.

Both examples show why it was really needed in C++ to allow variable declarations after other executable statements, and not in the beginning of the block like in C. This explains why this was added to C++, and not to C (and other languages) where it isn't really needed.

like image 41
Patrick Avatar answered Sep 20 '22 10:09

Patrick