ANSI C Type Qualifiers

I l @ ve RuBoard

ANSI C Type Qualifiers

You've seen that a variable is characterized by both its type and its storage class. ANSI C adds two more properties: constancy and volatility. These properties are declared with the keywords const and volatile , which create qualified types . The C9X committee proposes adding a third qualifier, restrict , designed to facilitate compiler optimizations; Appendix H, "The C9X Committee," discusses it briefly .

The const Type Qualifier

Chapter 4, "Character Strings and Formatted Input/Output," and Chapter 10 have already introduced const . To review, the const keyword in a declaration establishes a variable whose value cannot be modified by assignment or by incrementing or decrementing . On an ANSI-compliant compiler, the code

 const int nochange;   /* qualifies m as being constant */ nochange = 12;        /* not allowed                   */ 

should produce an error message. You can, however, initialize a const variable. Therefore, the following code is fine:

 const int nochange = 12;  /* ok */ 

The preceding declaration makes nochange a read-only variable. Once initialized , it cannot be changed.

You can use the const keyword to, for example, create an array of data that the program can't alter:

 const int days1[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; 
Using const with Pointers and Parameter Declarations

Using the const keyword when declaring a simple variable and an array is pretty easy. Pointers are more complicated because you have to distinguish between making the pointer itself const and making the value that is pointed to const . The declaration

 const float * pf;  /* pf points to a constant float value */ 

establishes that pf points to a value that must remain constant. The value of pf itself can be changed. For example, it can be set to point at another const value. In contrast, the declaration

 float * const pt;    /* pt is a const pointer */ 

says that the pointer pt itself cannot have its value changed. It must always point to the same address, but the pointed-to value can change. Finally, the declaration

 const float * const ptr; 

means both that ptr must always point to the same location and that the value stored at the location must not change.

One common use for this new keyword is declaring pointers that serve as formal function parameters. For example, consider the function print() from Listing 13.13. If you pass it a pointer to the beginning of the string, the function returns the length of the string. In general, passing a pointer to a function enables that function to alter data in the calling function, but the following prototype prevents that from happening:

 void print(const int array[], int limit); 

In a prototype and a function header, the parameter declaration const int array[] is the same as const int * array , so the declaration says that the data to which array points cannot be changed.

The ANSI C library follows this practice. If a pointer is used only to give a function access to values, the pointer is declared as a pointer to a const -qualified type. If the pointer is used to alter data in the calling function, then the const keyword isn't used. For instance, the ANSI C declaration for strcat() is this:

 char *strcat(char *, const char *); 

Recall that strcat() adds a copy of the second string to the end of the first string. This modifies the first string, but leaves the second string unchanged. The declaration reflects this.

Using const with Global Data

Recall that using global variables is considered a risky approach because it exposes data to being mistakenly altered by any part of a program. That risk disappears if the data is const , so it is perfectly reasonable to use global variables with the const qualifier. You can have const variables, const arrays, and const structures. (Structures are a compound data type discussed in the next chapter.)

One area that requires care, however, is sharing const data across files. There are two strategies you can use. The first is to follow the usual rules for external variables ”use defining declarations in one file and reference declarations (using the keyword extern ) in the other files:

 /* file1.c -- defines some global constants */ const double PI = 3.14159; const char * MONTHS[12] =      {"January", "February", "March", "April", "May", "June", "July",       "August", "September", "October", "November", "December"}; /* file2.c -- use global constants defined elsewhere */ extern const double PI; extern const * MONTHS[]; 

The second approach is to place the constants in an include file. Here you must take the additional step of using the static external storage class:

 /* constant.h -- defines some global constants */ static const double PI = 3.14159; static const char * MONTHS[12] =      {"January", "February", "March", "April", "May", "June", "July",       "August", "September", "October", "November", "December"}; /* file1.c -- use global constants defined elsewhere */ #include "constant.h" /* file2.c -- use global constants defined elsewhere */ #include "constant.h" 

If you don't use the keyword static , then including constant.h in file1.c and in file2.c would result in each file having a defining declaration of the same identifier, which is not supported by the ANSI standard. (Some compilers, however, do allow it.) By making each identifier static external, you actually give each file a separate copy of the data. That wouldn't work if the files were supposed to use the data to communicate with one another because each file would see only its own copy. Because the data is constant (by using the const keyword) and identical (by having both files include the same header file), however, that's not a problem.

The advantage of the header file approach is that you don't have to remember to use defining declarations in one file and reference declarations in the next; all files simply include the same header file. The disadvantage is that the data is duplicated . For the preceding examples, that's not a real problem, but it might be one if your constant data includes enormous arrays.

The volatile Type Qualifier

The volatile qualifier tells the compiler that a variable can have its value altered by agencies other than the program. It is typically used for hardware addresses and for data shared with other programs running simultaneously . For instance, an address might hold the current clock time. The value at that address changes as time changes, regardless of what your program is doing. Or an address could be used to receive information transmitted from, say, another computer.

The syntax is the same as for const :

 volatile int loc1;   /* loc1 is a volatile location       */ volatile int * ploc; /* ploc points to a volatile location */ 

These statements declare loc1 to be a volatile value and ploc to point to a volatile value. You may think that volatile is an interesting concept, but you might be wondering why the ANSI committee felt it necessary to make volatile a keyword. The reason is that it facilitates compiler optimization. Suppose, for example, you have code like this:

 val1 = x; /* some code not using x */ val2 = x; 

A smart (optimizing) compiler might notice that you use x twice without changing its value. It would temporarily store the x value in a register. Then, when x is needed for val2 , it can save time by reading the value from a register instead of from the original memory location. This procedure is called caching. Ordinarily, caching is a good optimization, but not if x is changed between the two statements by some other agency. If there were no volatile keyword, a compiler would have no way of knowing whether this might happen. Therefore, to be safe, the compiler couldn't cache. That was the pre-ANSI situation. Now, however, if the volatile keyword is not used in the declaration, the compiler can assume that a value hasn't changed between uses, and it can then attempt to optimize the code.

A value can be both const and volatile . For instance, the hardware clock setting normally should not be changed by the program, making it const , and it is changed by an agency other than the program, making it volatile . Just use both qualifiers in the declaration, as shown here; the order doesn't matter:

 volatile const int loc; const volatile int * ploc; 
I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net