Function Prototypes: Necessary or just good practice?

D

/dev/phaeton

In K&R2, function definitions in a program were preceded by a function
prototype, i.e.:

int my_function(int variable1, int variable2);

Then somewhere down the line is the actual definition of the function
itself:

int my_function(int variable1, int variable2)
{
do;
some;
stuff;
return (some_value);
}


However, I've noticed that if I omit the function prototype, the program
compiles (with gcc 4.6.x at least) and runs just fine. Is this
something that's now acceptable in ANSI standards since K&R2 was
printed? Or is this 'compiler forgiveness'- letting me do something I
probably shouldn't?


Thanks.

-J
 
J

Johann Klammer

/dev/phaeton said:
In K&R2, function definitions in a program were preceded by a function
prototype, i.e.:

int my_function(int variable1, int variable2);

Then somewhere down the line is the actual definition of the function
itself:

int my_function(int variable1, int variable2)
{
do;
some;
stuff;
return (some_value);
}


However, I've noticed that if I omit the function prototype, the program
compiles (with gcc 4.6.x at least) and runs just fine. Is this something
that's now acceptable in ANSI standards since K&R2 was printed? Or is
this 'compiler forgiveness'- letting me do something I probably shouldn't?


Thanks.

-J

Hello,

in a sense your function definition includes the prototype..
As long as any function that calls it is further down the file,
there will not be any problems.

methinks the prototype(only) would be

int my_function(int, int);

and the definition(minimum required) would be:

my_function(variable1, variable2)
{

}

if there's no prototype, AFAIK
some assumptions regarding the types
are made by the compiler. It assumes int as return type
and ints for any parameters specified (unless it's pointers
then it is pointers to int).
so for above example you get something like the following:

int my_function(int,int)

which just happens to be your original declaration.

This raises a question..
Why prototypes?
Including type information in function definitions increases
readability and is completely alright for static functions
as long as any functions that call it are further down the file.
However, this usually reverses the 'logical' order of function
definitions inside a file, so lowest level functions show up
first.
That is why most people explicitly specify prototypes also
for static functions.

They are usually needed for extern functions to be used with
header files, of course.

Warning: above is mostly how I understand C99, I do not know
too much about K&R C. So take it with a grain of salt.

bye,
JK
 
S

Stephen Sprunk

In K&R2, function definitions in a program were preceded by a function
prototype, i.e.:

int my_function(int variable1, int variable2);

Note this is a prototyped function _declaration_.
Then somewhere down the line is the actual definition of the function
itself:

int my_function(int variable1, int variable2)
{
do;
some;
stuff;
return (some_value);
}

This is a prototyped function _definition_. Note that a definition
declares the function as well, if that hasn't already been done.
However, I've noticed that if I omit the function prototype,

ITYM the function declaration.
the program compiles (with gcc 4.6.x at least) and runs just fine.

Since you don't include the rest of the program's source, we don't know
if this is luck or you just didn't happen to do something wrong.

Lots of stuff happens to work "just fine" on some implementations even
though that may not be guaranteed.
Is this something that's now acceptable in ANSI standards since K&R2
was printed? Or is this 'compiler forgiveness'- letting me do something I
probably shouldn't?

It's not new; in fact, it's the old (pre-ANSI) way of doing things.

K&R C allowed calling functions that hadn't previously been declared,
which caused an implicit declaration of a function by that name. This
was deprecated by ANSI. Unless you _know_ you are coding for a platform
for which there is no C89 (or later) compiler, which should be
incredibly rare these days, you should always declare your functions.

S
 
P

Philip Lantz

/dev/phaeton said:
In K&R2, function definitions in a program were preceded by a function
prototype, i.e.:

int my_function(int variable1, int variable2);

Then somewhere down the line is the actual definition of the function
itself:

int my_function(int variable1, int variable2)
{
do;
some;
stuff;
return (some_value);
}


However, I've noticed that if I omit the function prototype, the program
compiles (with gcc 4.6.x at least) and runs just fine. Is this
something that's now acceptable in ANSI standards since K&R2 was
printed? Or is this 'compiler forgiveness'- letting me do something I
probably shouldn't?

1. If all calls to the function are after the definition, then the
definition acts as a prototype and there is no problem. The following
points are based on the assumption that there is a call before the
definition.

2. It's allowed by the C90 standard, as long as the function returns int
and the correct parameters are passed. The number and types of
parameters are not checked, and the behavior is undefined if they don't
match the function definition (based on a non-trivial set of matching
rules in the standard).

3. It's not allowed by C99 and later standards. Many compilers may still
support it, though, for backward compatibility.

4. It is letting you do something you probably shouldn't--even when the
behavior is allowed and defined by the standard, it is much better to
always have a prototype-style definition or declaration of every
function before it is called.
 
K

Kaz Kylheku

In K&R2, function definitions in a program were preceded by a function
prototype, i.e.:

It's good practice to declare functions before calling them.
Calling undeclared functions is a deprecated language feature.

However, there is often no need for prototypes within one translation unit,
because you can organize the code so that each function is defined prior to any
calls to the function. A function definition serves as a prototype declaration.

This arrangement is impossible when you have mutual recursion among
two or more functions. Some of them have to be prototyped.
However, I've noticed that if I omit the function prototype, the program
compiles (with gcc 4.6.x at least) and runs just fine.

With what warning options?

How about with -Wall and -W?
 
B

Ben Bacarisse

in a sense your function definition includes the prototype..
As long as any function that calls it is further down the file,
there will not be any problems.

methinks the prototype(only) would be

int my_function(int, int);

That's fine, but so is the longer form with named parameters given by
the OP.
and the definition(minimum required) would be:

my_function(variable1, variable2)
{

}

No, that's not allowed. First, in C99, you are not allowed to omit the
return type. Even in older C (where the implicit int return is OK), the
parameters must be declared either in the function declarator (that's
what the OP did and is the modern way) or in a separate declaration list
between the ) and the { like this:

my_function(variable1, variable2)
int variable1, variable2;
{
}

This last form is old K&R C and was still legal in C90. Add "int" for
the return type and it's still legal in C99 (though very bad style
nowadays).
if there's no prototype, AFAIK
some assumptions regarding the types
are made by the compiler. It assumes int as return type

In C90 ("ANSI C") yes. In C99 implicit declarations are not permitted.
and ints for any parameters specified (unless it's pointers
then it is pointers to int).
so for above example you get something like the following:

int my_function(int,int)

No, you get int my_function() and there is a real difference. You are
confusing what are called the default argument promotions with an
implied prototype. When there's no prototype, the compiler assumes
nothing about the parameters, but it does perform certain type
conversions in a call. If the compiler assumed the prototype you've
written, a call like my_function(1.0, 2.0) would have the
arguments converted, but that's not what happens. The details hardly
matter, since this is only important for very old code.
which just happens to be your original declaration.

This raises a question..
Why prototypes?
Including type information in function definitions increases
readability and is completely alright for static functions
as long as any functions that call it are further down the file.

They (prototypes) also cause correct conversion of function arguments.
You don't get that with non-prototype declarations or definitions.

<snip>
 
R

Ralf Damaschke

Ben Bacarisse said:
No, that's not allowed. First, in C99, you are not allowed to
omit the return type. Even in older C (where the implicit int
return is OK), the parameters must be declared either in the
function declarator (that's what the OP did and is the modern
way) or in a separate declaration list between the ) and the {
like this:

my_function(variable1, variable2)
int variable1, variable2;
{
}

I still remember that I used to write
"main (argc, argv) char **argv; { /* some work */ return 0; }".

And C90 did not break such existing code omitting an implicit int
declaration:

| If the declarator includes an identifier list, the types of the
| parameters may be declared in a following declaration list. Any
| parameter that is not declared has type int.

-- Ralf
 
P

Peter Nilsson

Ben Bacarisse said:
In C99 implicit declarations are not permitted.

They're no longer required, but if a C99+ implementation
decides to support them anyway, no diagnostic is required
for their use.

....
They (prototypes) also cause correct conversion of function
arguments. ...

Unless the prototype ends with ellipsis and you're back to
default argument promotions for the unnamed arguments.
 
J

James Kuyper

They're no longer required, but if a C99+ implementation
decides to support them anyway, no diagnostic is required
for their use.

Its a constraint violation - 6.7.2p2: "At least one type specifier shall
be given in the declaration specifiers in each declaration,
and in the specifier-qualifier list in each struct declaration and type
name." Therefore a diagnostic is required.
 
B

Ben Bacarisse

James Kuyper said:
Its a constraint violation - 6.7.2p2: "At least one type specifier shall
be given in the declaration specifiers in each declaration,
and in the specifier-qualifier list in each struct declaration and type
name." Therefore a diagnostic is required.

I don't think he's talking about implicit int. I certainly wasn't.

However, I still think it's a constraint violation. I think 6.5.1 p2
and 6.5.2.2 p1 conspire to make it so (either on it's is probably
enough).
 
J

James Kuyper

I don't think he's talking about implicit int. I certainly wasn't.

Calling a function without a declaration in scope is a constraint
violation (6.5.2.2p2). If an actual declaration is present, an implied
one is unnecessary (except when the implicit int rule was in effect,
which it no longer is). So if an implicit declaration is needed, there's
either a constraint violation in the declaration of the function or in
the call of the function.
However, I still think it's a constraint violation. I think 6.5.1 p2
and 6.5.2.2 p1 conspire to make it so (either on it's is probably
enough).

6.5.2.2p1 is the one that seems relevant to me.
 
B

Ben Bacarisse

James Kuyper said:
Calling a function without a declaration in scope is a constraint
violation (6.5.2.2p2).

Did you mean p2? p2 is entirely conditional and it might not apply.
If an actual declaration is present, an implied
one is unnecessary (except when the implicit int rule was in effect,
which it no longer is). So if an implicit declaration is needed, there's
either a constraint violation in the declaration of the function or in
the call of the function.

I am sure we agree, but I don't find your reasoning easy to follow. In
addition to text that defines a constraint, there must also be an
absence on any text that permits implicit declarations. C90 has almost
exactly the same wording as C99's 6.5.2.2 p1 (and p2 for that matter)
yet C99 and C90 differ in this regard. This is because C90 has wording
(that C99 does not) that defines an explicit declaration in certain
cases.

My point is you can't just say "because of 6.5.2.2p2". Of course, I
also made this mistake by just citing two sections. I should also have
said "and there's nothing that says a declaration will be assumed".
6.5.2.2p1 is the one that seems relevant to me.

I am not sure it has much relevance (see below) but in any case it can't
rule out all cases on it's own. What's wrong with this:

void (*fp)(void) = function;

in the absence of a declaration for 'function'? 6.5.2.2 p1 does not
prohibit it, but 6.5.1 p2 does. As you can see, I'm retracting my view
that either one is probably enough. I think 6.5.1 p2 is probably enough
on it's own because it says that both the above and

function();

are constraint violations when there's no function declaration in scope.
6.5.2.2 p1 rules out things like this:

int f;
f();

but when f has no declaration at all, f() is not even a function call
expression.
 
P

Peter Nilsson

James Kuyper said:
Its a constraint violation - 6.7.2p2: "At least one type specifier shall
be given in the declaration specifiers in each declaration,
and in the specifier-qualifier list in each struct declaration and type
name." Therefore a diagnostic is required.

My bad. I was looking at using functions without a declaration in
scope.
e.g...

#include <stdio.h>
int main(void) { foo(); return 0; }
int foo(void) { puts("42"); return 0; }

AFAICS, if a C99+ implementation supports C90 style implicit function
declarations as an extension, then it is not required to issues a
diagnostic since no constraint would be violated.
 
D

/dev/phaeton

With what warning options?

How about with -Wall and -W?

Honestly? None. I suppose I should start using them.


However, the gist that I'm getting from all this...

It's a good idea to do prototyping, and in the worst case scenario it is
just superfluous. So I will continue to do them.

Sorry to start a lukewarm debate- though it's been quite educational for
me to read through it. I thank you all for that.


Note this is a prototyped function _declaration_.

I will have to re-read the K&R2 usage of "definition" and "declaration".
I seem to be confused on which is which. Thanks for the correction.

Out of time for now... Back to A&P II homework for the rest of the
night. *sigh*

-J
 
K

Keith Thompson

Peter Nilsson said:
My bad. I was looking at using functions without a declaration in
scope.
e.g...

#include <stdio.h>
int main(void) { foo(); return 0; }
int foo(void) { puts("42"); return 0; }

AFAICS, if a C99+ implementation supports C90 style implicit function
declarations as an extension, then it is not required to issues a
diagnostic since no constraint would be violated.

No, that's still a constraint violation (strictly speaking a syntax
error), and providing an extension doesn't permit a compiler to
omit the required diagnostic.

C99 6.5.1p2:

An identifier is a primary expression, provided it has been
declared as designating an object (in which case it is an lvalue)
or a function (in which case it is a function designator).

with a footnote:

Thus, an undeclared identifier is a violation of the syntax.

A conforming C99+ compiler may support this as an extension, but
it still has to issue a diagnostic.
 
K

Kaz Kylheku

Honestly? None. I suppose I should start using them.


However, the gist that I'm getting from all this...

It's a good idea to do prototyping, and in the worst case scenario it is
just superfluous. So I will continue to do them.

Or instead of duplicating every declaration, you could just turn on the right
diagnosticxs in the compiler so that you're told when a prototype is necessary,
but missing.

Writing declarations just for the sake of writing declarations can even
lead you to a mistake.

/* forgot to include "foo.h" where foo is declared */

int foo(char *x)
{
}

If you use -Wstrict-prototypes -Wmissing-prototypes, GCC can help you
catch the problem that a declaration was forgotten.

This could indicate that the header file was not included, meaning
that the program is at risk of being inconsistent (definition changes
but the header file declaration does not).

Sometimes this diagnostic indicates that a function which should be static has
been made external.

But if you have a declaration like in the above, then this diagnostic is shut
up:

/* forgot to include "foo.h" where foo is declared */

int foo(char *x);

/*...*/

int foo(char *x)
{
}

The issue is that "foo.h" could have the wrong prototype, causing other
translation units to have incorrect calls to foo.
 
J

James Kuyper

Honestly? None. I suppose I should start using them.


However, the gist that I'm getting from all this...

It's a good idea to do prototyping, and in the worst case scenario it is
just superfluous.

Keep in mind that your original question, whatever you might have
thought to the contrary, was not actually about whether prototyping
should be done, but about whether it should be done twice - once in a
function declaration, and a second time in the function definition. The
separate declaration often is, as you say, superfluous, and should not
be used if that is the case.

Whether you need to provide a separate declaration is just a matter of
how you organize your code, but you should always use a prototype
whenever you either declare or define a function. They're extremely
useful - they're never superfluous. That's primarily because they allow
the C implementation to recognize certain common kinds of mistakes, and
require it to give you a diagnostic message if you've made one of those
mistakes.

On the separate declaration issue:
The first part of a function's definition is, in itself, a declaration
of the function, so if you never use a function anywhere but in a single
file, and if you define that function prior to any calls to that
function, a separate declaration of the function unnecessarily
complicates your code, and should not be provided. The only case where
this doesn't work is mutually recursive functions - for instance, if
func1() calls func2(), and func2() also calls func1(), at least one of
them must be declared first, with the definition coming later.

For any function which is used in at least one file other than the one
in which it is defined, you should create a header file containing a
declaration of that function, and #include that header file in any file
where that function is used. You should also #include that header file
in the file where the function is defined - that way, if you made a
mistake and the declaration of the function isn't compatible with the
definition of the function, you'll get a diagnostic message telling you so.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top