int get_students( void ) ;
Without function prototypes, a program like:
#include <stdio.h> main() { float x ; float y ; x = 12 ; y = square( x ) ; printf( "%f %f\n", x, y ) ; } float square( float x ) { return( x * x ) ; }
main()
it finds a use of the function square()
which it hasn't seen before.
In order to complete the compiling of main()
the compiler
must assume some default characteristics for the function
square()
.
Sometimes the defaults are ok, and sometimes they're just plain
wrong.
Here, they're wrong.
(In particular, the default return type is integer, but here it is
floating.)
On top of that, even if the default return type is right, the default
specifies that no checking of the number or type of the arguments is
to be done.
So the compiler won't tell you if you've given the function the wrong
number or the wrong type of arguments.
The answer to this problem is to give main()
a glimpse
into the future, a view of what functions are to come.
That what the prototype is for.
The prototype for square()
would look like:
float square( float x ) ;
float square( float ) ;
However, there are several advantages of readability given by including
the variable names.)
We've seen that any of the data types that are valid for variables
are also valid for function return types and for parameter types.
(This is only partially true as we'll see in Part
5.)
We've also seen examples where a special "type" is used for the function
return type or for the whole argument list.
That type is void
.
When we have a return type of void
as in:
void print_integer( int x ) ;
y = print_integer( x ) ;
y
.
When we use void
for the parameter list as in:
int get_choice( void ) ;
selection = get_choice() ;
selection = get_choice( x ) ;