Thu, 13 Sep 2007
GNU gcc and -Wmissing-prototypes.
Many people who code in C consider warning messages optional or if they do enable warnings, use gcc's -Wall warning flag and leave it at that. However, there are a number of problems that gcc can warn about but doesn't unless it is specifically told to do so.
For example, consider a rather trivial example consisting of a main program file (main.c) like this:
#include <stdio.h>
#include "other.h"
int
main (void)
{
printf ("two cubed : %f\n", int_power (2.0, 3)) ;
return 0 ;
}
a second C file (other.c) like this:
double
int_power (int pow, double value)
{
double output = value ;
for ( ; pow > 1 ; pow --)
output *= value ;
return output ;
}
and the header file for the above C file (other.h) like this:
double int_power (double value, int pow) ;
Simple.
Compiling this code at the command line can be done like this:
gcc -Wall -Wextra main.c other.c -o program
which gives no warnings. However, when the resulting executable is run, it gives an obviously wrong result:
two cubed : 0.000000
What the ..... ?
Looking at the code to this rather trivial example, its pretty easy to figure out that the error is caused by the main program and the implementation of the function int_power disagreeing on the order of the two parameters.
In a more complicated real world situation, this can lead to seriously difficult to debug problems. The solution of course is to add the -Wmissing-prototype flag to the gcc command line:
gcc -Wall -Wextra -Wmissing-prototypes main.c other.c -o program
Now the compiler gives us a warning message:
other.c:3: warning: no previous prototype for 'int_power'
To get rid of this warning, the file other.c should include other.h. When we do that, we get a compile error telling us that there is a conflict between the function implementation in other.c and the function prototype in other.h:
other.c:6: error: conflicting types for 'int_power' other.h:1: error: previous declaration of 'int_power' was here
The fix of course is to make the implementation of int_power in other.c match the function prototype. Once that is done, the program compiles and even gives the correct result.
But we're not quite done yet. The behavior of the original broken code is slightly different when compiled with a C++ compiler. Compiling with g++:
g++ -Wall -Wextra main.c other.c -o program
results in an error message:
/tmp/cccTLc2H.o: In function `main': main.c:(.text+0x23): undefined reference to `int_power(double, int)' collect2: ld returned 1 exit status
So how does the C++ compiler know that something is wrong here when the C compiler didn't?
The most important thing to notice is that the error is produced by the linker. Secondly, one needs to remember that C++ (unlike C) allows function name overloading; that is, two (or more) functions can have the same name as long as they all have a unique (ordered) set of function argument types.
In the case above, the C++ linker (which may be the same as the C linker but behaves differently when linking C++ object files) knows the function called from main.c takes two parameters, a double followed by an int. However, the file other.c has a function of the same name, but with the order of the parameters reversed and hence can't be used. Since there is no other function of that name the linker gives an error.
Interestingly, the C++ compiler does not accept the -Wmissing-prototypes warning flag. Personally, I think it should, because obvious warnings from the parser stage of the compiler are an order of magnitude better than obscure error messages from the linker.
Finally, some C++ fan-boys might give this as an example of why C++ is a safer language than C. The question I would ask of those people is, "if you are so concerned with programming safety, why are you using C++ instead of Ocaml or Haskell?". I would also suggest that using a good C compiler like GNU gcc with every warning message you can find turned on is just as safe as running the same code through a C++ compiler.
Posted at: 07:03 | Category: CodeHacking | Permalink