C Primer Plus Notes

35
Chapter 3 Data and C Bits, Bytes, and Words The smallest unit of memory is called a bit. The byte is the usual unit of computer memory. For nearly all machines, a byte is 8 bits. A word is the natural unit of memory for a given computer design. Initializing a Variable To initialize a variable means to assign it a starting, or initial, value. Printing int Values You can use the printf() function to print int types. The %d notation is used to indicate just where in a line the integer is to be printed. The %d is called a format specifier because it indicates the form that printf() uses to display a value. Each %d in the format string must be matched by a corresponding int value in the list of items to be printed. That value can be an int variable, an int constant, or any other expression having an int value. Octal and Hexadecimal In C, special prefixes indicate which number base you are using. A prefix of 0x or 0X (zero-ex) means that you are specifying a hexadecimal value, so 16 is written as 0x10, or 0X10, in hexadecimal. Similarly, a 0 (zero) prefix means that you are writing in octal. For example, the decimal value 16 is written as 020 in octal. Displaying Octal and Hexadecimal To display an integer in octal notation instead of decimal, use %o instead of %d. To display an integer in hexadecimal, use %x. Page 1 of 35

Transcript of C Primer Plus Notes

Page 1: C Primer Plus Notes

Chapter 3 Data and C

Bits, Bytes, and Words

The smallest unit of memory is called a bit.

The byte is the usual unit of computer memory. For nearly all machines, a byte is 8 bits.

A word is the natural unit of memory for a given computer design.

Initializing a Variable

To initialize a variable means to assign it a starting, or initial, value.

Printing int Values

You can use the printf() function to print int types. The %d notation is used to indicate just where in a line the integer is to be printed. The %d is called a format specifier because it indicates the form that printf() uses to display a value. Each %d in the format string must be matched by a corresponding int value in the list of items to be printed. That value can be an int variable, an int constant, or any other expression having an int value.

Octal and Hexadecimal

In C, special prefixes indicate which number base you are using. A prefix of 0x or 0X (zero-ex) means that you are specifying a hexadecimal value, so 16 is written as 0x10, or 0X10, in hexadecimal. Similarly, a 0 (zero) prefix means that you are writing in octal. For example, the decimal value 16 is written as 020 in octal.

Displaying Octal and Hexadecimal

To display an integer in octal notation instead of decimal, use %o instead of %d. To display an integer in hexadecimal, use %x. If you want to display the C prefixes, you can use specifiers %#o, %#x, and %#X to generate the 0, 0x, and 0X prefixes, respectively.

Printing Characters

The printf() function uses %c to indicate that a character should be printed. Recall that a character variable is stored as a 1-byte integer value. Therefore, if you print the value of a char variable with the usual %d specifier, you get an integer. The %c format specifier tells printf() to display the character that has that integer as its code value.

Page 1 of 25

Page 2: C Primer Plus Notes

Printing Floating-Point Values

The printf() function uses the %f format specifier to print type float and double numbers using decimal notation, and it uses %e to print them in exponential notation. If your system supports the C99 hexadecimal format for floating-point numbers, you can use a or A instead of e or E. The long double type requires the %Lf, %Le, and %La specifiers to print that type. Note that both float and double use the %f, %e, or %a specifier for output. That's because C automatically expands type float values to type double when they are passed as arguments to any function, such as printf(), that doesn't explicitly prototype the argument type.

How to Declare a Simple Variable

type-specifier variable-name;

Page 2 of 25

Page 3: C Primer Plus Notes

Type Sizes

Note that using %d to display a float value doesn't convert the float value to the nearest int; instead, it displays what appears to be garbage. Similarly, using %f to display an int value doesn't convert an integer value to a floating-point value. Also, the results you get for too few arguments or the wrong kind of argument differ from platform to platform.

Chapter 4 Character Strings and Formatted Input/Output

#define

The preprocessor uses #include to incorporate information from another file. The preprocessor also lets you define constants.

#define TAXRATE 0.015

When your program is compiled, the value 0.015 will be substituted everywhere you have used TAXRATE. This is called a compile-time substitution. By the time you run the program, all the substitutions have already been made. Such defined constants are often termed manifest constants. The general form is as follows:

#define NAME value

The printf() Function

The instructions you give printf() when you ask it to print a variable depend on the variable type. For example, we have used the %d notation when printing an integer and the %c notation when printing a character. These notations are called conversion specifications because they specify how the data is to be converted into displayable form.

Page 3 of 25

Page 4: C Primer Plus Notes

Conversion Specification Modifiers for printf()

You can modify a basic conversion specification by inserting modifiers between the % and the defining conversion character.

Page 4 of 25

Page 5: C Primer Plus Notes

Page 5 of 25

Page 6: C Primer Plus Notes

Page 6 of 25

Page 7: C Primer Plus Notes

The Return Value of printf()The printf() function also has a return value; it returns the number of characters it printed. If there is an output error, printf() returns a negative value.

Using scanf()

scanf()converts string input into various forms: integers, floating-point numbers, characters, and C strings. It is the inverse of printf(), which converts integers, floating-point numbers, characters, and C strings to text that is to be displayed onscreen.

Like printf(), scanf() uses a control string followed by a list of arguments. The control string indicates the destination data types for the input stream of characters. The chief difference is in the argument list. The printf() function uses variable names, constants, and expressions. The scanf() function uses pointers to variables.

If you use scanf() to read a value for one of the basic variable types we've discussed, precede the variable name with an &.

If you use scanf() to read a string into a character array, don't use an &.

Page 7 of 25

Page 8: C Primer Plus Notes

The scanf() Return Value

The scanf() function returns the number of items that it successfully reads. If it reads no items, which happens if you type a nonnumeric string when it expects a number, scanf() returns the value 0. It returns EOF when it detects the condition known as "end of file." (EOF is a special value defined in the stdio.h file. Typically, a #define directive gives EOF the value –1.)

The * Modifier with printf() and scanf()

Both printf() and scanf() can use the * modifier to modify the meaning of a specifier, but they do so in dissimilar fashions. First, let's see what the * modifier can do for printf().

Suppose that you don't want to commit yourself to a field width in advance but rather you want the program to specify it. You can do this by using * instead of a number for the field width, but

Page 8 of 25

Page 9: C Primer Plus Notes

you also have to use an argument to tell what the field width should be. That is, if you have the conversion specifier %*d, the argument list should include a value for * and a value for d. The technique also can be used with floating-point values to specify the precision as well as the field width.

printf("The number is :%*d:\n", width, number);

The * serves quite a different purpose for scanf(). When placed between the % and the specifier letter, it causes that function to skip over corresponding input.

scanf("%*d %*d %d", &n);

Chapter 5 Operators, Expressions, and Statements

Expressions

An expression consists of a combination of operators and operands. (An operand is what an operator operates on.) Every expression has a value.

Statements

Statements are the primary building blocks of a program. A program is a series of statements with some necessary punctuation. A statement is a complete instruction to the computer. In C, statements are indicated by a semicolon at the end.

C considers any expression to be a statement if you append a semicolon. More typically, statements change values and call functions. Although a statement (or, at least, a sensible statement) is a complete instruction, not all complete instructions are statements.

x = 6 + (y = 5);

In it, the subexpression y = 5 is a complete instruction, but it is only part of the statement. Because a complete instruction is not necessarily a statement, a semicolon is needed to identify instructions that truly are statements.

Type Conversions

The basic rules are

1. When appearing in an expression, char and short, both signed and unsigned, are automatically converted to int or, if necessary, to unsigned int. (If short is the same size as int, unsigned short is larger than int; in that case, unsigned short is converted to unsigned int.) Under K&R C, but not under current C, float is

Page 9 of 25

Page 10: C Primer Plus Notes

automatically converted to double. Because they are conversions to larger types, they are called promotions.

2. In any operation involving two types, both values are converted to the higher ranking of the two types.

3. The ranking of types, from highest to lowest, is long double, double, float, unsigned long long, long long, unsigned long, long, unsigned int, and int. One possible exception is when long and int are the same size, in which case unsigned int outranks long. The short and char types don't appear in this list because they would have been already promoted to int or perhaps unsigned int.

4. In an assignment statement, the final result of the calculations is converted to the type of the variable being assigned a value. This process can result in promotion, as described in rule 1, or demotion, in which a value is converted to a lower-ranking type.

5. When passed as function arguments, char and short are converted to int, and float is converted to double.

The Cast Operator

It is possible for you to demand the precise type conversion that you want or else document that you know you're making a type conversion. The method for doing this is called a cast and consists of preceding the quantity with the name of the desired type in parentheses. The parentheses and type name together constitute a cast operator. This is the general form of a cast operator:

(type)

Chapter 8 Character Input/Output and Input Validation

Buffer

Why have buffers? First, it is less time-consuming to transmit several characters as a block than to send them one by one. Second, if you mistype, you can use your keyboard correction features to fix your mistake. When you finally press Enter, you can transmit the corrected version.

Chapter 9 Functions

Returning a Value from a Function with return

You can also use a statement like this:

return;

Page 10 of 25

Page 11: C Primer Plus Notes

It causes the function to terminate and return control to the calling function. Because no expression follows return, no value is returned, and this form should be used only in a type void function.

Function Types

Functions should be declared by type. A function with a return value should be declared the same type as the return value. Functions with no return value should be declared as type void.

To use a function correctly, a program needs to know the function type before the function is used for the first time. One way to accomplish this is to place the complete function definition ahead of its first use. However, this method could make the program harder to read. Also, the functions might be part of the C library or in some other file. Therefore, you generally inform the compiler about functions by declaring them in advance.

Recursion

C permits a function to call itself. This process is termed recursion.

Recursion Pros and Cons

Recursion has its good points and bad points. One good point is that recursion offers the simplest solution to some programming problems. One bad point is that some recursive algorithms can rapidly exhaust a computer's memory resources. Also, recursion can be difficult to document and maintain.

Chapter 10 Arrays and Pointers

Arrays

Recall that an array is composed of a series of elements of one data type. You use declarations to tell the compiler when you want an array. An array declaration tells the compiler how many elements the array contains and what the type is for these elements.

Pointers

The value of a pointer is the address of the object to which it points. How the address is represented internally is hardware dependent. Many computers, including PCs and Macintoshes, are byte addressable, meaning that each byte in memory is numbered sequentially. Here, the address of a large object, such as type double variable, typically is the address of the first byte of the object.

Applying the * operator to a pointer yields the value stored in the pointed-to object. Adding 1 to the pointer increases its value by the size, in bytes, of the pointed-to type.

Page 11 of 25

Page 12: C Primer Plus Notes

Using const with Formal Parameters

int sum(const int ar[], int n); /* prototype */

It's important to understand that using const this way does not require that the original array be constant; it just says that the function has to treat the array as though it were constant. Using const this way provides the protection for arrays that passing by value provides for fundamental types; it prevents a function from modifying data in the calling function. In general, if you write a function intended to modify an array, don't use const when declaring the array parameter. If you write a function not intended to modify an array, do use const when declaring the array parameter.

Pointers to constants can't be used to change values. A pointer-to-constant is normally used as a function parameter to indicate that the function won't use the pointer to change data.

It's valid to assign the address of either constant data or nonconstant data to a pointer-to-constant. However, only the addresses of nonconstant data can be assigned to regular pointers.

Constant pointer: you can declare and initialize a pointer so that it can't be made to point elsewhere. Such a pointer can still be used to change values, but it can point only to the location originally assigned to it.

Pointers and Multidimensional Arrays

The pointer notation equivalent of zippo[2][1] is *(*(zippo+2) + 1).

Variable-Length Arrays (VLAs)

The term variable in variable-length array does not mean that you can modify the length of the array after you create it. Once created, a VLA keeps the same size. What the term variable does mean is that you can use a variable when specifying the array dimensions.

int quarters = 4;int regions = 5;double sales[regions][quarters]; // a VLA

int sum2d(int rows, int cols, int ar[rows][cols]); // ar a VLA

Note that the first two parameters (rows and cols) are used as dimensions for declaring the array parameter ar. Because the ar declaration uses rows and cols, they have to be declared before ar in the parameter list.

Variable-length arrays also allow for dynamic memory allocation. This means you can specify the size of the array while the program is running. Regular C arrays have static memory allocation, meaning the size of the array is determined at compile time. That's because the array sizes, being constants, are known to the compiler.

Page 12 of 25

Page 13: C Primer Plus Notes

Chapter 11 Character Strings and String Functions

A character string is a char array terminated with a null character (\0).

Array and Pointer Differences

char heart[] = "I love Tillie!";char *head = "I love Millie!";

The chief difference is that the array name heart is a constant, but the pointer head is a variable. Only the pointer version can use the increment operator.

A compiler can choose to represent all identical string literals with a single copy in memory. For example, the following statements could all refer to a single memory location of string "Klingon":

char * p1 = "Klingon";p1[0] = 'F'; // ok?printf("Klingon");printf(": Beware the %ss!\n", "Klingon");

That is, the compiler can replace each instance of "Klingon" with the same address. If the compiler uses this single-copy representation and allows changing p1[0] to 'F', that would affect all uses of the string, so statements printing the string literal "Klingon" would actually display "Flingon":

Flingon: Beware the Flingons!

The scanf() Function

You've used scanf() with the %s format before to read a string. The chief difference between scanf() and gets() lies in how they decide when they have reached the end of the string: scanf() is more of a "get word" than a "get string" function. The gets() function, as you've seen, takes in all the characters up to the first newline. The scanf() function has two choices for terminating input. For either choice, the string starts at the first non-whitespace character encountered. If you use the %s format, the string runs up to (but not including) the next whitespace character (blank, tab, or newline). If you specify a field width, as in %10s, the scanf() collects up to 10 characters or up to the first whitespace character, whichever comes first.

The Careful Choice: strncpy()

The strcpy() function shares a problem with gets()—neither checks to see whether the source string actually fits in the target string. The safer way to copy strings is to use strncpy(). It takes a third argument, which is the maximum number of characters to copy.

Page 13 of 25

Page 14: C Primer Plus Notes

char *strncpy(char * s1, const char * s2, size_t n);

The sprintf() Function

The sprintf() function is declared in stdio.h instead of string.h. It works like printf(), but it writes to a string instead of writing to a display. Therefore, it provides a way to combine several elements into a single string. The first argument to sprintf() is the address of the target string. The remaining arguments are the same as for printf()—a conversion specification string followed by a list of items to be written.

String-to-Number Conversions

you must convert the string to a number. If the number is an integer, you can use the atoi() function (for alphanumeric to integer). It takes a string as an argument and returns the corresponding integer value.

The atoi() function still works if the string only begins with an integer. In that case, it converts characters until it encounters something that is not part of an integer. For example, atoi("42regular") returns the integer 42. What if the command line is something like hello what? On the implementations we've used, the atoi() function returns a value of 0 if its argument is not recognizable as a number. However, the ANSI standard says the behavior in that case is undefined.

We include the stdlib.h header because, under ANSI C, it contains the function declaration for atoi(). That header file also includes declarations for atof() and atol(). The atof() function converts a string to a type double value, and the atol() function converts a string to a type long value. They work analogously to atoi(), so they are type double and long, respectively.

Chapter 12 Storage Classes, Linkage, and Memory Management

Storage Classes

C provides five different models, or storage classes, for variables. You can describe a variable (or, more generally, a data object) in terms of its storage duration, which is how long it stays in memory, and its scope and its linkage, which together indicate which parts of a program can use it by name.

Scope

Scope describes the region or regions of a program that can access an identifier. A C variable has one of the following scopes: block scope, function prototype scope, or file scope.

Page 14 of 25

Page 15: C Primer Plus Notes

Linkage

A C variable has one of the following linkages: external linkage, internal linkage, or no linkage. Variables with block scope or prototype scope have no linkage. That means they are private to the block or prototype in which they are defined. A variable with file scope can have either internal or external linkage. A variable with external linkage can be used anywhere in a multifile program. A variable with internal linkage can be used anywhere in a single file.

Storage Duration

A C variable has one of the following two storage durations: static storage duration or automatic storage duration. If a variable has static storage duration, it exists throughout program execution. Variables with file scope have static storage duration. Note that for file scope variables, the keyword static indicates the linkage type, not the storage duration. A file scope variable declared using static has internal linkage, but all file scope variables, using internal linkage or external linkage, have static storage duration.

Variables with block scope normally have automatic storage duration. These variables have memory allocated for them when the program enters the block in which they are defined, and the memory is freed when the block is exited. The idea is that memory used for automatic variables is a workspace or scratch pad that can be reused.

Register Variables

Variables are normally stored in computer memory. With luck, register variables are stored in the CPU registers or, more generally, in the fastest memory available, where they can be accessed and manipulated more rapidly than regular variables. Because a register variable may be in a

Page 15 of 25

Page 16: C Primer Plus Notes

register rather than in memory, you can't take the address of a register variable. In most other respects, register variables are the same as automatic variables.

Static Variables with Block Scope

The name static variable sounds like a contradiction, like a variable that can't vary. Actually, static means that the variable stays put. Variables with file scope automatically (and necessarily) have static storage duration. It's also possible to create local variables—that is, variables having block scope that have static storage. These variables have the same scope as automatic variables, but they don't vanish when the containing function ends its job. That is, such variables have block scope, no linkage, but static storage duration. The computer remembers their values from one function call to the next. Such variables are created by declaring them in a block (which provides the block scope and lack of linkage) with the storage class specifier static (which provides the static storage duration).

Static Variables with External Linkage

A static variable with external linkage has file scope, external linkage, and static storage duration. This class is sometimes termed the external storage class, and variables of this type are called external variables. You create an external variable by placing a defining declaration outside of any function. As a matter of documentation, an external variable can additionally be declared inside a function that uses it by using the extern keyword. If the variable is defined in another file, declaring the variable with extern is mandatory.

Initializing External Variables

Like automatic variables, external variables can be initialized explicitly. Unlike automatic variables, external variables are automatically initialized to zero if you don't initialize them. This rule applies to elements of an externally defined array, too. Unlike the case for automatic variables, you can use only constant expressions to initialize file scope variables.

Storage-Class Specifiers

You may have noticed that the meaning of the keywords static and extern depends on the context. The C language has five keywords that are grouped together as storage-class specifiers. They are auto, register, static, extern, and typedef. The typedef keyword doesn't say anything about memory storage, but it is thrown in for syntax reasons. In particular, you can use no more than one storage-class specifier in a declaration, so that means you can't use one of the other storage-class specifiers as part of a typedef.

Allocated Memory: malloc() and free()

The five storage classes have one thing in common. After you decide which storage class to use, the decisions about scope and storage duration follow automatically. Your choices obey the

Page 16 of 25

Page 17: C Primer Plus Notes

prepackaged memory management rules. There is, however, one more choice, one that gives you more flexibility. That choice is using library functions to allocate and manage memory.

The malloc() function takes one argument: the number of bytes of memory you want. Then malloc() finds a suitable block of free memory. The memory is anonymous; that is, malloc() allocates memory but it doesn't assign a name to it. However, it does return the address of the first byte of that block. Therefore, you can assign that address to a pointer variable and use the pointer to access the memory. If malloc() fails to find the required space, it returns the null pointer.

double * ptd;ptd = (double *) malloc(30 * sizeof(double));

The typecast to (double *) is optional in C but required in C++, so using the typecast makes it simpler to move a program from C to C++.

The Importance of free()

The amount of static memory is fixed at compile time; it does not change while the program is running. The amount of memory used for automatic variables grows and shrinks automatically as the program executes. But the amount of memory used for allocated memory just grows unless you remember to use free().

The calloc() Function

Another option for memory allotment is to use calloc(). A typical use looks like this:

long * newmem;newmem = (long *)calloc(100, sizeof (long));

Like malloc(), calloc() returns a pointer-to-char in its pre-ANSI version and a pointer-to-void under ANSI. You should use the cast operator if you want to store a different type. This new function takes two arguments, both of which should be unsigned integers (type size_t under ANSI). The first argument is the number of memory cells you want. The second argument is the size of each cell in bytes.

The calloc() function throws in one more feature: It sets all the bits in the block to zero.

Dynamic Memory Allocation and Variable-Length Arrays

One difference is that the VLA is automatic storage. One consequence of automatic storage is that the memory space used by the VLA is freed automatically when the execution leaves the defining block—in this case, when the vlamal() function terminates. Therefore, you don't have to worry about using free(). On the other hand, the array created using malloc() needn't have its access limited to one function.

Page 17 of 25

Page 18: C Primer Plus Notes

VLAs are more convenient for multidimensional arrays.

Storage Classes and Dynamic Memory Allocation

You might be wondering about the connection between storage classes and dynamic memory allocation. Let's look at an idealized model. You can think of a program as dividing its available memory into three separate sections: one for static variables with external linkage, internal linkage, and no linkage; one for automatic variables; and one for dynamically allocated memory.

The amount of memory needed for the static duration storage classes is known at compile time, and the data stored in this section is available as long as the program runs. Each variable of these classes comes into being when the program starts and expires when the program ends.

An automatic variable, however, comes into existence when a program enters the block of code containing the variable's definition and expires when its block of code is exited. Therefore, as a program calls functions and as functions terminate, the amount of memory used by automatic variables grows and shrinks. This section of memory is typically handled as a stack. That means new variables are added sequentially in memory as they are created and then are removed in the opposite order as they pass away.

Dynamically allocated memory comes into existence when malloc() or a related function is called, and it's freed when free() is called. Memory persistence is controlled by the programmer, not by a set of rigid rules, so a memory block can be created in one function and disposed of in another function. Because of this, the section of memory used for dynamic memory allocation can end up fragmented—that is, unused chunks could be interspersed among active blocks of memory. Using dynamic memory tends to be a slower process than using stack memory, however.

ANSI C Type Qualifiers

You've seen that a variable is characterized by both its type and its storage class. C90 has added two more properties: constancy and volatility. These properties are declared with the keywords const and volatile, which create qualified types. The C99 standard adds a third qualifier, restrict, designed to facilitate compiler optimizations.

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.

The restrict Type Qualifier

The restrict keyword enhances computational support by giving the compiler permission to optimize certain kinds of code. It can be applied only to pointers, and it indicates that a pointer is the sole initial means of accessing a data object.

Page 18 of 25

Page 19: C Primer Plus Notes

Chapter 14 Structures and Other Data Forms

Gaining Access to Structure Members

Structure_name.member_name

Member Access by Pointer

The first method, and the most common, uses a new operator, ->.

him->income is fellow[0].income if him == &fellow[0]

Structures or Pointer to Structures?

The two advantages of the pointer argument method are that it works on older as well as newer C implementations and that it is quick; you just pass a single address. The disadvantage is that you have less protection for your data. Some operations in the called function could inadvertently affect data in the original structure. However, the ANSI C addition of the const qualifier solves that problem.

One advantage of passing structures as arguments is that the function works with copies of the original data, which is safer than working with the original data. Also, the programming style tends to be clearer.

Character Arrays or Character Pointers in a Structure

If you want a structure to store the strings, use character array members, not pointer.

Union

A union is a type that enables you to store different data types in the same memory space (but not simultaneously).

Enumerated Types

You can use the enumerated type to declare symbolic names to represent integer constants. By using the enum keyword, you can create a new "type" and specify the values it may have. (Actually, enum constants are type int; therefore, they can be used wherever you would use an int.) The purpose of enumerated types is to enhance the readability of a program.

typedef

The typedef facility is an advanced data feature that enables you to create your own name for a type. It is similar to #define in that respect, but with three differences:

Page 19 of 25

Page 20: C Primer Plus Notes

Unlike #define, typedef is limited to giving symbolic names to types only and not to values.

The typedef interpretation is performed by the compiler, not the preprocessor. Within its limits, typedef is more flexible than #define.

When using typedef, bear in mind that it does not create new types; instead, it just creates convenient labels.

Chapter 15 Bit Fiddling

Signed Integers

The representation of signed numbers is determined by the hardware, not by C. Probably the simplest way to represent signed numbers is to reserve 1 bit, such as the high-order bit, to represent the sign. In a 1-byte value, this leaves 7 bits for the number itself. In such a sign-magnitude representation, 10000001 is –1 and 00000001 is 1. The total range, then, is –127 to +127.

One disadvantage of this approach is that it has two zeros: +0 and –0. This is confusing, and it also uses up two bit patterns for just one value.

The two's-complement method avoids that problem and is the most common system used today. We'll discuss this method as it applies to a 1-byte value. In that context, the values 0 through 127 are represented by the last 7 bits, with the high-order bit set to 0. So far, that's the same as the sign-magnitude method. Also, if the high-order bit is 1, the value is negative. The difference comes in determining the value of that negative number. Subtract the bit-pattern for a negative number from the 9-bit pattern 100000000 (256 expressed in binary), and the result is the magnitude of value. For example, suppose the pattern is 10000000. As an unsigned byte, it would be 128. As a signed value, it is negative (bit 7 is 1) and has a value of 100000000–10000000, or 10000000 (128). Therefore, the number is –128. (It would have been –0 in the sign-magnitude system.) Similarly, 10000001 is –127, and 11111111 is –1. The method represents numbers in the range –128 to +127.

The simplest method for reversing the sign of a two's-complement binary number is to invert each bit (convert 0s to 1s and 1s to 0s) and then add 1. Because 1 is 00000001, –1 is 11111110 + 1, or 11111111, just as you saw earlier.

The one's-complement method forms the negative of a number by inverting each bit in the pattern. For instance, 00000001 is 1 and 11111110 is –1. This method also has a –0: 11111111. Its range (for a 1-byte value) is –127 to +127.

Page 20 of 25

Page 21: C Primer Plus Notes

Masks

A mask is a bit pattern with some bits set to on (1) and some bits to off (0).

Usage: Toggling Bits

Toggling a bit means turning it off if it is on, and turning it on if it is off. You can use the bitwise EXCLUSIVE OR operator to toggle a bit.

Usage: Checking the Value of a Bitif ((flag & MASK) == MASK)

Bitwise Shift Operators

number << n; number >> n;

Bit Fields

The second method of manipulating bits is to use a bit field, which is just a set of neighboring bits within a signed int or an unsigned int. A bit field is set up with a structure declaration that labels each field and determines its width.

struct {unsigned int code1 : 2;unsigned int code2 : 2;unsigned int code3 : 8;

prcode;

Chapter 16 The C Preprocessor and the C Library

First Steps in Translating a Program

The compiler has to put a program through some translation phases before jumping into preprocessing. The compiler starts its work by mapping characters appearing in the source code to the source character set. Second, the compiler locates each instance of a backslash followed by a newline character and deletes them. Next, the compiler breaks the text into a sequence of preprocessing tokens(In basic terms, tokens are groups separated from each other by spaces) and sequences of whitespace and comments. Finally, the program is ready for the preprocessing phase, and the preprocessor looks for potential preprocessing directives, indicated by a # symbol at the beginning of a line.

Manifest Constants: #define

Each #define line (logical line, that is) has three parts. The first part is the #define directive itself. The second part is your chosen abbreviation, known as a macro. Some macros represent

Page 21 of 25

Page 22: C Primer Plus Notes

values; they are called object-like macros. (C also has function-like macros) The macro name must have no spaces in it, and it must conform to the same rules that C variables follow: Only letters, digits, and the underscore (_) character can be used, and the first character cannot be a digit. The third part (the remainder of the line) is termed the replacement list or body. When the preprocessor finds an example of one of your macros within your program, it almost always replaces it with the body. This process of going from a macro to a final replacement is called macro expansion.

Using Arguments with #define

By using arguments, you can create function-like macros that look and act much like functions. A macro with arguments looks very similar to a function because the arguments are enclosed within parentheses. Function-like macro definitions have one or more arguments in parentheses, and these arguments then appear in the replacement portion.

#define SQUARE(X) X *X

Macro or Function?

Many tasks can be done by using a macro with arguments or by using a function. Which one should you use? There is no hard-and-fast rule, but here are some considerations.

Macros are somewhat trickier to use than regular functions because they can have odd side effects if you are unwary. Some compilers limit the macro definition to one line, and it is probably best to observe that limit, even if your compiler does not.

The macro-versus-function choice represents a trade-off between time and space. A macro produces inline code; that is, you get a statement in your program. If you use the macro 20 times, you get 20 lines of code inserted into your program. If you use a function 20 times, you have just one copy of the function statements in your program, so less space is used. On the other hand, program control must shift to where the function is and then return to the calling program, and this takes longer than inline code.

Macros have an advantage in that they don't worry about variable types. (This is because they deal with character strings, not with actual values.) Therefore, the SQUARE(x) macro can be used equally well with int or float.

File Inclusion: #include

When the preprocessor spots an #include directive, it looks for the following filename and includes the contents of that file within the current file. The #include directive in your source code file is replaced with the text from the included file.

The #include directive comes in two varieties:

Page 22 of 25

Page 23: C Primer Plus Notes

#include <stdio.h> Filename in angle brackets

#include "mystuff.h" Filename in double quotation marks

On a Unix system, the angle brackets tell the preprocessor to look for the file in one or more standard system directories. The double quotation marks tell it to first look in your current directory (or some other directory that you have specified in the filename) and then look in the standard places.

The #undef Directive

The #undef directive "undefines" a given #define.

Note that the scope of a #define macro extends from the point it is declared in a file until it is the subject of an #undef directive or until the end of the file, whichever comes first. Also note that the position of the #define in a file will depend on the position of an #include directive if the macro is brought in via a header file.

Conditional Compilation

You can use the other directives mentioned to set up conditional compilations. That is, you can use them to tell the compiler to accept or ignore blocks of information or code according to conditions at the time of compilation.

The #ifdef, #else, and #endif Directives

The #ifdef directive says that if the following identifier (MAVIS) has been defined by the preprocessor, follow all the directives and compile all the C code up to the next #else or #endif, whichever comes first. If there is an #else, everything from the #else to the #endif is done if the identifier isn't defined.

The form #ifdef #else is much like that of the C if else. The main difference is that the preprocessor doesn't recognize the braces ({}) method of marking a block, so it uses the #else (if any) and the #endif (which must be present) to mark blocks of directives.

The #ifndef Directive

The #ifndef directive can be used with #else and #endif in the same way that #ifdef is. The #ifndef asks whether the following identifier is not defined; #ifndef is the negative of #ifdef.

The #if and #elif Directives

The #if directive is more like the regular C if. It is followed by a constant integer expression that is considered true if nonzero, and you can use C's relational and logical operators with it.

Page 23 of 25

Page 24: C Primer Plus Notes

You can use the #elif directive (not available in some older implementations) to extend an if-else sequence.

#line and #error

The #line directive lets you reset the line numbering and the filename as reported by the _ _LINE_ _ and _ _FILE_ _ macros.

The #error directive causes the preprocessor to issue an error message that includes any text in the directive. If possible, the compilation process should halt.

#pragma

Modern compilers have several settings that can be modified by command-line arguments or by using an IDE menu. The #pragma lets you place compiler instructions in the source code.

Inline Functions

Normally, a function call has overhead. That means it takes execution time to set up the call, pass arguments, jump to the function code, and return. Reducing that time is one of the justifications for using function-like macros. C99 has an alternative, inline functions. So making a function an inline function may shortcut the usual function call mechanism, or it may do nothing at all.

The way to create an inline function is to use the function specifier inline in the function declaration. Usually, inline functions are defined before the first use in a file, so the definition also acts as a prototype.

Because an inline function doesn't have a separate block of code set aside for it, you can't take its address. (Actually, you can take the address, but then the compiler will generate a non-inline function.) Also, an inline function may not show up in a debugger.

For the compiler to make inline optimizations, it has to know the contents of the function definition. This means the inline function definition has to be in the same file as the function call. For this reason, an inline function ordinarily has internal linkage. Therefore, if you have a multifile program, you need an inline definition in each file that calls the function. The simplest way to accomplish this is to put the inline function definition in a header file and then include the header file in those files that use the function. An inline function is an exception to the rule of not placing executable code in a header file. Because the inline function has internal linkage, defining one in several files doesn't cause problems.

The exit() and atexit() Functions

The exit() function is invoked automatically upon return from main(). The ANSI standard has added a couple nice features that we haven't used yet. The most important addition is that you can specify particular functions to be called when exit() executes. The atexit() function

Page 24 of 25

Page 25: C Primer Plus Notes

provides this feature by registering the functions to be called on exit; the atexit() function takes a function pointer as its argument.

The qsort() Function

The "quick sort" method is one of the most effective sorting algorithms, particularly for larger arrays. Developed by C. A. R. Hoare in 1962, it partitions arrays into ever smaller sizes until the element level is reached. First, the array is divided into two parts, with every value in one partition being less than every value in the other partition. This process continues until the array is fully sorted.

The name for the C implementation of the quick sort algorithm is qsort(). The qsort() function sorts an array of data objects.

The Assert Library

The assert library, supported by the assert.h header file, is a small one designed to help with debugging programs. It consists of a macro named assert(). It takes as its argument an integer expression. If the expression evaluates as false (nonzero), the assert() macro writes an error message to the standard error stream (stderr) and calls the abort() function, which terminates the program. (The abort() function is prototyped in the stdlib.h header file.) The idea is to identify critical locations in a program where certain conditions should be true and to use the assert() statement to terminate the program if one of the specified conditions is not true. Typically, the argument is a relational or logical expression. If assert() does abort the program, it first displays the test that failed, the name of the file containing the test, and a line number.

Page 25 of 25