3

I have two files: test1.c, and test2.c, which contains the main() function.

test1.c:

#include <stdio.h>  // printf() function declaration/prototype

// function definition
void say_hello() {
  printf("\tHello, world!\n");
}

test2.c:

#include <stdio.h>  // printf() function declaration/prototype
int main() {
  printf("Inside main()\n");
  say_hello();

  return 0;
}

And this is my makefile:

a.out: test1.o test2.o
    $(CXX) -o a.out test1.o test2.o

test1.o: test1.c
    $(CXX) -c test1.c

test2.o: test2.c
    $(CXX) -c test2.c

Now it should be clear where the problem lies: The main() function in test2.c calls say_hello() without declaring it! I run the following command, to use the gcc compiler: make CXX=gcc I get this warning to the screen:

gcc -c test1.c
gcc -c test2.c
test2.c: In function ‘main’:
test2.c:16:3: warning: implicit declaration of function ‘say_hello’ [-Wimplicit-function-declaration]
   say_hello();
   ^
gcc -o a.out test1.o test2.o

Although the *.o files got compiled and linked into the executable. That's weird. Imagine my surprise when I run the a.out file, and I see that main() successfully called say_hello(), a function which is not declared inside of main's translation unit, as if there were no issue at all! I reason that since say_hello() was not previously declared in test2.c, it should not allowed to be called by main() at all. Notice that I've added comments to the #include <stdio.h>. These preprocessor directives include the function declarations/prototypes, which extend their scope into the corresponding *.c files. That is why we are able to use them. No function declaration/prototype == no scope in that translation unit, or so I thought until now.

Then I compiled the above code as C++ code: make CXX=g++

I get this error to the screen:

test2.c: In function ‘int main()’:
test2.c:16:13: error: ‘say_hello’ was not declared in this scope
   say_hello();
             ^
makefile:18: recipe for target 'test2.o' failed
make: *** [test2.o] Error 1

g++ does what it's supposed to do, and stops the compilation process. But gcc did not do this! What's going on? Is it a perk of the C programming language? Is it an issue with the compiler?

Galaxy
  • 2,363
  • 2
  • 25
  • 59
  • 5
    Pick a language. – Mad Physicist May 23 '18 at 19:40
  • 6
    *Is it a perk of the C programming language* - yes, it is. C is naive and thinks that the programmer knows what they are doing. – Eugene Sh. May 23 '18 at 19:40
  • This question pertains to both C and C++, although I usually use them interchangeably as one single language. – Galaxy May 23 '18 at 19:41
  • Why wouldn't it let you call an undeclared function? Declaration just specifies the function signature, not anything real about the function. – Mad Physicist May 23 '18 at 19:41
  • @MadPhysicist ... which is quite important. If not provided, it has to *assume* some default signature. – Eugene Sh. May 23 '18 at 19:42
  • Doesn't a declaration of a function say, "Hey, I'm defined in another translation unit, but I'll let you call me." A declaration extends scope of the function into another translation unit. – Galaxy May 23 '18 at 19:43
  • 1
    @Galaxy A declaration more or less just tells you what type something has (type of function in C and C++ is usually called a signature). It doesn't really have anything to do with multiple translation units and what not, although it's also useful for that. – Cubic May 23 '18 at 19:44
  • 1
    @Galaxy Not really. Functions linkage is external by default. Once you have function in some translation unit, it is visible for all. – Eugene Sh. May 23 '18 at 19:44
  • 2
    @Galaxy The question can’t pertain to both languages since C++ doesn’t allow this. ;-) – Konrad Rudolph May 23 '18 at 19:44
  • _"Also, in C89, functions returning int may be implicitly declared by the function call operator and function parameters of type int do not have to be declared when using old-style function definitions."_ see: http://en.cppreference.com/w/c/language/declarations – Richard Critten May 23 '18 at 19:45
  • 3
    It's a backward compatibility thing. C was born without prototypes. The programmer was supposed to know how to call each function and each undeclared function was assumed to return int (and take an unspecified number of (promoted) arguments, which is a consequence of no prototypes). – Petr Skocik May 23 '18 at 19:48
  • 1
    @PSkocik True about early C. At [$0.02/byte](https://jcmit.net/memoryprice.htm) in 1978, why use any code/functionality unless it was absolutely required? – chux - Reinstate Monica May 23 '18 at 19:51
  • 1
    "g++ does what it's supposed to do...But gcc did not" That is an incorrect assumption. They are both implementing a different language, correctly. Are you asking why C and C++ are _different_ in this particular regard? That's not what you're asking in the title to this question. – Drew Dormann May 23 '18 at 20:01

1 Answers1

5

Simple, because C allows undeclared functions to be called and C++ does not. Either way, gcc warns you and you may want to take warnings seriously.

ricco19
  • 713
  • 5
  • 16
  • But WHY does C allow undeclared functions to be called, unlike C++? – Galaxy May 23 '18 at 19:46
  • 3
    Because it *can*. C++ is a different language with some more strict rules. – Eugene Sh. May 23 '18 at 19:47
  • 1
    Or with `-Werror` (`-Werror=implicit-function-declaration`). That's even more serious. :) – Petr Skocik May 23 '18 at 19:51
  • @Galaxy Historic reasons, coming from the B language which had only one storage type — which became `int` in C. Though you needed to declare functions you used within blocks, like `main() { extrn /* sic */ printn, char, putchar; … }`, but I'm not sure if that was mandatory. – Triang3l May 26 '18 at 10:54
  • Consider two parts of making a program: compiling and linking. Why don't you try getting rid of `say_hello` in `test1.c` and see what happens? The program compiles fine, but when the linker tries to search for `say_hello` (since it's called in `test2.c`), it fails. So one way to phrase this difference between C and C++ is that C will throw a linker error and C++ will throw a compiler error. – Max Coplan Sep 09 '19 at 20:45