Must declare function prototype in C? [duplicate]
Asked Answered
S

10

64

I am kind of new to C (I have prior Java, C#, and some C++ experience). In C, is it necessary to declare a function prototype or can the code compile without it? Is it good programming practice to do so? Or does it just depend on the compiler? (I am running Ubuntu 9.10 and using the GNU C Compiler, or gcc, under the Code::Blocks IDE)

Suannesuarez answered 4/4, 2010 at 16:59 Comment(1)
Related: Are prototypes required for all functions in C89, C90 or C99?Fluorescence
I
72

In ANSI C (meaning C89 or C90), you do not have to declare a function prototype; however, it is a best practice to use them. The only reason the standard allows you to not use them is for backward compatibility with very old code.

If you do not have a prototype, and you call a function, the compiler will infer a prototype from the parameters you pass to the function. If you declare the function later in the same compilation unit, you'll get a compile error if the function's signature is different from what the compiler guessed.

Worse, if the function is in another compilation unit, there's no way to get a compilation error, since without a a prototype there's no way to check. In that case, if the compiler gets it wrong, you could get undefined behavior if the function call pushes different types on the stack than the function expects.

Convention is to always declare a prototype in a header file that has the same name as the source file containing the function.

In C99 or C11, standard C requires a function declaration in scope before you call any function. Many compilers do not enforce this restriction in practice unless you force them to do so.

Intricate answered 4/4, 2010 at 17:8 Comment(10)
You will not necessarily get a "compile error", if the actual function type is different from the inferred one. This is undefined behavior in C, but not a constraint violation.Labionasal
You're right, should have been more specific, what I meant was that most compilers will throw a warning.Intricate
In C99, you do need a function declaration - though I suppose it does not have to be a prototyped declaration. C90 (C89) was a lot more lax about it. Note that you do need a prototype in scope if the function uses 'variable arguments' - even in C89/C90.Blalock
@JonathanLeffler In both C99 implementations I've used (gcc, clang), a missing declaration flags a warning, not an error, even though the declaration is mandatory.Antipode
@JeremyP: the compilers are still (too?) forgiving by default, largely because of the legacy code bases that still have to be updated. The compilers don't enforce the letter of the standard unless you force their hand (-Werror, for example, and perhaps -pedantic or -Wold-style-definition or -Wold-style-declaration or -Wmissing-prototypes or -Wstrict-prototypes or all (or most) of the above). I usually use -std=c11 and all those except -pedantic, but I make sure that POSIX or related #define's are active. I don't write #define _GNU_SOURCE or #define _BSD_SOURCE very often.Blalock
@JonathanLeffler: I've seen some systems which use different default calling conventions for non-prototyped functions than are used for prototyped functions that take arguments, and also use different link-time naming. If a function has a prototype and isn't marked as using stack-based arguments, attempting to call it without a visible declaration will result in a link-time error.Elemi
@supercat: can you identify the systems. It isn't orthodox C for functions to be renamed (or otherwise unlinkable) when mis-invoked. C++ can (and does) rename functions, of course, in order to provide type-safe linking. Are you sure it wasn't using a C++ compiler?Blalock
@JonathanLeffler: One that comes to mind is the Archimedes compiler for the 8051. It is extremely orthodox for C functions to be renamed somewhat (quite commonly by prepending a _) so as to prevent naming conflicts with things like assembler keywords, CPU registers, compiler-generated labels, etc.Elemi
@JonathanLeffler: BTW, I think using name prefixes to identify calling conventions is a good pattern; adding such a pattern to the ARM ABI would allow code which use the CPU for floating-point math (and doesn't require an FPU) to inter-operate smoothly with code that expects floating-point values to be passed in FPU registers. Have methods which which expect things in FPU registers apply a prefix to their name; when compiling such a function, include a "weak" definition for a function named without that prefix which loads FPU registers with values passed elsewhere and calls...Elemi
...the one with the prefix. Have code which would naturally have things in the FPU registers call the specially-named one, but include a weak definition for a specially-named function that adjusts the parameter format and calls the non-specially named one. Such an approach would allow code written to use an FPU to call directly to methods that expect an FPU, and vice versa, without added overhead, and would allow calls between code expecting different calling conventions to be automatically dispatched through stubs that would fix the parameters as needed.Elemi
L
75

It is never required to declare a prototype for a function in C, neither in "old" C (including C89/90) nor in new C (C99). However, there's a significant difference between C89/90 and C99 with regard to function declarations.

In C89/90 it was not necessary to declare a function at all. If the function is not declared at the point of the call, the compiler "guesses" (infers) the declaration implicitly from the types of the arguments passed in the call and assumes that the return type is int.

For example

int main() {
  int i = foo(5); 
  /* No declaration for `foo`, no prototype for `foo`.
     Will work in C89/90. Assumes `int foo(int)` */

  return 0;
}

int foo(int i) {
  return i;
}

In C99 every function that you call must be declared before point of the call. However, it is still not necessary to declare it with a prototype specifically. A non-prototype declaration will work as well. This means that in C99 the "implicit int" rule no longer works (for inferred function return types, in this case), but parameter types can still be guessed from the argument types if function is declared without a prototype.

The previous example will not compile in C99, since foo is not declared at the point of the call. Yet, you can add a non-prototype declaration

int foo(); /* Declares `foo`, but still no prototype */

int main() {
  int i = foo(5); 
  /* No prototype for `foo`, although return type is known. 
     Will work in C99. Assumes `int foo(int)` */

  return 0;
}
...

and end up with valid C99 code.

Nevertheless, it is always a good practice to declare a prototype for the function before you call it.

An additional note: I said above that it is never required to declare a function prototype. In fact, for some functions it is a requirement. In order to properly call a variadic function in C (printf for example) the function must be declared with a prototype before the point of the call. Otherwise, the behavior is undefined. This applies to both C89/90 and C99.

Labionasal answered 4/4, 2010 at 17:16 Comment(6)
+1 for a detailed answer with all the nuances explained wellFluorescence
"will not compile in C99" -- no compiler errors (only warnings) on gcc and clang with -std=c99 on my system. ideone also compiles itOceanid
@Oceanid GCC documentation comes with help: "(...) to obtain all the diagnostics required by the standard, you should also specify -pedantic (or -pedantic-errors if you want them to be errors rather than warnings) (...)".Kinswoman
@Kinswoman what is your point? Are you trying to say that "-std=c99" is not enough to declare it "will compile in c99"? Perhaps you are right, it depends on what someone is trying to say exactly and your interpretation may be closer to what the author of the answer meant by "will not compile in C99"Oceanid
@jfs, because in GCC "-std=c99" means c99 standard plus gnu extensions. The problem might compile because of GNU extensions allows it. You need to add "-Wpedantic" for GCC to issue all the warnings demanded by strict ISO C.Tshombe
"The -pedantic option causes gcc to print required diagnostics for violations of constraints and syntax rules. In some cases, those diagnostics are merely warnings -- and there's no easy way to distinguish between those warnings and other warnings that aren't required by the language. Replace -pedantic by -pedantic-errors to cause gcc to treat language violations as fatal errors." —— From https://mcmap.net/q/21215/-what-is-the-default-c-std-standard-version-for-the-current-gcc-especially-on-ubuntuPictograph
I
72

In ANSI C (meaning C89 or C90), you do not have to declare a function prototype; however, it is a best practice to use them. The only reason the standard allows you to not use them is for backward compatibility with very old code.

If you do not have a prototype, and you call a function, the compiler will infer a prototype from the parameters you pass to the function. If you declare the function later in the same compilation unit, you'll get a compile error if the function's signature is different from what the compiler guessed.

Worse, if the function is in another compilation unit, there's no way to get a compilation error, since without a a prototype there's no way to check. In that case, if the compiler gets it wrong, you could get undefined behavior if the function call pushes different types on the stack than the function expects.

Convention is to always declare a prototype in a header file that has the same name as the source file containing the function.

In C99 or C11, standard C requires a function declaration in scope before you call any function. Many compilers do not enforce this restriction in practice unless you force them to do so.

Intricate answered 4/4, 2010 at 17:8 Comment(10)
You will not necessarily get a "compile error", if the actual function type is different from the inferred one. This is undefined behavior in C, but not a constraint violation.Labionasal
You're right, should have been more specific, what I meant was that most compilers will throw a warning.Intricate
In C99, you do need a function declaration - though I suppose it does not have to be a prototyped declaration. C90 (C89) was a lot more lax about it. Note that you do need a prototype in scope if the function uses 'variable arguments' - even in C89/C90.Blalock
@JonathanLeffler In both C99 implementations I've used (gcc, clang), a missing declaration flags a warning, not an error, even though the declaration is mandatory.Antipode
@JeremyP: the compilers are still (too?) forgiving by default, largely because of the legacy code bases that still have to be updated. The compilers don't enforce the letter of the standard unless you force their hand (-Werror, for example, and perhaps -pedantic or -Wold-style-definition or -Wold-style-declaration or -Wmissing-prototypes or -Wstrict-prototypes or all (or most) of the above). I usually use -std=c11 and all those except -pedantic, but I make sure that POSIX or related #define's are active. I don't write #define _GNU_SOURCE or #define _BSD_SOURCE very often.Blalock
@JonathanLeffler: I've seen some systems which use different default calling conventions for non-prototyped functions than are used for prototyped functions that take arguments, and also use different link-time naming. If a function has a prototype and isn't marked as using stack-based arguments, attempting to call it without a visible declaration will result in a link-time error.Elemi
@supercat: can you identify the systems. It isn't orthodox C for functions to be renamed (or otherwise unlinkable) when mis-invoked. C++ can (and does) rename functions, of course, in order to provide type-safe linking. Are you sure it wasn't using a C++ compiler?Blalock
@JonathanLeffler: One that comes to mind is the Archimedes compiler for the 8051. It is extremely orthodox for C functions to be renamed somewhat (quite commonly by prepending a _) so as to prevent naming conflicts with things like assembler keywords, CPU registers, compiler-generated labels, etc.Elemi
@JonathanLeffler: BTW, I think using name prefixes to identify calling conventions is a good pattern; adding such a pattern to the ARM ABI would allow code which use the CPU for floating-point math (and doesn't require an FPU) to inter-operate smoothly with code that expects floating-point values to be passed in FPU registers. Have methods which which expect things in FPU registers apply a prefix to their name; when compiling such a function, include a "weak" definition for a function named without that prefix which loads FPU registers with values passed elsewhere and calls...Elemi
...the one with the prefix. Have code which would naturally have things in the FPU registers call the specially-named one, but include a weak definition for a specially-named function that adjusts the parameter format and calls the non-specially named one. Such an approach would allow code written to use an FPU to call directly to methods that expect an FPU, and vice versa, without added overhead, and would allow calls between code expecting different calling conventions to be automatically dispatched through stubs that would fix the parameters as needed.Elemi
R
6

it's not a must, if the function is defined before its use.

Rightism answered 4/4, 2010 at 17:5 Comment(3)
What's the point of defining it after its use?Briones
Just the logical order of code in the file, or the fuction is defined in other file.Rightism
In fact new-style definition of a function is a prototype as much as it is declaration. If the definition happens before any use, another prototype without body would be pointless.Kinswoman
P
4

It is not required, but it is bad practice not to use prototypes.

With prototypes, the compiler can verify you are calling the function correctly (using the right number and type of parameters).

Without prototypes, it's possible to have this:

// file1.c
void doit(double d)
{
    ....
}

int sum(int a, int b, int c)
{
    return a + b + c;
}

and this:

// file2.c

// In C, this is just a declaration and not a prototype
void doit();
int sum();

int main(int argc, char *argv[])
{
    char idea[] = "use prototypes!";

    // without the prototype, the compiler will pass a char *
    // to a function that expects a double
    doit(idea);

    // and here without a prototype the compiler allows you to
    // call a function that is expecting three argument with just
    // one argument (in the calling function, args b and c will be
    // random junk)
    return sum(argc);
}
Polymorphism answered 4/4, 2010 at 17:7 Comment(3)
@NeilButterworth: When compiling file2.c the definition of doit (which is in file1.c) is not visible.Fussell
@NeilButterworth - I'll reformat, but the comments // file1.c and // file2.c are meant to imply these are different files. When compiling file2.c, the compiler will not have seen the definition in file1.cPolymorphism
My comment preceded your edit. I'll delete it.Desiccator
B
2

Function prototype is not mandatory according to the C99 standard.

Bouffant answered 4/4, 2010 at 17:13 Comment(0)
S
2

In C, if we do not declare a function prototype and use the function definition, there is no problem and the program compiles and generates the output if the return type of the function is "integer". In all other conditions the compiler error appears. The reason is, if we call a function and do not declare a function prototype then the compiler generates a prototype which returns an integer and it searches for the similar function definition. if the function prototype matches then it compiles successfully. If the return type is not integer then the function prototypes do not match and it generates an error. So it is better to declare function prototype in the header files.

Shinberg answered 20/5, 2012 at 7:6 Comment(0)
X
1

C allows functions to be called even though they have not previously been declared, but I strongly recommend you to declare a prototype for all functions before using them so that the compiler can save you if you use the wrong arguments.

Xeres answered 4/4, 2010 at 17:7 Comment(0)
H
1

You should put function declarations in the header file (X.h) and the definition in the source file (X.c). Then other files can #include "X.h" and call the function.

Haug answered 4/4, 2010 at 17:8 Comment(6)
Why is this -1? This is what you should be doing.Haug
Can someone explain why this is a bad idea, I was always under the impression this was standard practice? The only issue that occurs from this is circular inclusion. But, otherwise if you plan your includes you won't run into that problem.Fancy
Because not all functions are meant to be public. If you have static void foo() defined after static int bar(), and bar calls foo .. well :) Why would you prototype functions that aren't exposed in a public header? I'm not the one who down voted, though.Mccormick
I assume the downvote (not from me) was because this doesn't address the question.Desiccator
@Tim, bar can call foo because the compiler will already know about it from the prototype. Unless this is untrue in C, it should be true in C++. Can you post an example so maybe we can get a better idea of what you mean? @Neil Even though it doesn't answer the question directly it does relate to a "best practice" that is related to the question which I don't see as a need to down vote. Maybe not an upvote, but it isn't something that I would want removed or is unnecessary.Fancy
@Tim maybe because a programmer would like to have all functions from a c file in a descriptive way in its header?Unclothe
S
0

It's not absolutely necessary to declare a function for the calling code to compile. There are caveats though. Undeclared function is assumed to return int and compiler will issue warnings first about function being undeclared and then about any mismatches in return type and parameter types.

Having said that it's obvious that declaring functions properly with prototypes is a much better practice.

Sheltonshelty answered 4/4, 2010 at 17:10 Comment(0)
G
-4

Select ‘Options’ menu and then select ‘Compiler | C++ Options’. In the dialog box that pops up, select ‘CPP always’ in the ‘Use C++ Compiler’ options. Again select ‘Options’ menu and then select ‘Environment | Editor’. Make sure that the default extension is ‘C’ rather than ‘CPP’.

Gocart answered 10/6, 2014 at 7:27 Comment(1)
This does not seem to answer the question that was asked but some loosely related question about a specific IDE.Blalock

© 2022 - 2024 — McMap. All rights reserved.