Read Sams Teach Yourself C in 24 Hours Online
Authors: Tony. Zhang
23 067231861x CH18 1/25/00 10:31 AM Page 303
Using Special Data Types and Functions
303
For instance, the prototype of the Convert2Upper() function in line 13 contains two arguments that are all char pointers declared with PTR_STR.
In lines 17–20, two arrays of pointers, str and moon, are declared with STRING. moon is initialized to point to the strings of the Japanese haiku. In lines 21 and 22, two int variables, i and term, are declared with INTEGER.
The for loop in lines 24–33 allocates enough memory space dynamically based on the size of the haiku. The Conver2Upper() function is then called in line 31 to copy strings referenced by moon to the memory locations pointed by str and to convert all lowercase characters to their uppercase counterparts as well. Line 32 prints out the strings referenced by moon. The definition of the Conver2Upper() function is shown in lines 42–54.
In lines 34–37, another for loop prints out the content from the memory locations referenced by str. There are a total of three strings with uppercase characters in the content.
After a string is displayed on the screen, the memory space allocated for the string is released by calling the free() function. Because line 35 prints out each string with a newline at its beginning instead of the end, a newline that will follow the final string is printed on line 38.
On the screen, you see two copies of the haiku—the original one and the one with all-18
uppercase characters.
Recursive Functions
You already know that in C a function can be called by another function. But can a function call itself? The answer is yes. A function can call itself from a statement inside the body of the function itself. Such a function is said to be
recursive
.
Listing 18.4 contains an example of calling a recursive function to add integers from 1
to 100.
TYPE
LISTING 18.4
Calling a Recursive Function
1: /* 18L04.c: Calling a recursive function */
2: #include
3:
4: enum con{MIN_NUM = 0,
5: MAX_NUM = 100};
6:
7: int fRecur(int n);
8:
9: main()
continues
23 067231861x CH18 1/25/00 10:31 AM Page 304
304
Hour 18
LISTING 18.4
continued
10: {
11: int i, sum1, sum2;
12:
13: sum1 = sum2 = 0;
14: for (i=1; i<=MAX_NUM; i++)
15: sum1 += i;
16: printf(“The value of sum1 is %d.\n”, sum1);
17: sum2 = fRecur(MAX_NUM);
18: printf(“The value returned by fRecur() is %d.\n”, sum2);
19:
20: return 0;
21: }
22: /* function definition */
23: int fRecur(int n)
24: {
25: if (n == MIN_NUM)
26: return 0;
27: return fRecur(n - 1) + n;
28: }
After the executable file 18L04.exe is created and executed, the following output is displayed on the screen of my computer:
The value of sum1 is 5050.
OUTPUT
The value returned by fRecur() is 5050.
In the program in Listing 18.4, a recursive function, fRecur(), is declared in line
ANALYSIS
7 and defined in lines 23–28.
You can see from the definition of the fRecur() function that the recursion is stopped in line 26 if the incoming int variable, n, is equal to the value contained by the enum name MIN_NUM. Otherwise, the fRecur() function is called by itself over and over in line 27.
Note that each time the fRecur() function is called, the integer argument passed to the function is decreased by one.
Now, let’s have a look at the main() function of the program. The for loop, shown in lines 14 and 15, adds integers from 1 to the value represented by another enum name, MAX_NUM. In lines 4 and 5, MIN_NUM and MAX_NUM are respectively assigned 0 and 100 in an enum declaration. The printf() call in line 16 then prints out the sum of the addition made by the for loop.
In line 17, the recursive function, fRecur(), is called and passed an integer argument starting at the value of MAX_NUM. The value returned by the fRecur() function is then assigned to an int variable, sum2.
23 067231861x CH18 1/25/00 10:31 AM Page 305
Using Special Data Types and Functions
305
When the fRecur() function returns, the value of sum2 is printed out in line 18. From the output, you can see that the execution of the recursive function fRecur() produces the same result as the for loop inside the main() function.
Recursive functions are useful in making clearer and simpler implementations of algorithms. On the other hand, however, recursive functions may run slower than their iterative equivalents due to the overhead of repeated function calls.
Function arguments and local variables of a program are usually stored temporarily by the computer, in a block of memory called the
stack
. Each call to a recursive function makes a new copy of the arguments and local variables.
The new copy is then put on the stack. If you see your recursive function behaving strangely, it’s probably overwriting other data stored on the stack.
Although it’s possible that the recursive function is simply being executed too many times and exhausting the stack resource, this kind of problem
often occurs when you have a case of “runaway recursion.” In Listing 18.4, the if statement in line 25 stops the recursion when n is equal to MIN_NUM. In your own recursive functions, always remember to have such a condition in place to stop the recursion so that the function will return back to the original caller.
18
Revisiting the
main()
Function
As you’ve learned, each C program must have one and only one main() function. The execution of a program starts and ends at its main() function.
As with other functions in C, you can pass arguments to a main() function. So far, I’ve been using the void keyword in the definition of the main() function to indicate that there are no arguments passed to the function. Now, the question is what to do if you want to pass information to the main() function.
Command-Line Arguments
Because each C program starts at its main() function, information is usually passed to the main() function via command-line arguments.
A
command-line argument
is a parameter that follows a program’s name when the program is invoked from the operating system’s command line. For instance, given a C program, test.c, whose executable file is called test.exe, if you are using a PC, you might run the program from a DOS prompt like this:
test argument1 argument2 argument3
23 067231861x CH18 1/25/00 10:31 AM Page 306
306
Hour 18
argument1, argument2, and argument3 are called command-line arguments to the main() function in the test.c program.
The next subsection teaches you how to receive command-line arguments.
Receiving Command-Line Arguments
There are two built-in arguments in the main() function that can be used to receive command-line arguments. Usually, the name of the first argument is argc, and it is used to store the number of arguments on the command line. The second argument is called argv and is a pointer to an array of char pointers. Each element in the array of pointers points to a command-line argument that is treated as a string.
In order to use argc and argv, declare your main() function in your program like this: main(int argc, char *argv[])
{
. . .
}
Let’s continue to use the example shown in the last section. Suppose that the main() function defined in the test.c program looks like this:
main(int argc, char *argv[])
{
. . .
}
If you run the executable file of the program from a prompt like this:
test argument1 argument2 argument3
the value received by argc is 4 because the name of the program itself is counted as the first command-line argument. Accordingly, argv[0] holds a representation of the program name, and argv[1], argv[2], and argv[3] contain the strings of argument1, argument2, and argument3, respectively.
The program in Listing 18.5 is another example of passing command-line arguments to the main() function.
LISTING 18.5
Passing Command-Line Arguments to the main()
TYPE
Function
1: /* 18L05.c: Command-line arguments */
2: #include
3:
4: main (int argc, char *argv[])
5: {
23 067231861x CH18 1/25/00 10:31 AM Page 307
Using Special Data Types and Functions
307
6: int i;
7:
8: printf(“The value received by argc is %d.\n”, argc);
9: printf(“There are %d command-line arguments passed to main().\n”, 10: argc);
11: if(argc) {
12: printf(“The first command-line argument is: %s\n”, argv[0]); 13: printf(“The rest of the command-line arguments are:\n”);
14: for (i=1; i
15: printf(“%s\n”, argv[i]);
16: }
17: return 0;
18: }
After the executable file 18L05.exe is executed with several command-line arguments, the following output is displayed on the screen of my computer (I entered “Hello, world!” in this example.):
18L05.exe Hello, world!
OUTPUT
The value received by argc is 3.
There are 3 command-line arguments passed to main().
The first command-line argument is: C:\app\18L05.EXE
The rest of the command-line arguments are:
18
Hello,
world!
The first line of the output shown above contains the executable file 18L05.exe,
ANALYSIS
and the command-line arguments, Hello, world!, from a DOS prompt on my computer. The purpose of the program in Listing 18.5 is to show you how to check the number of command-line arguments and print out the strings that hold the arguments entered by the user.
Note that there are two arguments, argc and argv, that are declared in line 4 for the main() function. Then, the statements in lines 8 and 9 print out the value of the total number of arguments held by argc. If the program was run with no command-line arguments, argc can be 0, in which case argv is a null pointer. Line 11 makes sure that argc and argv contain data; if not, we return 0 and end the program.
Line 12 prints out the first string saved in the memory location pointed to by argv[0].
As you can see from the output, the content of the first string is the executable file name of the program in Listing 18.5, plus the path to the executable file.
The for loop in lines 14 and 15 displays the rest of the strings that contain the command-line arguments entered by the user. In this example, I enter two command-line argument strings, “Hello,” and “world!”, which are shown back on the screen after the execution of the for loop.
23 067231861x CH18 1/25/00 10:31 AM Page 308
308
Hour 18
argc and argv are normally used as the two built-in arguments in the main() function, but you can use other names to replace them in their declarations.
As long as the data types are correct, your main() function will still be able to receive the command line arguments.
Summary
In this lesson you’ve learned the following important data type, keywords, and concepts in C:
• The enum (that is, enumerated) data type can be used to declare named integer constants.
• By default, the first enum name starts with the value of 0. Each name in the rest of the list increases by one from the value contained by the name on its left side.
• If needed, you can assign any integer values to enumerated names.
• You can create your own names for data types with the help of the typedef keyword. Those names can then be used as synonyms for the data types.
• In ANSI C, there is a header file called stddef.h that contains a dozen typedef definitions.
• A function in C can be made to call itself. Such a function is said to be recursive.
• You can use command-line arguments to pass information to the main() function in your program.
• There are two built-in arguments to the main() function.
• The first built-in argument receives the number of command-line arguments entered by the user. The second built-in argument is a pointer to an array of pointers that refers to the strings of command-line arguments.
In the next lesson you’ll learn about collecting variables of different types with structures.
Q&A
Q What can you do with the
enum
data type?
A
The enum data type can be used to declare names that represent integer constants.
You can use the default values held by enum names, or you can assign values to enum names and use them later in the program. The enum data type makes the C
program more readable and easier to maintain because you can use words that you understand as the names of enum, and you will only have to go to one place to update the values when needed.
23 067231861x CH18 1/25/00 10:31 AM Page 309
Using Special Data Types and Functions
309
Q Why do you need to use the
typedef
keyword?
A
By using the typedef keyword, you can define your own names to represent the data types in C. You can represent complex data types in a single word and then use that word in subsequent variable declarations. In this way, you can avoid typing errors when writing a complex declaration over and over. Also, if a data type is changed in the future, you just need to update the typedef definition of the data type, which fixes every use of the typedef definition.
Q Does a recursive function help to improve the performance of a program?
A
Not really. Normally, a recursive function only makes the implementations of some algorithms clearer and simpler. A recursive function may slow down the speed of a program because of the overhead of repeated function calls.