A function passes data from and to the calling function through the argument list and return value. While the latter is for output only, the former can be both input and output.
When a function call executes, the computer allocates memory space in the function data area for each formal parameter. The value of each actual parameter is stored in the memory cell allocated to its corresponding formal parameter. The function body can manipulate this value.
Please note that the changed value is not copied back into the actual arguments. Therefore, in our previous examples, the argument list of a function is used for input only. To use it as output, the reference of the actual argument, rather then its value, should be passed between the two functions.
A asterisk ('*') before an identifier indicates that the variable contains the address of another variable. Such a variable is called a "pointer" in C. For instance, char *signp tells the compiler that signp will contain the address of a type char variable. The program in FIGURE 6.1 has three output values in its argument list.
In the calling function, the "address of" ('&', ampersand) operator is used to pass a pointer. For example, see FIGURE 6.3. The memory content looks like FIGURE 6.4:
The character '*' (asterisk) in C has three different meanings. When used between two numbers, it means "multiplication"; then used in a declaration like "char *signp", it indicates that signp is a pointer; when used in a statement like "*signp = '-';", it means "to follow the pointer". Similarly, an ampersand '&' indicates "address of" when used before a variable, but "and" when used as an operator between two operands — '&' is "bitwise and", and '&&' is the logical "and".
The program in FIGURE 6.6 orders three numbers.
If a function only produces a single output value, it is preferred to use it as the return value of the function, and to keep the argument list as input only, passing by values. In this way, the function will not accidentally produce changes that are not desired.
For example, in the program of FIGURE 6.8, the constant macros have a scope that begins at their definitions, and continues to the end of the source file. The scope of a function name start from its prototype (if available) or header, and continues to the end of the source file. Formal arguments are local to the body of the defining function.
If a "local" variable/function has the same name as a "global" one (such as the name "one" in the previous example), the former is used whenever the name is used in the local scope. It is invalid for more than one local (or global) name to be identical within the same scope.
For example, the incomplete program in FIGURE 6.9, the output parameters of the function scan_fraction need to be passed to function scanf as the following:
status = scanf("%d %c%d", nump, &slash, denomp);
Please note that scanf expects addresses of variable to be passed after
the format, so the address of (&) symbol is used. In this example, since the first
and the third arguments are pointers (addresses of variable) themselves, they
are directly passed.
Now we can see that since scanf may get many values (one for each format specifier in the format string), all arguments passed to it must be pointers. The return value of scanf is the number of items it successfully converts and stores.
In general, if function f1 passes an argument x to another function f2, there are the following situations:
Analysis: each "number" is represented by a numerator and a denominator, both are integers. Each operation takes two numbers as input, and produce one number as output. The operations can be repeated.
Design: FIGURE 6.11
Implementation: FIGURE 6.12. Two functions in it are left as exercises. In the program, "stubs" are used as place holders.
To insert studs into incomplete program makes it possible to test parts of a program before the whole thing is finished. Each stub displays an identification message and assigns values to its output parameters to prevent execution errors caused by undefined values. For example, see FIGURE 6.14.
Especially, studs allow the main function to be debugged and tested before the sub-functions are done — this is called "top-down testing", which means to get the big picture right before filling in all the details.
To test a sub-function by itself is called "unit testing", which can be performed with a simple "driver function", which calls the function to be tested with proper actual arguments. For example, see FIGURE 6.15.
The process of separately testing individual functions before inserting them into the whole program is called "bottom-up testing". Tests of the entire systems are called "system integration tests".
Debugging tips:
An output argument in an argument list must be a pointer.
Give identifiers proper ranges, that is, neither too large nor too small.