Seeing into the Future

Now we come to the point where it's time to address the need for those lines at the beginning of the file that look like:

int get_students( void ) ;

Such lines are called function prototypes.

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 ) ;
}

would not work right. Why not? The answer is that as the compiler is scanning the function 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 ) ;

Notice that it looks just like the first line of the function declaration except it has a semicolon at the end. That's the best way to do it. You can even use the cut and paste feature of your editor to create the prototypes! (C actually allows some variation in the prototypes. In particular it ignores the names of parameters in prototypes even to the extent that they can be left out altogether. e.g. 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 ) ;

we are telling the compiler that there is to be no return value from this function. So it would be illegal to include a statement such as:

y = print_integer( x ) ;

in the same program. The expression on the right hand side of the assignment would have no value to be copied to y.

When we use void for the parameter list as in:

int get_choice( void ) ;

we're telling the compiler that there are to be no arguments to this function. So here, we could use the function like:

selection = get_choice() ;

but

selection = get_choice( x ) ;

would be an error.

Which of these function prototypes are valid?

int foo( void x ) ;
char bar( int ) ;
float which( int x, char y ) ;
void is_it( void ) ;
double who( float x, y ) ;