11.4. Multimodule ProgramsThe trouble with the way that I built the reverse program is that the reverse function cannot easily be used in other programs. For example, let's say that I wanted to write a function that returns 1 if a string is a palindrome, and 0 if it is not. A palindrome is a string that reads the same forward and backward; for example, "noon" is a palindrome, and "nono" is not. I could use the reverse function to implement my palindrome function. One way to do this is to cut-and-paste reverse () into the palindrome program file, but this is a poor technique for at least three reasons:
As I'm sure you realize, there's a better way to share functions. 11.4.1. Reusable FunctionsA better strategy for sharing reverse () is to remove reverse () from the reverse program, compile it separately, and then link the resultant object module into whichever programs wish to use it. This technique avoids all three of the problems listed in the previous section and allows the function to be used in many different programs. Functions with this property are called reusable. 11.4.2. Preparing a Reusable FunctionTo prepare a reusable function, create a source code module that contains the source code of the function, together with a header file that contains the function's prototype. Then compile it into an object module by using the -c option of gcc. An object module contains machine code together with symbol-table information that allows it to be combined with other object modules when an executable file is being created. Here is a listing of our new filesfirst, "reverse.h": 1 /* REVERSE.H */ 2 3 int reverse (); /* Declare but do not define this function */ Here is a listing of the new "reverse.c": 1 /* REVERSE.C */ 2 3 #include <stdio.h> 4 #include "reverse.h" 5 6 /****************************************************************/ 7 8 reverse (before, after) 9 10 char *before; /* A pointer to the original string */ 11 char *after; /* A pointer to the reversed string */ 12 13 { 14 int i; 15 int j; 16 int len; 17 18 len = strlen (before); 19 20 for (j = len - 1, i = 0; j >= 0; j--, i++) /* Reverse loop */ 21 after[i] = before[j]; 22 23 after[len] = 0; /* terminate reversed string */ 24 } Here's a listing of a main program that uses reverse (): 1 /* MAIN1.C */ 2 3 #include <stdio.h> 4 #include "reverse.h" /* Contains the prototype of reverse () */ 5 6 /****************************************************************/ 7 8 main () 9 10 { 11 char str [100]; 12 13 reverse ("cat", str); /* Invoke external function */ 14 printf ("reverse (\"cat\") = %s\n", str); 11.4.3. Separately Compiling and Linking ModulesTo compile each source code file separately, use the -c option of gcc. This creates a separate object module for each source code file, each with a ".o" suffix: $ gcc -c reverse.c ...compile reverse.c to reverse.o. $ gcc -c main1.c ...compile main1.c to main1.o. $ ls -lG reverse.o main1.o -rw-r--r-- 1 ables 311 Jan 5 18:08 main1.o -rw-r--r-- 1 ables 181 Jan 5 18:08 reverse.o $ _ Alternatively, you can place all of the source code files on one line: $ gcc -c reverse.c main1.c ...compile each .c file to .o file. $ _ To link them all together into an executable called "main1", list the names of all the object modules after the gcc command: $ gcc reverse.o main1.o -o main1 ...link object modules. $ ls -lG main1 ...examine the executable. -rwxr-xr-x 1 ables 24576 Jan 5 18:25 main1* $ ./main1 ...run the executable. reverse ("cat") = tac reverse ("noon") = noon $ _ In UNIX environments, one often used the stand-alone linking loader (ld) to link separate modules. gcc can do this job, and it includes many default library references that would be painful to have to manually determine and add to your command line. You can see the work gcc does by adding the -v option to the command line to generate verbose output. 11.4.4. Reusing the Reverse FunctionNow that you've seen how the original reverse program may be built out of a couple of modules, let's use the reverse module again to build the palindrome program. Here's the header and source code listing of the palindrome functionfirst, "palindrome.h": 1 /* PALINDROME.H */ 2 3 int palindrome (); /* Declare but do not define */ Here is our file "palindrome.c": 1 /* PALINDROME.C */ 2 3 #include "palindrome.h" 4 #include "reverse.h" 5 #include <string.h> 6 7 /****************************************************************/ 8 9 int palindrome (str) 10 11 char *str; 12 13 { 14 char reversedStr [100]; 15 reverse (str, reversedStr); /* Reverse original */ 16 return (strcmp (str, reversedStr) == 0); /* Compare the two */ 17 } Here's the source code of the program "main2.c" that tests the palindrome function (): 1 /* MAIN2.C */ 2 3 #include <stdio.h> 4 #include "palindrome.h" 5 6 /****************************************************************/ 7 8 main () 9 10 { 11 printf ("palindrome (\"cat\") = %d\n", palindrome ("cat")); 12 printf ("palindrome (\"noon\") = %d\n", palindrome ("noon")); 13 } The way to combine the "reverse", "palindrome", and "main2" modules is as before: compile the object modules and then link them. We don't have to recompile "reverse.c", as it hasn't changed since the "reverse.o" object file was created. $ gcc -c palindrome.c ...compile palindrome.c to palindrome.o. $ gcc -c main2.c ...compile main2.c to main2.o. $ gcc reverse.o palindrome.o main2.o -o main2 ...link them. $ ls -lG reverse.o palindrome.o main2.o main2 -rwxr-xr-x 1 ables 24576 Jan 5 19:09 main2* -rw-r--r-- 1 ables 306 Jan 5 19:00 main2.o |