Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced C 1992

.pdf
Скачиваний:
92
Добавлен:
17.08.2013
Размер:
4.28 Mб
Скачать

Data Types, Constants, Variables, and Arrays

C C C

 

C2C

 

C C C

 

C

#include <stdio.h> /* Make includes first part of file */ #include <string.h>

int main(void); /* Declare main() and the fact that this program doesn’t use any passed parameters. */

int main()

{

int nCounter = 0;

{

int nCountLoop = 0; /* This nCounter is unique to the loop */

do

{

nCountLoop += 3; /* Increments (and prints) the loop’s

nCounter */

printf(“nCountLoop is = %d?\n”, nCountLoop);

}

while (++nCounter < 10); /* Increments the function’s nCounter */

}

printf(“Ended, nCounter is = %d?\n”, nCounter);

return (0);

}

Using unique variable names is the only way to guarantee that there will be no confusion over which variable is being used. This is a good case of “the language lets you do something, but you really don’t want to.”

Life Span (Or How Long Is It Going To Be Here?)

Determining how long a variable will be kept is another problem that perplexes aspiring programmers. Let’s look at the keyword modifier static. This modifier has several purposes that, unfortunately, are related.

39

Part I • Honing Your C Skills

When static is used on a variable found within a function or block, it tells the compiler never to discard or reallocate the variable. The variable is created at compile time and is initialized to zero. The opposite of static in this situation is auto (the default). That variable, found inside a function or block, is reallocated every time the function or block is entered.

When static is used on a variable that is defined outside any functions or blocks, its meaning is that the variable is known to only those functions contained in the specified source file, and are not known outside the source file. When a variable is known outside the source file, it is called an external variable. (Don’t confuse this with the keyword extern.) The extern keyword tells the compiler that the variable is being defined (and not declared). Because extern and static conflict, they cannot be used together. The program LIFETIME.C, in Listing 2.6, shows a variable’s lifetime.

Listing 2.6. LIFETIME.C.

/* LIFETIME, written 15 May 1992 by Peter D. Hipson */ /* An example of variable lifetime. */

#include <stdio.h> // Make includes first part of file #include <string.h>

int nLife = {5}; // Initialize to 5, default is 0.

int main(void); // Define main() and the fact that this program doesn’t // use any passed parameters.

void

DisplayLife(void); // Define DisplayLife()

int main()

{

int nCounter = 0;

do

{

int nCountLoop = 0; /* This nCounter is unique to the loop */

nCountLoop += 3; /* Increments (and prints) the loop’s nCounter */

40

 

Data Types, Constants, Variables, and Arrays

C C C

 

 

C2C

 

 

C C C

 

nLife += nCounter;

C

 

 

 

 

printf(“nCountLoop is = %d\n”, nCountLoop);

 

 

}

 

 

 

while (++nCounter < 10); /* Increments the function’s nCounter */

 

 

 

DisplayLife();

 

 

 

printf(“Ended, nCounter is = %d\n”, nCounter);

 

 

 

return (0);

 

 

}

 

 

 

void DisplayLife()

 

 

{

 

 

 

printf(“DisplayLife(), nLife = %d?\n”, nLife);

 

 

}

 

 

 

 

 

 

In LIFETIME.C, thevariable nLife is known to both main() and DisplayLife(). This sharing of the variable is an acceptable programming practice and is commonly used as outlined previously.

In the preceding example, if the declaration of nLife had been the following:

static int nLife = {5}; // Initialize to 5, default is zero.

the result would have been the same. The reason is that only one source file is in this program; therefore, nLife had to be visible in only one file. Whenever possible, remember to make your external variables static: If they are known in only one source file, they are much less likely to be modified unintentionally by another function in a different source file.

Type Casting

This chapter has referred to type casting, but what is a cast? A cast is C’s way of converting a variable of one type to another type. This topic is very important when

41

Part I • Honing Your C Skills

errors and misuse of a variable’s types occur. Nothing is more disastrous in a C program than inadvertently assigning a pointer to an integer using a cast and not catching the error.

Won’t the compiler give a message? No. If you cast one type of variable to a different type, the compiler assumes that you know what you are doing, and it says nothing. There is a time and a place for a cast. Before using one, however, be sure to look carefully at your code and determine that the effect of the cast (or the lack of the cast) is what you want and expect.

Listing 2.7 shows the CASTS.C program. A number of variables, all initialized, are in this program. First, the initialized values of each variable are printed, a few assignments are made, and then the result of these assignments is printed.

Listing 2.7. CASTS.C.

/* CASTS, written 15 May 1992 by Peter D. Hipson */ /* Using casts to change a data type. */

#include <stdio.h> // Make includes first part of file #include <string.h>

int main(void); // Define main() and the fact that this program doesn’t // use any passed parameters.

int main()

{

 

 

float

fValue

= 123.0F;

double

dValue

= 987.0;

long double ddValue

= 123123123123.0L;

int

nInteger

= 12345;

int

nIntegerAgain = 12345;

long

lLong

= 987;

unsigned

long ulLong = 987;

char

cChar

= ‘A’;

printf(“ fValue %f \n dValue %lf \n ddValue %Lf \n “ “nInteger %d \n lLong %ld \n ulLong %lu \n cChar %c\n”, fValue,

42

Data Types, Constants, Variables, and Arrays

dValue,

ddValue,

nInteger,

lLong,

ulLong,

cChar);

/* These assignment statements generate a warning message about type conversion. */

nInteger

= dValue;

lLong

= ddValue;

ulLong

= ddValue;

cChar

= nIntegerAgain;

printf(“\n fValue %f \n dValue %lf \n ddValue %Lf \n “ “nInteger %d \n lLong %ld \n ulLong %lu \n cChar %c\n”, fValue,

dValue,

ddValue,

nInteger,

lLong,

ulLong,

cChar);

/* With a cast, there is no warning message; however, the conversion is the same */

nInteger

= (int)dValue;

lLong

= (long)ddValue;

ulLong

= (unsigned long)ddValue;

cChar

= (char)nIntegerAgain;

printf(“\n fValue %f \n dValue %lf \n ddValue %Lf \n “ “nInteger %d \n lLong %ld \n ulLong %lu \n cChar %c\n”, fValue,

dValue,

ddValue,

nInteger,

lLong,

ulLong,

C C C

C2C C

C C C

continues

43

Part I • Honing Your C Skills

Listing 2.7. continued

cChar);

printf(“\nNotice that ‘lLong’ and ‘ulLong’” “both have the wrong value.\n”);

return (0);

}

After compiling and running CASTS.C, you get the following result:

fValue 123.000000 dValue 987.000000

ddValue 123123123123.000000 nInteger 12345

lLong 987 ulLong 987 cChar A

fValue 123.000000 dValue 987.000000

ddValue 123123123123.000000 nInteger 987

lLong -1430928461 ulLong 2864038835 cChar 9

fValue 123.000000 dValue 987.000000

ddValue 123123123123.000000 nInteger 987

lLong -1430928461 ulLong 2864038835 cChar 9

Notice that ‘lLong’ and ‘ulLong’ both have the wrong value.

You may want to know how ulLong managed to get such a strange value. Your first guess probably is that it should have received the least-significant digits from ddValue; there seems to be no relationship, however, between the value 123123123123

44

Data Types, Constants, Variables, and Arrays

C C C

 

C2C

 

C C C

 

C

and the result held in ulLong of 2864038835. The difference is easy to explain, though, when you look at the hex values of the converted number. The value 123123123123 is too large to store in a single 32-bit unsigned (or signed) integer. The hex representation of 123123123123 is 1C AA B5 C3 B3, a value that requires five bytes to store. Because ulLong has only four bytes, the leading digits, 1C, are truncated, leaving the result that is assigned to ulLong: AA B5 C3 B3 (2864038835 in decimal).

This same type of truncation happens when a short int is assigned a value that was stored in a long int that was too large. For example, if the value 123123123 is stored in ulLong, when it is assigned to an unsigned integer the result is 46515 (see Table 2.6).

Table 2.6. Examples of conversions of C data types.

Original

Original

Original

 

Result

Result

data type

in decimal

in hex

Conversion

in hex

in decimal

 

 

 

 

 

 

long int

123123123

0x756B5B3

To short

0xB5B3

46515

 

 

 

int, by

 

 

 

 

 

truncating

 

 

 

 

 

(the leading

 

 

 

 

 

0x756 is

 

 

 

 

 

dropped).

 

 

short

12345

0x3039

To char by

0x39

‘9’

int

 

 

truncating

 

 

 

 

 

and type

 

 

 

 

 

change (the

 

 

 

 

 

leading 0x30

 

 

 

 

 

is dropped).

 

 

long

123123123123

0x1CAAB5C3B3

Convert to

0xAAB5C3B3

2864038835

double

 

 

integer,

 

 

 

 

 

and truncate

 

 

 

 

 

(the leading

 

 

 

 

 

0x1C is

 

 

 

 

 

dropped).

 

 

 

 

 

 

 

 

As shown in Table 2.6, it’s important to remember that truncation occurs using the internal format of the number, not the number you see and use. It is easy to lose the number you had, and if you are changing types (such as from integer to char), the result can be difficult to predict.

45

Part I • Honing Your C Skills

Casts have their place in C programming. Because your goal should be to have your program compile with no warning messages, a cast can sometimes be the only way to suppress a warning.

When a cast is used on a parameter used in a function call, the effect is predictable: First, the variable is converted to the correct type, and then it is passed. If you have prototyped the function correctly, the compiler knows the data types of the parameters and ensures that the conversions are completed, giving whatever warnings are appropriate. If no parameter types are provided with the prototype or the prototype is missing, the compiler doesn’t know the correct types, makes no conversions for you, and issues only a missing prototype message.

Arrays

Arrays are collections of identical data objects, known by a common name and addressable either as a group or as a single object. Any data object that can be defined can be defined as an array.

Declaration of Arrays

Like a single data object, arrays have to be declared. The process of declaring an array is not difficult. You must, however, provide the compiler with some more information. You must tell how many of the desired data objects will be found in the array. For example, an array of int may be defined as

int nArray[15];

In this declaration, an array of integers has been created (remember that a declaration allocates memory). The first member in the array is addressed as nArray[0], and the final member is addressed as nArray[14]. Here’s an example of one of the most common coding errors:

#define

MAX_SIZE

20

int

nArray[MAX_SIZE];

int

i;

 

/* Other lines of code */

46

Data Types, Constants, Variables, and Arrays

C C C

 

C2C

 

C C C

 

C

for (i = 1; i <= MAX_SIZE; i++)

{

nArray[i] = i;

}

In the preceding fragment, the array element nArray[15] is initialized. Your program crashes because there is no element 15. The probable result is that some part of the program (often much later past the loop) that probably is not related to the failed part either produces incorrect results or simply crashes and dies. Also, the array element nArray[0] is never initialized because the loop starts with the second element in the array.

When a for() loop is used to initialize an array, always make sure that the following two statements are true:

1.The initial index value is zero (unless there is a valid reason for some other starting value).

2.When the array is being tested to the end, the test does not exceed the number of elements defined.

An example of the preceding loop being written correctly shows that the first element is initialized correctly and thatthe loop ends with the last element, nArray[14]:

for (i = 0; i < MAX_SIZE; i++)

{

nArray[i] = i;

}

Working with arrays can be difficult, especially when their bounds are exceeded. Many C implementations have little or no array bound checking. Generally, you should be sure that you have not exceeded the bounds of any arrays in your program.

Definition of an Array

An array can be declared with the following line:

int nArray[15];

When an array is external (defined in a different source file), it must be defined in any other source files that may need to access it. Because you don’t want the compiler to reallocate the storage for an array, you must tell the compiler that the array is

47

Part I • Honing Your C Skills

allocated externally and that you want only to access the array. To do this, you use an array definition, which might look like this:

extern int

nArray[];

This statement tells the compiler two important things:

1.The array has been declared (and storage allocated) in a different source file.

2.The size of the array is unknown.

Because the compiler knows only what you tell it (the compiler doesn’t search your source files to find where nArray[] was declared), it needs at least the name of the array and its type (so that the array can be indexed properly). Although it’s not necessary, especially in dealing with single-dimensional arrays, to tell the compiler the number of elements in an array, the compiler has no way of knowing where the end of the array is. You must make sure the array is used properly and you don’t exceed the bounds of the array.

If you choose to use the following definition:

extern int

nArray[MAX_SIZE];

you will tell the compiler at least the number of elements in the array. This is a good start in being able to ensure that you have not exceeded the bounds of the array. Again, note that the majority of C compilers (whether ANSI or not) do not check array (or string) bounds.

Array Indexing

When C stores an array in memory, it uses a rather complex set of pointers. Generally, you have to consider only that a block of memory has been allocated for the array. Then you can work with this memory and let C do the address computations for you.

At times, however, it’s necessary to work with the array as a single object. The most common time is when the array must be passed to a function. The most common occurrence of arrays passing to functions is when you pass a string to a character function, such as C’s strlen() function.

Let’s look at a simple program that creates one-, two-, and three-dimensional strings. ARRAY1, in Listing 2.8, creates three arrays, initializes them using the standard C array-subscripting techniques, and then accesses the members in the string using an alternative array indexing method. (I’m not saying that you should use this method.)

48