10

Preamble: My C may be fairly rusty; I first started writing C programs in somewhere around 1993 -- compilers may have been different back then, but I recall that when one attempted to refer to a C function that was not declared, the compiler would abort. This is from memory.

Currently, I am perplexed as to why GCC (4.4.3) is so forgiving on me when I [intentionally] mismatch or omit declaration of bar below, with its definition in bar.c. Because the compiler does not warn me, the program proceeds to a fatal addressing error at runtime -- since bar wants an address and is given an integer, it ends up de-referencing that integer as an address.

A strict compiler, or so I would think, would abort on me with an error. Am I missing something? My build command line is as follows:

cc -o foobar -g -Wall -std=c99 -fexec-charset=ISO-8859-1 -DDEBUG foo.c bar.c

With foo.c:

int main() {
    int a;
    bar(a);
    return 0;
}

and bar.c:

void bar(int * a) {
    *a = 1;
}

I have intentionally omitted declaration of bar and, as mentioned, intentionally pass it an integer (could be anything really) instead of an address that its actual definition would otherwise mandate. Because $(CC) does not stop me, I end up with a segmentation fault (x86, Ubuntu 10.04). I am aware that a compliant C (C99?) compiler would implicitly create an int bar(void) declaration for bar if none otherwise found, but in this case that's obviously not what I want at all!

I want to protect myself from the kind of errors -- where I make the human mistake of mismatching declarations and definitions or omitting the former altogether.

I tried to instead just invoke the compiler without the linking step (with the -c switch) -- but it doesn't matter as compiling still succeeds with warnings. The linker might complain though, but I want the compiler to stop me before that happens.

I do not actually want to turn all my warnings into errors (e.g. with -Werror), because:

  • I could have included the wrong float bar(double a); at the top of foo.c, which would eliminate the warning altogether, but doesn't change the fact that the resulting program crashes; alas, a program that compiles successfully without warnings (even with the -Wall switch) but still is faulty
  • I have and will have other types of warnings that should stay warnings and not prevent successfully building the program
  • It would be dealing with the effect of the problem, rather than the problem itself
  • It's not just the types of warnings, but also particular instances thereof; I wouldn't want to turn a specific warning into an error because in some instances that would not be applicable; this would be too "coarse" of a solution which doesn't take into account the specifics of and the context in which the warning occurred
Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
  • 1
    Doesn't answer the question, but using a C++ compiler (implicit declarations are simply forbidden) would catch all that. (Possibly only at link time for the mismatched declaration/definition, but still caught.) – Mat Oct 11 '11 at 11:01
  • @Mat Yes, I am aware of that. Thanks for the heads up. Can't use C++ here. I did solve the problem though, will wait a bit for alternatives and then post my own answer. – Armen Michaeli Oct 11 '11 at 11:51

6 Answers6

7

To turn this warning into an error when compiling with gcc, pass the switch -Werror=implicit-function-declaration to the compiler.

Trying to answer your "why" question: yes, it might look odd that this is by default a warning and not an error. This is for historical reasons. For details, see e.g. Why does/did C allow implicit function and typeless variable declarations?, or read it in Ritchie's own words at http://cm.bell-labs.com/who/dmr/chist.html.

Community
  • 1
  • 1
Carl
  • 937
  • 10
  • 21
  • 1
    Thank you @Carl. Unfortunately, it has been long time since I stumbled on the particular use case. I hope to be able to revisit it in the future, since it is at such a fundamental level. This said, I no longer think `-combine` is a good solution, in fact I think it does far more harm than good to avert the problem. The underlying problem is of compiler aiding the programmer properly, avoiding situations where the programmer has to support the compiler instead. – Armen Michaeli Mar 14 '15 at 11:42
4

You could probably force additional warnings for gcc:

-Wmissing-prototypes
-Wmissing-declarations

Using both (along with -Werror) will probably help you to avoid such situations, but require some more code writing.

Inspired by this.

EDIT: Example

// file: mis1.c
int main(void)
{
    int a;

    bar(a);

    return 0;
}

// file: mis2.c
#include <stdio.h>

double bar(double a)
{
    printf("%g\n", a);
    return a;
}

Compiling with gcc 3.3.4 (DJGPP) as:

gcc -Wall -Wmissing-prototypes -Wmissing-declarations -Werror mis2.c mis1.c -o mis.exe

Compiler output:

mis2.c:5: warning: no previous prototype for `bar'
mis1.c: In function `main':
mis1.c:6: warning: implicit declaration of function `bar'

Fix? #Include the following file in both files:

// file: mis.h
extern int bar(int);

Recompiling you get:

mis2.c:6: error: conflicting types for `bar'
mis.h:3: error: previous declaration of `bar'

Fix? Define and declare bar everywhere in the same way, correct, for example, mis.h:

// file: mis.h
extern double bar(double);

Likewise you could change bar() in mis2.c to match that of mis.h.

Community
  • 1
  • 1
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • 1
    Thank you - very helpful information, especially if one considers that one may use `-Werror=missing-prototypes` and `-Werror=missing-declarations` and get something that looks like what I am after, except one very important fact: the situation where I'd have a bogus say, `double bar(double a);` in `foo.c` is completely undetected by your proposal and `bar` crashes program again. – Armen Michaeli Oct 11 '11 at 12:28
  • @amn: how about the actual example that I just added? – Alexey Frunze Oct 11 '11 at 18:12
  • it is an improvement, but the original problem still remains - having a "wrong" prototype of `bar` and having a call (according to the the prototype) compiles just fine but naturally, the program crashes. This either is a gaping hole with C or I need to be corrected on exactly what am I doing/expecting wrong. I added my own answer by the way. – Armen Michaeli Oct 16 '11 at 11:45
4

From the gcc docs on warnings:

-Wimplicit-function-declaration (C and Objective-C only) Give a warning whenever a function is used before being declared. In C99 mode (-std=c99 or -std=gnu99), this warning is enabled by default and it is made into an error by -pedantic-errors. This warning is also enabled by -Wall.

...

-pedantic-errors (my emphasis) Like -pedantic, except that errors are produced rather than warnings.

...

-pedantic Issue all the warnings demanded by strict ISO C and ISO C++; reject all programs that use forbidden extensions, and some other programs that do not follow ISO C and ISO C++. For ISO C, follows the version of the ISO C standard specified by any -std option used.

It looks to me that -pedantic-errors will do what you want (turn these warnings into errors), however it sounds like it will also turn on a host of other checks you may or may not want. =/

dtyler
  • 1,187
  • 6
  • 11
1

The closest I found to a solution to my problem was to simply use the flag -combine which indirectly causes the compiler to abort compilation when attempting to call a function that is missing a prototype or where prototypes mismatch or do not match the definition.

I am afraid it has drawbacks though. Since input files now are combined in one compilation run, one ends up with a single object file, which has some implications of its own. In short, -combine does something more than just fix my problem, and that may be a problem in itself.

Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
0

You can turn all warning to error with

cc [..] -Werror [..]

. This will partially solve your problem.

Patrick B.
  • 11,773
  • 8
  • 58
  • 101
  • In addition to what I've already said about `-Werror`, it doesn't really solve my problem. I could have included a completely improper `float bar(double a);` declaration at the top of `foo.c` and no warnings would have been generated at all! The resulting program would still crash though, because actual `bar` still expects an address. – Armen Michaeli Oct 11 '11 at 10:38
0
  • I could have included the wrong float bar(double a); at the top of foo.c, which eliminates the warning altogether, but doesn't change
    the fact that the resulting program crashes. Alas, a program that
    compiles successfully without warnings (even with the -Wall switch)
    and beautifully crashes at runtime.

Herefore it is essential, additional to other measures, to include the same header file (including the prototype) in foo.c and bar.c. This ensures that the correct prototype is applied at both places.

glglgl
  • 89,107
  • 13
  • 149
  • 217
  • 1
    It doesn't address the obvious issues in the situation I am describing - namely that a human might make an error and mismatch either combination of the two - a declaration, a definition, and/or a prototype (altogether spread across several translating units), which will not pop up as even a warning during compilation, yet produces a "perfectly" faulty program. You are instead referring to a scenario where the programmer already has fixed the error themselves by providing proper declarations. C++ has acknowledged and addressed the actual problem, as @Mat said. – Armen Michaeli Oct 11 '11 at 12:21