Best c99 questions in October 2011

Bizarre use of conditional operator in Linux

31 votes

In the 3.0.4 Linux kernel, mm/filemap.c has this line of code:

retval = retval ?: desc.error;

I've tried compiling a similar minimal test case with gcc -Wall and don't get any warnings; the behavior seems identical to:

retval = retval ? retval : desc.error;

Looking at the C99 standard, I can't figure out what formally describes this behavior. Why is this OK?

As several others have said, this is a GCC extension, not part of any standard. You'll get a warning for it if you use the -pedantic switch.

The point of this extension is not really visible in this case, but imagine if instead it was

retval = foo() ?: desc.error;

With the extension, foo() is called only once. Without it, you have to introduce a temporary variable to avoid calling foo() twice.

Why was mixing declarations and code forbidden up until C99?

22 votes

I have recently become a teaching assistant for a university course which primarily teaches C. The course arbitrarily standardized on C90, probably due to widespread compiler support. One of the very confusing concepts to C newbies with previous Java experience is the rule that variable declarations and code may not be intermingled within a block (compound statement).

This limitation was finally lifted with C99, but I wonder: does anybody know why it was there in the first place? Does it simplify variable scope analysis? Does it allow the programmer to specify at which points of program execution the stack should grow for new variables?

I assume the language designers wouldn't have added such a limitation if it had absolutely no purpose at all.

In the very beginning of C the available memory and CPU resources were really scarce. So it had to compile really fast with minimal memory requirements.

Therefore the C language has been designed to require only a very simple compiler which compiles fast. This in turn lead to "single pass compiler" concept: The compiler reads the source-file and translates everything into assembler code as soon as possible - usually while reading the source file. For example: When the compiler reads the definition of a global variable the appropriate code is emitted immediately.

This trait is visible in C up until today:

  • C requires "forward declarations" of all and everything. A multi pass compiler could look forward and deduce the declarations of variables of functions in the same file by itself.
  • This in turn makes the *.h files necessary.
  • When compiling a function, the layout of the stack frame must be computed as soon as possible - otherwise the compiler had to do several passes over the function body.

Nowadays no serious C compiler is still "single pass", because many important optimizations cannot be done within one pass. A little bit more can be found in Wikipedia.

The standard body lingered for quite some time to relax that "single pass" point in regard to the function body. I assume, that other things were more important.