2

I am trying to understand why we need to include prototypes of functions in the source code. From my understanding: In order to obtain an executable file from multiple source files, the source files need to be transfromed to object files. Object files can refer to each other without any problem: for example, a the main file can call the foo function which will be compiled from another source file. The linker is in charge to resolve all the references to the various functions / symbols from all the source files.

I sucessfully compiled this two files independantly and then produced the executable. You can notice there is no #include "function.h" in the main.c

function.c

int foo() {
return 1;
}

main.c

int main() {
return foo();
}

commands used:

gcc mainc.c function.c -S
gcc main.o function.o -o exec

I get a warning with the first command, due to implicit declaration of the function foo, but the output exec is working. So my question is:

Why do we need to include prototypes of functions?

DJ_Joe
  • 161
  • 1
  • 1
  • 5
  • Because the Standard requires it? – ad absurdum Nov 26 '17 at 22:47
  • 3
    you picked an optimistic example. but try using `float foo() { return 25.0; }` and see what happens when the result is interpreted as integer. – Jean-François Fabre Nov 26 '17 at 22:48
  • 1
    Possible duplicate of [Are prototypes required for all functions in C89, C90 or C99?](https://stackoverflow.com/questions/434763/are-prototypes-required-for-all-functions-in-c89-c90-or-c99) – Stephan Lechner Nov 26 '17 at 22:56
  • As of C99, this code will not compile - implicit declarations are no longer allowed. – John Bode Nov 26 '17 at 23:15
  • The main reason is to avoid the bugs that occurred in the days before prototypes because of mismatches between what the function implementation expected and what the function consumer provided. Code is vastly safer as a result of having prototypes available. And since C99, you need function declarations in scope before calling a function. That isn't formally a requirement for a prototype — a non-prototype declaration is sufficient — but there's a lot of benefit to having prototypes. You would be a fool to ignore them. – Jonathan Leffler Nov 27 '17 at 00:02
  • I want to comment on your dismissal of "it gives a warning...but". You would be well advised to pay attention to warnings. Many programmers eliminate all warnings, and compile with flags that convert warnings into fatal errors (i.e., -Wall -Werror for gcc) just to make sure they notice if new warnings pop up. The compiler warns about the implicit declaration exactly because it is a likely source of tricky errors. In the most mundane sense, prototypes are the means of eliminating that warning, while at the same time rectifying the dangerous situation that the compiler is warning about. – lockcmpxchg8b Nov 27 '17 at 00:30

5 Answers5

3

Your example matches the way C sees the functions when there's no prototype.

In C89 and earlier it is allowed to call a function that has not been declared, in that case, the compiler assumes int f(), so it works in your example (and the linker finds the function name so no complaining on that side)

But if you change the return value as float, or add parameters, the compiler will generate wrong parameter passing / return value assigning code, and you'll get weird results.

(one good example of this is trying to call the math functions returning double or float without prototype without including math.h: No math.h include - sqrt function return value?).

That's why prototypes are here. To guide the compiler on how to call external functions properly.

note that gcc warns about this even without any warning flags (so, at the lowest warning level, never a good sign for your code) with:

test.c:11:12: warning: implicit declaration of function 'foo' [-Wimplicit-function-declaration]
     return(foo());
            ^~~
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
0

The compiler is not smart as we think, sometimes we need to tell the compiler what the function expects : for exemple the parameters, the output, return type... When you don't prototypes your functions the compiler deducts what your function expects but is not accurate. When you prototype your function you simply tell the compiler that you potentially have a function named "foo" in your program that take a void parameter

Sorry if I made grammar mistakes but english isn't my native language. Regards

nathanagez
  • 36
  • 1
  • 6
0

Yes, it is possible to call functions when there are no function prototypes in scope. In fact, that's the way C worked for years. So in that sense, function prototypes are not required.

However, in the absence of function prototypes, calling external functions correctly places a heavy burden on the programmer to get all the external function calls exactly right. If you call a function with the wrong arguments, or if there's a mismatch in the function return type. you get bizarrely wrong results, which the compiler typically can't warn you about. (Once upon a time, lint could warn you about them, but lint faded away pretty much into nonexistence.)

Function prototypes are a tool to help programmers (and compilers) more reliably match up the arguments and return types in external function calls. Over time, they have gone from nonexistent (early C didn't have them at all), to optional, to required.

They are required now because the Standard says they are. Why does the Standard require them? It's not so much because they're necessary in order for compilers to generate proper code. (Again, history shows that early C compilers got by fine without them.) Rather, the Standard requires them because the consensus view today is that it's more important to get consistently reliable programs than it is to let C programmers be cowboys and try to do everything themselves.

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

Why use prototypes? There are a few reasons. C was designed to be parsed in a single pass over the source file. One consequence of that choice is that an identifier must be defined before (i.e., at a smaller line number in the source file than) it is used. If not, C will make an assumption that the identifier evaluates to type int if used in a scalar context, or function-returning-int in a function-call context.

Some times this default-int assumption is okay (e.g., your example). Other times not so much (See Jean-François Fabre's answer).

Prototypes are used to declare the actual type of a function early in a source file, without having to give the whole definition of the function. This is useful for giving the definition of things defined in other source files, but it is also useful for resolving definition/use dependencies in a single source file. E.g., this nonsensical pair of functions can't be ordered in a way that all functions are defined before they are used. The early prototype for bar solves this.

float bar(float n);    

float foo(float n)
{
  if(n < 15.0f)
    return bar(n);
  return 3.0f * n + 1.0f;
}

float bar(float n)
{
  if(n > 3.0f)
    return foo(n*2.0f);
  return n-1.0f;
}
lockcmpxchg8b
  • 2,205
  • 10
  • 16
0

As I mentioned in my comment, as of C99 implicit int declarations are no longer supported - all functions must be explicitly declared before use.

Part of it is to ensure binary compatibility between the caller and the function. If the caller expects the function to return a 32-bit int, but the function is defined to return a 64-bit double, then you'll have a runtime issue of some sort (either the value will be truncated, or memory will be overwritten, or some other issue).

If the caller and the function are defined in separate source files, then there's no way for the compiler to know that they're compatible unless you have a declaration for the function in the caller. If they're defined in the same source file, then the compiler can compare the implicit function declaration against the definition and issue a diagnostic if they don't match.

C's translation model is fairly primitive compared to more modern languages, and it relies on you, the programmer, to provide it with all the information necessary to ensure the final product works. That means including any declarations for external functions.

In the '80s and early '90s it was ridiculously easy to write code that compiled without warnings or errors yet had dozens of time bombs lurking because function declarations and definitions didn't line up. Enforcing explicit function declarations is the result of several decades of bitter experience.

John Bode
  • 119,563
  • 19
  • 122
  • 198