7

In C++, it is a compiler error to call a function before it is declared. But in C, it may compile.

#include<stdio.h>
int main()
{
   foo(); // foo() is called before its declaration/definition
} 

int foo()
{
   printf("Hello");
   return 0; 
} 

I have tried and know that it is correct but I can't get the reason behind it. Can anyone please explain how the compilation process actually takes place and differs in both the languages.

ayushi grover
  • 91
  • 1
  • 5
  • @NathanOliver If you disable warnings it will compile, I am not sure whether it will do it silently. – Iharob Al Asimi Feb 12 '16 at 16:15
  • 3
    @NathanOliver I think you're compiling as C++. Anyway, IIRC, this was valid in C89, but not in newer standards. – juanchopanza Feb 12 '16 at 16:16
  • @juanchopanza Yep you are correct. – NathanOliver Feb 12 '16 at 16:17
  • Always compile your C code with all warnings enabled and never, never use _implicit declaration of functions_. – Jabberwocky Feb 12 '16 at 16:20
  • http://coliru.stacked-crooked.com/a/bad6f86dee456e45 – Simon Kraemer Feb 12 '16 at 16:20
  • Have a look here: [Are prototypes required for all functions in C89, C90 or C99?](http://stackoverflow.com/questions/434763/are-prototypes-required-for-all-functions-in-c89-c90-or-c99) – Jumayel Feb 12 '16 at 16:21
  • 1
    @iharob You can in ANSI C. Please stop saying wrong things. – fuz Feb 12 '16 at 16:28
  • @FUZxxl I don't think that's what I meant, and I realized my mistake and deleted my answer. I really think you have a thing with me. I do believe you are a very good programmer, but I don't think you know everything. I don't, and I keep learning on a daily basis. Sadly I can't study more because I have to work and I find SO a good place for learning because there are always questions that make you learn fundamental things that are very needed. Also, my programs are usually very efficient and robust, so I use my knowledge correctly. Somethings I don't know and when I need I research. – Iharob Al Asimi Feb 12 '16 at 16:29
  • @iharob I don't have a thing with you. I have a problem with wrong or misleading statements and you made a couple of them, thus I politely ask you to refrain from doing so. Wrong statements cause beginners to learn wrong things (and later defend their incorrect knowledge against someone trying to teach them what's actually right) so making them (especially without any kind of explanation) is a bad idea on a site like this. – fuz Feb 12 '16 at 16:32
  • @FUZxxl I can't agree more with you, really. I do think that sometimes people defend some things that are wrong just because a *more experienced programmer said*. But I say "*You can't, that's wrong*" because I mean that the fact that it compiles doesn't mean it's necessarily correct. Whether it was allowed or not is irrelevant to my comment. – Iharob Al Asimi Feb 12 '16 at 16:33
  • 2
    One good reason is that C != C++ as languages – Ed Heal Feb 12 '16 at 16:39

4 Answers4

14

The fact that the code "compiles" as a program doesn't mean you can do it. The compiler should warn about implicit declaration of the function foo().

In this particular case implicit declaration would declare an identical foo() and nothing bad will happen.

But suppose the following, say this is

main.c

/* Don't include any header, why would you include it if you don't
   need prototypes? */

int main(void)
{
    printf("%d\n", foo()); // Use "%d" because the compiler will
                           // implicitly declare `foo()` as
                           //
                           //              int foo()
                           //
                           // Using the "correct" specifier, would
                           // invoke undefined behavior "too".
    return 0;
}

Now suppose foo() was defined in a different compilation unit1 foo.c as

foo.c

double foo()
{
    return 3.5;
}

does it work as expected?

You could imagine what would happen if you use malloc() without including stdio.h, which is pretty much the same situation I try to explain above.

So doing this will invoke undefined behavior2, thus the term "Works" is not applicable in the understandable sense in this situation.

The reason this could compile is because in the very old days it was allowed by the standard, namely the standard.

The standard has never allowed this so you can't compile a program if you call a function that has no prototype ("declaration") in the code before it's called.

Modern compilers warn about this because of the potential for undefined behavior that can easily occur, and since it's not that hard to forget to add a prototype or to include the appropriate header it's better for the programmer if the compiler can warn about this instead of suddenly having a very unexplicable bug.


1It can't be compiled in the same file because it would be defined with a different return type, since it was already implicitly declared

2Starting with the fact that double and int are different types, there will be undefined behavior because of this.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • 2
    Since the question is about C vs. C++ behaviour, you could mention it has never been allowed in standard C++. – juanchopanza Feb 12 '16 at 16:19
  • *"presumably nothing bad would happen"* is not correct. It is a solid *fact* that nothing bad would happen in C because implicit declarations of functions is allowed (although generally not a good idea) – abelenky Feb 12 '16 at 17:01
  • The answer was great. Just what I needed with the appropriate amount of insight. Thanks ! – ayushi grover Feb 13 '16 at 08:57
5

When C was developed, the function name was all you needed to be able to call it. Matching arguments to function parameters was strictly the business of the programmer; the compiler didn't care if you passed three floats to something that needed just an integer.

However, that turned out to be rather error prone, so later iterations of the C language added function prototypes as a (still optional) additional restriction. In C++ these restrictions have been tightened further: now a function prototype is always mandatory.

We can speculate on why, but in part this is because in C++ it is no longer enough to simply know the function name. There can be multiple functions with the same name, but with different arguments, and the compiler must figure out which one to call. It also has to figure out how to call (direct or virtual?), and it may even have to generate code in case of a template function.

In light of all that I think it makes sense to have the language demand that the function prototype be known at the point where the function is called.

H. Guijt
  • 3,325
  • 11
  • 16
  • The normal way C++ handles functions with the same name but different parameters is known as [name mangling](http://en.wikipedia.org/wiki/Name_mangling) . Microsoft C also uses mangled names for part of the windows library, depending on calling convention (standard versus fast call), such as __imp__CreateFileA@28, where imp means DLL import (dynamic DLL) versus static (included in program image) linkage , A means Ascii (versus W for 16 bit unicode), and the @28 means the number of bytes used for parameters , – rcgldr Feb 12 '16 at 16:34
  • prototypes were added in C89 (the first standard actually), but it also had the rule that they were optional for some functions (such as `int foo()`) – M.M Feb 15 '16 at 12:00
4

Originally, C had no function prototypes, and C++ did not exist.

If you said

extern double atof();

this said that atof was a function returning double. (Nothing was said about its arguments.)

If you then said

double d = atof("1.3");

it would work. If you said

double d2 = atof();    /* whoops, forgot the argument to atof() */

the compiler would not complain, but something weird would happen if you tried to run it.

In those days, if you wanted to catch errors related to calling functions with the wrong number or type of arguments, that was the job of a separate program, lint, not the C compiler.

Also in those days, if you just called a function out of the blue that the compiler had never heard of before, like this:

int i = atoi("42");

the compiler basically pretended that earlier you had said

extern int atoi();

This was what was called an implicit function declaration. Whenever the compiler saw a call to a function whose name it didn't know, the compiler assumed it was a function returning int.

Fast forward a few years. C++ invents the function prototypes that we know today. Among other things, they let you declare the number and types of the arguments expected by a function, not just its return type.

Fast forward a few more years, C adopts function prototypes, optionally. You can use them if you want, but if you don't, the compiler will still do an implicit declaration on any unknown function call it sees.

Fast forward a few more years, to C11. Now implicit int is finally gone. A compiler is required to complain if you call a function without declaring it first.

But even today, you may be using a pre-C11 compiler that's still happy with implicit int. And a C11-complaint compiler may choose to issue a mere warning (not a compilation-killing error) if you forget to declare a function before calling it. And a C11-compliant compiler may offer an option to turn off those warnings, and quietly accept implicit ints. (For example, when I use the very modern clang, I arrange to invoke it with -Wno-implicit-int, meaning that I don't want warnings about implicit int, because I've still got lots of old, working code that I don't feel like rewriting.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
1

Why can I call a function in C without declaring it?

Because in C, but not in C++, a function without a prototype is assumed to return an int.

This is an implicit declaration of that function. If that assumption turns out to be true (the function is declared later on with return-type of int), then the program compiles just fine.

If that assumption turns out to be false (it was assumed to return an int, but then actually is found to return a double, for example) then you get a compiler error that two functions cannot have the same name. (eg. int foo() and double foo() cannot both exist in the same program)

Note that all of this is C only.
In C++, implicit declarations are not allowed. Even if they were, the error message would be different because C++ has function overloading. The error would say that overloads of a function cannot differ only by return type. (overloading happens in the parameter list, not the return-type)

abelenky
  • 63,815
  • 23
  • 109
  • 159
  • 1
    note, this is C89/C90 only. The implicit int was removed in C99 – M.M Feb 15 '16 at 12:10
  • Conforming implementations are still allowed to accept programs that rely upon implicit declarations as an extension, provided that whenever an implementation which is given such a program will output at least one diagnostic. Note that an implementation that unconditionally output "Warning: Water is wet!" would satisfy the latter requirement. – supercat Dec 06 '21 at 18:58