Managing Compiler Warnings

The C compiler will often report messages. These messages can be divided into error messages and warning messages. Error messages indicate things that must be corrected in order for the compile to succeed. Warnings alert the programmer to bad practices and problems that might occur later when the program is run.

With the maximum compile warning level set, the compiler reports on the smallest of infractions, but it usually does so intelligently and diligently. Sometimes warnings are issued for valid C programming practices, and some developers disable these warnings with certain compiler options. By doing this, they prevent the C compiler from providing useful advice.

The best advice that can be provided here is to always use the maximum warning level available. This forces the developer to address all source code issues until the warnings disappear from the compilation. The only justifiable reason for going to a lower warning level is when you've inherited someone else's source code and you do not have the luxury of time to fix all the causes of warnings.

Tip

Always compile with the maximum warning level turned on. Time spent eliminating causes of warning messages, can save a lot of time later while debugging your program.

With the GNU compiler under FreeBSD and Linux, this is done by adding the -Wall option.


The following shows how to use the GNU compiler under FreeBSD with the maximum warning level enabled:

bash$ gcc -Wall hello.c
					

The compile examples in this book will all use the -Wall option unless the example involves a non-GNU compiler.

Note

Most UNIX command-line options do not require a space to appear between the option letter and the option's argument. For example, the option may be specified as -Wall or -W all, since these are equivalent.


Working with Compiler Warning Messages

When a high warning level is used by the compiler, every possible warning message is reported. A low warning level will report only the most important messages and suppress the rest.

As noted earlier, there is one drawback to using a high warning level with your C compiler: Sometimes you'll receive warning messages for valid C language constructs. Well-designed compilers will help you cope with these problems, however, since they allow you to use tricks to convey your real intention.

Warnings About Assignments

A programmer often loves the economy of expression available in the C language. This means that the programmer will employ the smallest number of statements or operators to accomplish a task. Sometimes this involves doing an assignment and a test for non-zero all in one step. Consider the if statement in Listing 1.1.

Code Listing 1.1. asgn1.c— Warnings About Value Assignment in the if Statement
1:    #include <string.h>
2:
3:    char *
4:    Basename(char *pathname) {
5:        char *cp;                    /* Work Pointer */
6:
7:        if ( cp = strrchr(pathname,'/') )
8:            return cp + 1;           /* Return basename pointer */
9:        return pathname;             /* No directory component */
10:   }

Note

The program listings in this book include line numbers at the extreme left. Do not type these if you are entering the example programs manually. They are included only for ease of reference.


Here is the compile session for Listing 1.1:

$ cc -c -Wall asgn1.c
asgn1.c: In function `Basename':
asgn1.c:7: warning: suggest parentheses around assignment used as truth value
$

Notice the statement in line 7. The reason the compiler flags this statement as a possible error is that often the C programmer really intends to use the comparison operator == to compare values instead of assigning a value in an if statement. The compiler has no way of confirming whether the actual assignment is correct or whether a comparison was intended instead. The developer is left to decide the issue after the compiler has issued the warning.

Note that the statement is not incorrect, but neither is it certain that it reflects the programmer's true intention. Some might be tempted to argue that comparison is normal in an if statement and that the assignment in an if statement is unusual. The fact remains, however, that the C language is defined such that both are equally valid expressions.

Compiler writers have developed clever tricks for dealing with these thorny issues. This particular case can be resolved this way: If an assignment is coded as shown in Listing 1.1, it is flagged with a warning because it represents a possible error on the programmer's part. If this does represent an error, the programmer replaces the single equals symbol with a double equals symbol and recompiles. If the assignment is the intent, the programmer encloses the assignment with a set of brackets. When this is done, the compiler will assume that the programmer knows what he is doing.

Listings 1.2 and 1.3 show two different ways to resolve the warning issue in favor of the assignment.

Code Listing 1.2. asgn2.c—Additional Parentheses Quiet an Assignment Warning
1:   #include <string.h>
2:
3:   char *
4:   Basename(char *pathname) {
5:       char *cp;                    /* Work Pointer */
6:
7:       if ( (cp = strrchr(pathname,'/')) )
8:           return cp + 1;           /* Return basename pointer */
9:       return pathname;             /* No directory component */
10:  }

Code Listing 1.3. asgn3.c—Parentheses and Comparison Quiet an Assignment Warning
1:   #include <string.h>
2:
3:   char *
4:   Basename(char *pathname) {
5:       char *cp;                    /* Work Pointer */
6:
7:       if ( (cp = strrchr(pathname,'/')) != 0 )
8:           return cp + 1;           /* Return basename pointer */
9:       return pathname;             /* No directory component */
10:  }

Note the extra pair of parentheses around the assignment in line 7 of both Listings 1.2 and 1.3. The C syntax here did not require the parentheses, but the compiler took this as a cue from the developer that he knows what he is doing. While Listing 1.2 shows a solution acceptable to the GNU compiler, some other UNIX compilers will insist on the construct shown in Listing 1.3. For this reason, the solution in Listing 1.3 is preferred. It is clearer to the reader of the source code.

Tip

There is normally no longer a need to economize in C language expressions for the sake of optimization. Today's optimizing compilers are very effective at producing optimal code without any help from the programmer. For this reason it is better to make an expression easier to read than to reduce it to the fewest number of C operators.


This discussion has been presented using the C language if statement, but this issue applies to other statements as well. Warnings about assignments in the switch and while statements can be quieted in the same manner.

Warnings About Unused Arguments

Some compilers will complain about unused arguments. The thinking appears to be that if the argument is defined, then it was meant to be used. The truth of the matter is that the function arguments define an interface. There is no real requirement to fully use the interface that is defined, since an interface may also be intended for future use.

An example of the unused argument problem is the ubiquitous main() program. The main program interface is often defined as follows:

int main(int argc,char *argv[]);

If the program being written does not use the arguments that are present, it doesn't seem proper to remove the arguments simply because they are unused. This is what often is done by programmers to eliminate the compiler warnings.

Instead, it seems preferable to leave the arguments declared to indicate that the interface supports passing those values in that way. Listing 1.4 shows a simple way to avoid this problem.

Code Listing 1.4. uargs.c—Quieting Unused Argument Warnings
1:   #include <stdio.h>
2:
3:   int
4:   main(int argc,char **argv) {
5:
6:       (void) argc;
7:       (void) argv;
8:
9:       puts("Hello World!");
10:      return 0;
11:  }

The C language permits a reference of a value in isolation, within a statement. Normally, this is not a useful construct, since there is no useful side effect in this case. However, it can be used as a useful compiler side effect, and this is exactly what is done with the (void) cast in lines 6 and 7 of Listing 1.4.

It should be noted that the GNU compiler in the FreeBSD 3.4 Release does not warn about unused arguments (gcc version 2.7.2.3). However, the compiler that you are using might.

Resolving Unused Variable Warnings

Sometimes the compiler will warn you about unused variables that you have declared in your code. These warnings create a strong temptation to remove the variables from your code immediately. You should exercise great care before doing so.

Warning

Be extremely careful about removing unused variables and buffers. Make sure that you fully evaluate the C preprocessing directives of the source code before you assume that these values are never used. Sometimes compiling a program with different macro settings can cause these variable declarations to be needed. This is especially true when source code is compiled on different UNIX platforms.


The problem of unused variables often occurs in code that is designed to be portable to many different UNIX platforms. The specific problem is normally that the original developer never properly allowed for the unused declarations at the right time with the help of the correct C preprocessing directives. What often happens is that the source code is patched and modified by several people, and those changes never get fully retested on the other platforms on which it was meant to compile.

Listing 1.5 illustrates a program that, when compiled a certain way, will have unused variables. But are these variables truly unnecessary?

Code Listing 1.5. uvars.c—An Example of Unused Variable Declarations
1:   /* uvars.c */
2:
3:   #include <stdio.h>
4:   #include <unistd.h>
5:   #include <sys/types.h>
6:
7:   int
8:   main(int argc,char **argv) {
9:       pid_t PID;              /* Process ID */
10:
11:      (void) argc;
12:      (void) argv;
13:
14:  #ifdef SHOW_PID
15:      PID = getpid();         /* Get Process ID */
16:      printf("Hello World! Process ID is %d
",(int)PID);
17:  #else
18:      puts("Hello World!");
19:  #endif
20:
21:      return 0;
22:  }

When Listing 1.5 is compiled without defining the C macro SHOW_PID, the result looks like this:

$ cc -Wall uvars.c
uvars.c: In function `main':
uvars.c:9: warning: unused variable `PID'
$

The compiler in this example has complained that the declared variable PID in line 9 is not used. This happens because the macro SHOW_PID is not defined, causing line 18 to be compiled in the place of lines 15 and 16. In this compile, the variable PID is unreferenced.

However, if you take this warning message at face value and remove the declaration of variable PID in line 9, then you will solve the immediate problem but create another, longer-term problem. If you define the macro SHOW_PID in the next compile, you find that it is necessary under different compile conditions:

$ cc -Wall -DSHOW_PID uvars.c
$ ./a.out
Hello World! Process ID is 73337
$

Adding the option -DSHOW_PID to the cc command line defined the SHOW_PID macro for this particular compile. As shown, you can see that the compile was successful and without any warning messages.

While this concept is obvious in this small example program, this same scenario often occurs in many real-life examples of UNIX code that are much more complex. The message here is to be careful about what you assume should be deleted from the source code when you get unused variable warnings.

Resolving Unreferenced String Warnings

Unreferenced string constants will also cause warnings to be generated. Sometimes programmers leave a string constant in a program so that it will become part of the final executable. A common practice is to define version strings in a program so that the executable file can be dumped and matched up with a particular version of a source module.

Tip

To eliminate compiler warnings about unreferenced string constants, simply declare the string constant as a constant using the C language const keyword.


The solution to these warnings is simply to define the string constant as a constant using the const keyword. The compiler does not complain about unreferenced constants. Listing 1.6 shows an example of an embedded CVS string that causes an unreferenced string warning to be issued by the compiler.

Code Listing 1.6. ustring.c—Example of an Unreferenced CVS String
1:   /* ustring.c */
2:
3:   #include <stdio.h>
4:
5:   static char cvsid[] =
6:    "$Header: /home/cvs/prj/ustring.c,v 1.6 2010/03/30 01:59:34 uid Exp $";
7:
8:   int
9:   main(int argc,char **argv) {
10:
11:          (void) argc;
12:          (void) argv;
13:
14:          puts("Hello World!");
15:          return 0;
16:  }

The compile session for Listing 1.6 is as follows:

$ cc -Wall ustring.c
ustring.c:5: warning: 'cvsid'defined but not used
$

Note lines 5 and 6 of Listing 1.6, where the string array cvsid[] is declared. The purpose of this declaration is simply to have the string constant appear in the final executable file. This allows you to identify the version of the source code that went into the executable program. It can be displayed with the ident command:

$ ident a.out
a.out:
     $Header: /home/cvs/prj/ustring.c,v 1.6 2010/03/30 01:59:34 uid Exp $
$

The ident(1) command locates the string constants that start with $Header: and end with the $ character. The problem is that the compiler complains about this string constant because the string itself is not used within the code.

Tip

Some prefer to use the CVS/RCS identification string $Id$ instead of $Header$, since the string is shorter (the directory path is not included). However, note that some versions of the ident(1) command will not report the $Id$ string (for example, HPUX 10.2 and 11.0 will not report $Id$, but Linux and FreeBSD will).

Other UNIX platforms may not have the ident(1) command at all (AIX 4.3 and SunOS 5.6, for example). In that case you can use the strings(1) command and grep(1) for the string '$Header:' instead:

$ strings a.out | grep '$Header:'


The compiler is easily quieted about the unreferenced string by simply defining the string as a constant. The compiler does not require constants to be referenced. See Listing 1.7 for the corrected source code.

Code Listing 1.7. ustring2.c—Eliminating the Unused String Constant Warning
1:   /* ustring.c */
2:
3:   #include <stdio.h>
4:
5:   static const char cvsid[] =
6:    "$Header: /home/cvs/prj/ustring.c,v 1.6 2010/03/30 01:59:34 uid Exp $";
7:
8:   int
9:   main(int argc,char **argv) {
10:
11:          (void) argc;
12:          (void) argv;
13:
14:          puts("Hello World!");
15:          return 0;
16:  }

Line 5 of Listing 1.7 shows the added const keyword that is necessary to soothe the compiler. The compile session that follows confirms this:

$ cc -Wall ustring2.c
$

Unlike the other compile, there are no warning messages.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.16.254