4.3. Recursion and argument passingSo far, we've seen how to give functions a type (how to declare the return value and the type of any arguments the function takes), and how the definition is used to give the body of the function. Next we need to see what the arguments can be used for. 4.3.1. Call by valueThe way that C treats arguments to functions is both simple and consistent, with no exceptions to the single rule. When a function is called, any arguments that are provided by the caller are simply treated as expressions. The value of each expression has the appropriate conversions applied and is then used to initialize the corresponding formal parameter in the called function, which behaves in exactly the same way as any other local variables in the function. It's illustrated here:
void called_func(int, float);
main(){
called_func(1, 2*3.5);
exit(EXIT_SUCCESS);
}
void
called_func(int iarg, float farg){
float tmp;
tmp = iarg * farg;
}Example 4.6The arguments to The initialization of the formal parameters is the last time that any communication occurs between the caller and the called function, except for the return value. For those who are used to FORTRAN and
#include <stdio.h>
#include <stdlib.h>
main(){
void changer(int);
int i;
i = 5;
printf("before i=%d\n", i);
changer(i);
printf("after i=%d\n", i);
exit(EXIT_SUCCESS);
}
void
changer(int x){
while(x){
printf("changer: x=%d\n", x);
x--;
}
}Example 4.7The result of running that is: before i=5 changer: x=5 changer: x=4 changer: x=3 changer: x=2 changer: x=1 after i=5 The function 4.3.2. Call by referenceIt is possible to write functions that take pointers as their arguments, giving a form of call by reference. This is described in Chapter 5 and does allow functions to change values in their callers. 4.3.3. RecursionWith argument passing safely out of the way we can look at recursion. Recursion is a topic that often provokes lengthy and unenlightening arguments from opposing camps. Some think it is wonderful, and use it at every opportunity; some others take exactly the opposite view. Let's just say that when you need it, you really do need it, and since it doesn't cost much to put into a language, as you would expect, C supports recursion. Every function in C may be called from any other or itself. Each
invocation of a function causes a new allocation of the variables
declared inside it. In fact, the declarations that we have been using
until now have had something missing: the keyword
/* Example of auto */
main(){
auto int var_name;
.
.
.
}
The storage for auto variables is automatically allocated and freed on function entry and return. If two functions both declare large automatic arrays, the program will only have to find room for both arrays if both functions are active at the same time. Although auto is a keyword, it is never used in practice because it's the default for internal declarations and is invalid for external ones. If an explicit initial value (see ‘ initialization’) isn't given for an automatic variable, then its value will be unknown when it is declared. In that state, any use of its value will cause undefined behaviour. The real problem with illustrating recursion is in the selection of examples. Too often, simple examples are used which don't really get much out of recursion. The problems where it really helps are almost always well out of the grasp of a beginner who is having enough trouble trying to sort out the difference between, say, definition and declaration without wanting the extra burden of having to wrap his or her mind around a new concept as well. The chapter on data structures will show examples of recursion where it is a genuinely useful technique. The following example uses recursive functions to evaluate expressions
involving single digit numbers, the operators Those of you who understand BNF notation might like to know that the expressions it will understand are described as follows:
<primary> ::= digit | (<exp>)
<unary> ::= <primary> | -<unary> | +<unary>
<mult> ::= <unary> | <mult> * <unary> |
<mult> / <unary> | <mult> % <unary>
<exp> ::= <exp> + <mult> | <exp> - <mult> | <mult>
The main places where recursion occurs are in the function
If you don't understand what it does, try running it. Trace its actions by hand on inputs such as 1 1+2 1+2 * 3+4 1+--4 1+(2*3)+4 That should keep you busy for a while!
/*
* Recursive descent parser for simple C expressions.
* Very little error checking.
*/
#include <stdio.h>
#include <stdlib.h>
int expr(void);
int mul_exp(void);
int unary_exp(void);
int primary(void);
main(){
int val;
for(;;){
printf("expression: ");
val = expr();
if(getchar() != '\n'){
printf("error\n");
while(getchar() != '\n')
; /* NULL */
} else{
printf("result is %d\n", val);
}
}
exit(EXIT_SUCCESS);
}
int
expr(void){
int val, ch_in;
val = mul_exp();
for(;;){
switch(ch_in = getchar()){
default:
ungetc(ch_in,stdin);
return(val);
case '+':
val = val + mul_exp();
break;
case '-':
val = val - mul_exp();
break;
}
}
}
int
mul_exp(void){
int val, ch_in;
val = unary_exp();
for(;;){
switch(ch_in = getchar()){
default:
ungetc(ch_in, stdin);
return(val);
case '*':
val = val * unary_exp();
break;
case '/':
val = val / unary_exp();
break;
case '%':
val = val % unary_exp();
break;
}
}
}
int
unary_exp(void){
int val, ch_in;
switch(ch_in = getchar()){
default:
ungetc(ch_in, stdin);
val = primary();
break;
case '+':
val = unary_exp();
break;
case '-':
val = -unary_exp();
break;
}
return(val);
}
int
primary(void){
int val, ch_in;
ch_in = getchar();
if(ch_in >= '0' && ch_in <= '9'){
val = ch_in - '0';
goto out;
}
if(ch_in == '('){
val = expr();
getchar(); /* skip closing ')' */
goto out;
}
printf("error: primary read %d\n", ch_in);
exit(EXIT_FAILURE);
out:
return(val);
}Example 4.8Footnotes1. Stroustrup B. (1991). The C++ Programming Language 2nd edn. Reading, MA: Addison-Wesley |
The C BookThis book is published as a matter of historical interest. Please read the copyright and disclaimer information. GBdirect Ltd provides up-to-date training and consultancy in C, Embedded C, C++ and a wide range of other subjects based on open standards if you happen to be interested. |
|
West Yorkshire Office
GBdirect Ltd
Training: 0800 651 0338 Please call between 0900 and 1700 (UK time) on Monday to Friday South East Regional Office
GBdirect Ltd
Training: 0800 651 0338 Please call between 0900 and 1700 (UK time) on Monday to Friday Please note: |