ANSI C Function Prototyping

I l @ ve RuBoard

ANSI C Function Prototyping

The traditional, pre-ANSI C scheme for declaring functions was deficient in that it declared a function's return type, but not its arguments. Let's look at the kinds of problems that arise when the old form of function declarations is used.

The following pre-ANSI declaration informs the compiler that imin() returns a type int value:

 int imin(); 

However, it says nothing about the number or type of imin() 's arguments. Therefore, if you use imin() with the wrong number or type of arguments, the compiler doesn't catch the error.

The Problem

Let's look at some examples involving imax() , a close relation to imin() . Listing 9.4 shows a program that declares imax() the old-fashioned way and then uses imax () incorrectly.

Listing 9.4 The misuse.c program.
 /* misuse.c -- uses a function incorrectly */ #include <stdio.h> int imax();      /* old-style declaration */ int main(void) {   printf("The maximum of %d and %d is %d.\n",        3, 5, imax(3));   printf("The maximum of %d and %d is %d.\n",        3, 5, imax(3.0, 5.0));   return 0; } int imax(n, m) int n, m; {   int max;   if (n > m)        max = n;   else        max = m;   return max; } 

The first call to printf() omits an argument to imax() , and the second call uses floating-point arguments instead of integers. Despite these errors, the program compiles and runs.

Here's the output using Microsoft's Visual C++ 5.0:

 The maximum of 3 and 5 is 6618680. The maximum of 3 and 5 is 1074266112. 

With Borland C 3.0 (DOS), we got values of 3 and . The two compilers work fine; they are merely victims of the program's failure to use function prototypes .

What's happening? The mechanics may differ among systems, but here's what goes on with a PC or VAX. The calling function places its arguments in a temporary storage area called the stack, and the called function reads those arguments off the stack. These two processes are not coordinated with one another. The calling function decides which type to pass based on the actual arguments in the call, and the called function reads values based on the types of its formal arguments. Therefore, the call imax(3) places one integer on the stack. When the imax() function starts up, it reads two integers off the stack. Only one was actually placed on the stack, so the second value read is whatever value happened to be sitting in the stack at the time.

The second time the example uses imax() , it passes float values to imax() . This places two double values on the stack. (Recall that a float is promoted to double when passed as an argument.) On our system, that's two 64-bit values, so 128 bits of data are placed on the stack. When imax() reads two int s from the stack, it reads the first 64 bits on the stack because, on our system, each int is 32 bits. These bits happened to correspond to two integer values, the larger of which was 1074266112.

The ANSI Solution

The ANSI standard's solution to the problems of mismatched arguments is to permit the function declaration to declare the variable types, too. The result is a function prototype , a declaration that states the return type, the number of arguments, and the types of those arguments. To indicate that imax() requires two int arguments, you can declare it with either of the following prototypes:

 int imax(int, int); int imax(int a, int b); 

The first form uses a comma-separated list of types. The second adds variable names to the types. Remember that the variable names are dummy names and don't have to match the names used in the function definition.

With this information at hand, the compiler can check to see whether the function call matches the prototype. Are there the right number of arguments? Are they the correct type? If there is a type mismatch and if both types are numbers or pointers, the compiler does a type cast to convert the actual arguments to the same type as the formal arguments. For example, imax(3.0, 5.0) becomes imax ((int) 3.0, (int) 5.0) . We've modified Listing 9.4 to use a function prototype. The result is shown in Listing 9.5.

Listing 9.5 The proto1.c program.
 /* proto1.c -- uses a function prototype */ #include <stdio.h> int imax(int, int);        /* prototype */ int main(void) {   printf("The maximum of %d and %d is %d.\n",        3, 5, imax(3));   printf("The maximum of %d and %d is %d.\n",        3, 5, imax(3.0, 5.0));   return 0; } int imax(int n, int m) {   int max;   if (n > m)        max = n;   else        max = m;   return max; } 

When we tried to compile Listing 9.5, our compiler gave an error message stating that the call to imax() had too few parameters.

What about the type errors? To investigate those, we replaced imax(3) with imax(3, 5) and tried compilation again. This time there were no error messages, and we ran the program. Here is the resulting output:

 The maximum of 3 and 5 is 5. The maximum of 3 and 5 is 5. 

As promised , the 3.0 and 5.0 of the second call were converted to 3 and 5 so that the function could handle the input properly.

Although it gave no error message, our compiler did give a warning to the effect that a double was converted to int and that there was a possible loss of data. The difference between an error and a warning is that an error prevents compilation and a warning permits compilation. One interesting point is that one compiler made this type cast without telling us. Most programmers object to this "silent" correction, but by resetting the warning level on that compiler, we got it to warn us about the type conversion. Many compilers enable you to select which warning you want made. However, the ANSI standard does not require compilers to provide that feature.

No Arguments and Unspecified Arguments

Suppose you give a prototype like this:

 void print_name(); 

An ANSI C compiler will assume that you have decided to forego function prototyping, and it will not check arguments. To indicate that a function really has no arguments, use the void keyword within the parentheses:

 void print_name(void); 

ANSI C interprets the preceding expression to mean that print_name() takes no arguments. It then checks to see that you, in fact, do not use arguments when calling this function.

A few functions, such as printf() and scanf() , take a variable number of arguments. In printf() , for instance, the first argument is a string, but the remaining arguments are fixed in neither type nor number. ANSI C allows partial prototyping for such cases. You could, for example, use this prototype for printf() :

 int printf(char *, ...); 

This prototype says that the first argument is a string (Chapter 11, "Character Strings and String Functions," elucidates that point) and that there may be further arguments of an unspecified nature.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net