Essence%2BOf%2BC - THE ESSENCE OF C A Summary of the C...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: THE ESSENCE OF C A Summary of the C Language Compiled by K. J. Bricknell 1 OVERVIEW Origins and Nature of C, Libraries, Linking, Compilation, Memory Map, Language Fundamentals, Keywords Origins of C C was developed at the Bell Laboratories in the early 70s by UNIX pioneer Dennis Ritchie and was first implemented on a DEC PDP-11 using the UNIX operating system. C, like all the structured languages (Pascal, PL/I, etc) can trace its ancestry back to ALGOL. For many years, the de-facto standard for C was the version supplied with UNIX Version 5, as described by Kernighan and Ritchie. To achieve standardisation, ANSI established a committee in 1983 to define the C language once and for all. Given this lineage, most current literature on the language covers both the old UNIX version of C and the ANSI standard. The Nature of C C is considered to be a middle level language because it combines elements of high-level languages with the functionalism of assembly language. Because it allows the direct manipulation of bits, bytes, words and pointers, it is well suited to system-level programming. C is a structured language in that it has the ability to section off and "hide" from the rest of the program all information and instructions required to perform a specific task. C is a primitive language in that it has none of the built-in features for input and output, string manipulation and higher mathematics found in other languages. C's features come in function libraries. C is a compiled language. C compilers read the source code and convert it to object code, sometimes referred to as binary or machine code. Libraries and Linking C compilers come with a standard C library of functions. These functions perform commonly needed tasks. ANSI specifies a minimal set of functions; however, most compilers provide many others. When a function which is not part of the C program is called, the compiler "remembers" its name. Later, the linker combines your code with the object code in the standard library. Some functions defined in the original UNIX version of C are not defined in ANSI because they are redundant. Essence of C 1 Functions written by the programmer may also be placed in a library. Separate Compilation C allows a program to be contained in separate files, which are compiled separately before being linked. The advantage of separate compilation is that, if the programmer changes the code in one file, he does not need to recompile the whole program. This saves time. C's Memory Map A compiled C program creates and uses four logically distinct regions of memory, viz: Program code. Global variables. The stack, which holds the return addresses of function calls, arguments to functions and local variables, and saves the current state of the CPU. The heap, a region of free memory the program can use via C's dynamic allocation functions for things liked linked lists, etc. C Language Fundamentals Any section of a C program is likely to contain the following elements: Variables (created by the programmer). Function calls (created by the programmer or provided in function libraries by the compiler). Operators, which are part of the language and which are used for assignment, arithmetic, comparison, etc. Control statements, which are also part of the language and which control the order in which functions and statements are executed. ANSI C Keywords The following is the complete list of C keywords, itemized by usage, and as defined by the ANSI standard. Of these enum , const , signed , void , and volatile were added by the ANSI committee. Category Data Types Data Type Modifiers Access Type Modifiers Storage Class Specifiers User-Defined Data Types Selection (Conditional) Iteration Jump Compile Time Operator char long const auto enum case do break sizeof double short volatile extern struct default for goto register typedef else while return continue static union if switch Keyword float signed int unsigned void 2 Essence of C 2 C EXPRESSIONS Data Types, Identifiers, Variables, Storage Class Specifiers, Access Type Modifiers, Constants, Operators The Five Basic Data Types There are five basic data types in C. These are: Data Type character integer floating point double floating point valueless Keyword char int float double void Size Generally one byte Generally four bytes Generally four bytes Generally eight bytes The type void either explicitly declares a function as returning no value or creates generic pointers. Modifying the Basic Types The first four basic data type keywords may be preceded by modifiers. The four modifiers are: Modifier long short signed unsigned Applied To int and double int char and int char and int The use of signed on integers is redundant. Sign Bits. In general, negative numbers are represented using two's complement, which reverses all bits in the number except the sign bit. If the sign bit is zero, the number is positive. Identifier Names ANSI calls the names of variables, functions, labels and various other user-defined objects identifiers. The additional term external names refers to function names and global variables shared between files. Significant Characters. If an identifier is not used in an external link process, the first 31 characters are significant, otherwise (in ANSI) at least the first six will be significant. In C, upper and lowercase are treated as distinct. Hence identifiers. count , Count , and COUNT are three separate Essence of C 3 Variables Variables are declared in three basic places: Inside functions (local (or auto) variables). In the definition of function parameters (formal parameters). Outside of all functions (global variables). Local (Auto) Variables Local variables are not "known" outside the code block in which they are declared. (A code block is a block of code beginning and terminating with curly braces.) They are created only when the code block in which they are declared is entered and are destroyed upon exit from that block. For this reason, they cannot retain their values between calls to that code block. (However, as will be seen, the compiler may be directed to retain their values through the use of the static storage class specifier (see below).) Unless otherwise specified, local variables are stored on the stack. Initialization. Local variables may be initialized with some known value, which will be assigned to the variable each time the block of code in which they are declared is entered. The keyword auto . auto is virtually never used since all non-global variables are, by default, assumed to be Formal Parameters The variables which accept the values passed to a function are called formal parameters. Formal parameters behave like any other local variables. As with local variables, assignments may be made to formal parameters. Function Prototypes. The ANSI standard provides for function prototypes which help verify that the arguments used to call the function are compatible with its formal parameters. Global Variables Global variables are "known" by the whole program and hold their value throughout program execution. They are created by being declared outside of any function. If a global and local variable share the same name, the local variable holds sway inside the function in which it is declared and the global variable will not be affected. Global variables are stored in a fixed region of memory set aside by the compiler. They should be used only where necessary because, apart from memory considerations, unnecessary use of global variables is inconsistent with the main characteristic of structured languages, namely, the compartmentalization of code and data. Storage Class Specifiers Storage class specifiers, of which there are four in C, tell the compiler how to store the subsequent variable. One of these specifiers (auto ) is virtually never used since, as previously stated, all non-global variables are, by default, assumed to be auto. The remaining three specifiers are as follows: extern Because C allows separately compiled modules, and because a global variable must be declared in only one of those files, there must be some way of telling all the files about the existence of that global variable. The extern specifier tells the compiler that the variables that follow have been declared in 4 Essence of C another module. When the linker links the two modules, all references to the external variables are resolved. static static variables are not "known'" outside their function or file; however, they differ from normal variables in that they maintain their value between calls. The effect of static differs depending on whether it is applied to a local variable or a global variable: static Local Variables. When the static specifier is applied to a local variable, the compiler creates permanent storage for it, as it does for global variables. The difference is that a static local variable is still "known" only to the code block in which it is declared. local variables are very important for the creation of stand-alone functions where the retention of values is required, given that they prevent the necessity to use global variables with the attendant risk of undesirable side effects. static static Global Variables. static applied to a global variable creates a global variable that is "known" only to the file in which it is declared. Generally, a static global variable is used where a local static cannot do the job. register Variables Prior to the ANSI standard, the register specifier only applied to types int and char . It caused the value of the variable to be kept in a CPU register rather than in memory, meaning that operations on the variable would be much faster. ANSI broadened the definition of register so that it may now be applied to any variable. In addition, ANSI simply states that "access to the object be as fast as possible". In practice, char and int variables are still stored in CPU registers, whereas larger objects like arrays (which cannot, of course, be stored in registers) may still receive preferential treatment. The register specifier may only be applied to local variables and formal parameters. register variables are ideal for loop control and other places where many references will be made to the same variable. The compiler automatically transforms register variables into non-register variables when the limit is reached. Because register variables do not have addresses, the & operator (see below) cannot be used on them as it can with variables located in memory. Access Type Modifiers The ANSI standard introduced two new type modifiers. These modifiers (const and how variables may be accessed or modified. Usage is as follows: volatile ) control const Variables of type const may be given an initial value; however, that value cannot be changed by the program. Such a variable receives its value from explicit initialization or by some hardware-dependent means. Many functions in the C standard library use const in their parameter declaration. const Pointers. As will be seen, functions can modify a variable pointed to by a pointer. However, if the pointer is specified as const in the formal parameter declaration, the function's code will not be able to modify what it points to. Essence of C 5 volatile The value of variables of type volatile may be changed in ways not explicitly specified in the program (eg, a variable used to hold the real time of the system). The contents of the variable are altered without any explicit assignment statements. Constants In C, constants refer to fixed values which the program may not alter, and which can be any of the basic data types. Character constants are enclosed between single quotes, eg, 'a' . The use of scientific notation for floating point numbers is allowed. Specific types of numeric constants may be specified by using suffixes. F and L following a floating point type cause the number to be treated as, respectively, a float and a long double. U and L following an integer type cause the number to be treated as, respectively, an unsigned and a long . Hexadecimal constants begin with an 0x . Octal constants begin with a 0 . C allows the definition of string constants even though it does not formally have a string data type. A string is a set of characters, or a character, enclosed in double quotes. Backslash Character Constants. Some characters, however, cannot be entered into a string from the keyboard. For this reason, C includes the following special "backslash" characters: Chara \b \f \n \r \t \" \' Meaning Backspace Form feed New line Carriage return Horizontal tab Double quote Single quote Chara \0 \\ \v \a \N \xN Meaning Null Backslash Vertical tab Alert Octal constant Hexadecimal constant Assignment, Arithmetic, Relational and Logical Operators C is very rich in operators and defines four classes, viz, arithmetic, relational, logical and bitwise. Assignment Operator The assignment operator is a single equals sign (= ). Arithmetic Operators The main arithmetic operators are: + * / % The modulus operator (% ) cannot be used on floating-point types. The unary minus multiplies its single operand by -1, i.e., any number preceded by a minus sign switches its sign. Two additional operators (the increment and decrement operators) are available. These are: ++ ++ -- and -- produce faster code for increment and decrement operations. As an example of usage, ++x is the same as x = x + 1 and --x is the same as x = x - 1. ++x can also be written as x++ . When the operator follows the operand in this way, C uses the value of the operand before incrementing (or 6 Essence of C decrementing) it. On the other hand, if the operator precedes the operand in an expression, C performs the operation before using the value of the operand. For example: x = 10; y = ++x; sets y to 11. However, if the operand had preceded the operator (i.e., x++ ), y is set to 10. Either way, x is set to 11. Relational and Logical Operators The relational and logical operators are as follows: Operator > < == && || ! Meaning Greater than Less than Equal logical AND logical OR logical NOT Operator >= <= != Meaning Greater than or equal to Less than or equal to Not equal The idea of true and false underlies the concepts of relational and logical operators. In C, true is any value other than zero and false is zero. All relational and logical operations produce a result of either 1 or 0. Although C does not contain an exclusive OR (XOR ) operator, a function can easily be created to perform that task. Bitwise Operators Since C was designed to take the place of assembly language for most programming tasks, it needed to be able to support assembler-like operations. Bitwise operations refer to testing, setting or shifting the actual bits in a byte or word (byte and word corresponding to the types char and int and their variants). C's bitwise operators are: Operator & | >> << ^ ~ Meaning AND OR Shift right Shift left Exclusive OR One's complement Pre-Amble - Representation of Positive and Negative Numbers The following is an example of a binary number representing 100 decimal: 0 0 1 1 0 0 64 32 16 8 1 4 0 2 0 1 64 + 32 + 4 =100 Negative numbers are handled somewhat differently. Most computers choose to represent such numbers using two's complement notation. Using such notation, the left-most bit represents the sign bit. 1 means negative and 0 means positive. The remaining bits represent the value of the number. In two's complement notation, the value -1 is represented by: 1 1 1 1 1 1 1 1 Essence of C 7 A convenient way to covert a negative decimal number to binary is to add 1 to the value, express the absolute value in binary, then complement all the bits. For example, to convert -5 to binary: add 1: result in binary: complement: -4 0 0 1 1 0 1 0 1 0 1 1 0 0 1 0 1 To convert the binary number back to decimal: complement: convert to decimal: change sign: subtract 1 0 0 4 -4 -5 0 0 0 1 0 0 Bitwise Operators - General Bitwise operators may be used on any integer value in C. AND, OR, XOR, Shift Left and Shift Right bitwise operators take two operands; however, One's Complement is a unary operator and takes one argument only. Clearing a Bit - Bitwise AND ( &) Bitwise ANDing is frequently used for "masking" operations, i.e., the operator may be used to set specific bits to zero. Any bit that is 0 in either operand causes the corresponding bit to be set to 0. For example, the following resets a parity bit to 0 : return (ch & 127); Note that the number 127 has bit 8 set to 0 . Here is what happens: parity bit set 1 0 0 1 0 1 0 1 0 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 parity bit unset 0 0 0 1 0 1 0 1 0 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 ch 127 result Setting a Bit - Bitwise OR ( |) Similarly, the bitwise OR may be used to set a bit. Any bit which is set to 1 in either operand causes the corresponding bit in the outcome to be set to 1 . For example, the following is 128 | 3 : 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 3 in binary 128 in binary result Exclusive OR ( ^) Exclusive OR (XOR) sets a bit only if the bits being compared are different. One interesting property of XOR is that any value XOR'd with itself produces 0. This is useful in assembly programming. Swapping Values. An interesting application of XOR is that it can be used to exchange two values without the need for an extra memory location, e.g.: a ^= b; b ^= a; a ^= b; Shift Operators ( << and >> ) Shift operators move all bits left or right as specified. As bits are shifted off one end, zeroes are brought in at the other. A shift is not a rotate; the bits shifted off one end are lost. 8 Essence of C Left shifting has the effect of multiplying the shifted value by two. For right shifts, what is shifted in on the left depends on the sign of the value being shifted and also on how the operation is implemented on the particular machine. If the sign is 0 (positive), then zeroes will be shifted in. If the sign is 1 (negative), on some machines 1s will be shifted in and on others, 0s. The former is known as an arithmetic right shift; the latter is known as a logical right shift. C does not guarantee a defined result if a value is shifted by an amount greater or less than the number of bits in the data item. One's Complement Operator ( ~) The One's Complement operator reverses the state of each bit in the specified variable. Note that two complements in a row return the byte to its original value. Use of Bitwise Operators as Assignment Operators As with all binary arithmetic operators in C (see C Shorthand, below), the bitwise operators can be used as assignment operators by tacking on an equal sign. For example: word &= 15; would perform the same function as: word = word & 15; Using Constants When using constants in bitwise operations it is usually more convenient to express the constants in hexadecimal. Bitwise Operations on Different Data Items When bitwise operations are performed between two values of different sizes, the system aligns the operands on the right. If the shorter of the two items is a signed quantity, and the value is negative, then the sign is extended to the left to match the number of bits in the larger-sized value. So if the value is negative, 1s are filled in on the left; if positive, 0s are filled in. If the smaller item is unsigned, it is filled in from the left with 0s. The ? Operator The ternary operator ? replaces certain statements of the if-then-else form. In the following: y = x >10 ? 100 : 200 the first expression is evaluated. If it evaluates to true, y is made equal to 100. If it evaluates to false, y is made equal to 200. The & and * Operators Preamble. A pointer is the memory address of a variable. A pointer variable is a variable specifically declared to hold a pointer. Pointers: provide a fast means of referencing array elements, allow C functions to modify their calling parameters, and Essence of C 9 support linked lists and other dynamic data structures. A pointer is of sufficient size to hold an address as defined by the architecture of the computer. Pointer Operators. The following operators are used to manipulate pointers: The & Operator The & operator returns the memory address of its operand. For example: m = &count; places the address of count into m . & can be thought of as "the address of". The * Operator The * operator is the complement of & . It returns the value of the variable at the address which follows it. For example, in the following extension of the preceding code fragment: q = *count; the value of count is placed in q . * can be thought of as "at address". The Compile-Time Operator sizeof is a unary compile time operator which returns the length, in bytes, of the variable or parenthesized type specifier that precedes it. Usage is illustrated in the following: sizeof printf("%f ", sizeof f); printf("%d", sizeof(int)); The Comma Operator The comma operator may be used to string together several expressions. For example, in: x - (y = 3,y + 1); the left side of the comma operator is evaluated as void, which means that and then x is assigned the value 4 . y is first assigned the value 3 The . and -> Operators The . and -> operators reference individual elements in structures and unions. The . operator is used when working with the actual structure or union, e.g.: employee.wage = 123.23; The -> is used when working with a pointer to the structure or union, e.g.: emp_ptr->wage = 123.23; Miscellaneous Features Casting An expression can be forced to be of a particular type by using a cast. Casts are often considered to be operators. As an operator, casts are unary, and have the same precedence as other unary operators. An example of a cast which ensures that an expression evaluates to type float is as follows: x = (float) x / 2; In the following example, a fractional number is truncated to a whole number: 10 Essence of C double x = 3.141592654, double y; y = (short) x; // resulting value of y is 3.0 In the following example, casting is used to round to the nearest integer: double x, y; x y x y = = = = 4.67; (short) (x + 0.5); 4.45; (short) (x +.05); // resulting value of y is 5.0 // resulting value of y is 4.0 C Shorthand C has a special shorthand which simplifies the coding of certain assignments. For example: x = x + 10; x = x * 10; can be written as can be written as x += 10; x *= 10; Some further examples using the bitwise operators are as follows: Operator >>= <<= &= ^= |= Usage a >>= b a <<= b a &= b a ^= b a |= b Result in a After Operation shifted right by b bits shifted left by b bits a ANDed with b a exclusive ORed with b a ORed with b a a Operator Precedence The following lists the precedence of all C operators: () ! * + << < == & ^ | && || ? = , ~ / ++ % -> -. -(type) * & sizeof >> <= > != >= += -= *= /= More on Assignments - Type Conversion and Multiple Assignments Type Conversion In an assignment statement, the type conversion rule is that the value on the right side of the assignment is converted to the type on the left side. When converting from integers to characters, and from long integers to integers, the appropriate amount of high-order bits will be removed. Type Conversion in Expressions. Operators, constants and variables are the constituents of expressions. An expression in C is any valid combination of these elements. When constants and variables of different types are mixed in an expression, they are converted to the same type. The compiler converts all operands up to the type of the largest operand, a process known as type promotion. This is done operation by operation. Essence of C 11 Multiple Assignments The assignment of a value to many variables is possible using multiple assignment, eg: x = y = z = 0; 12 Essence of C Essence of C 13 3 PROGRAM CONTROL STATEMENTS Conditional, Iteration, and Jump Statements Overview The ANSI standard categorizes C's program control statements as follows: Category Selection (or Conditional) Statements Iteration Jump Label Relevant Keywords if for break case switch while goto default do-while return continue True and False and Conditional Expressions in C Many C statements rely on the outcome of a conditional expression which evaluates to either true or false. In C, unlike many other languages, true is any non-zero value and false is zero. This approach facilitates efficient coding. The programmer is not restricted to conditional expressions involving only the relational and logical operators (as in BASIC or Pascal). Any valid C expression may be used to control the if. All that is required is an evaluation to either zero or no-zero. For example: b = a * c; if(b) printf("%d\n",b); else printf("b evaluated to zero\n"); Selection (or Conditional) Statements The if Statement The general form of an if statement is: if(expression) statement; else statement; where statement may be a single statement, a block, or nothing, and the else statement is optional. The conditional statement produces a scalar result, ie, an integer, character or floating point type. 14 Essence of C Nested ifs. The ANSI standard specifies that 15 levels of nesting must be supported. In C, an else statement always refers to the nearest if statement in the same block and not already associated with an if . For example: if(i) { if(j) statement 1; if(k) statement 2; else statement 3; } else statement 4; // this if is associated with // this else // this is associated with if(i) The if-else-if Ladder. written like this: if(expression) statement; else if(expression) statement; else if(expression) statement; ... else statement; To avoid excessive indentation, if-else-if ladders are normally The switch Statement switch successively tests the value of an expression against a list of integer or character constants. When a match is found, the statements associated with that constant are executed. The general form is: switch(expression) { case constant1: statement sequence; break; case constant2: statement sequence; break; ... default: statement sequence; } default: is executed if no matches are found. Although case is a label statement, it cannot exist outside a switch . Some important aspects of switch are as follows: If the break A switch can only test for equality, whereas if can evaluate an expression. No two case constants can have identical values. If character constants are used, they will automatically be converted to integers. break statement is omitted, execution continues on into the next or the end of the switch is reached. For example, in: case statement until either a Essence of C 15 switch(i) { case 1: case 2: case 3: flag=0; break; case 4: flag=1; case 5: error(flag); break; default: process(ch); } if i is equal to either 1, 2 or 3, flag is set to 0. If i is equal to 4, flag is set to 1 and error(flag) is executed. If i is equal to 5, error(flag) is called with whatever value was in flag before the switch was entered. Nested switch Statements. A outer switch. switch statement may be part of the statement sequence of an Iteration Statements The for Loop The general form of the for statement is: for(initialization;condition;increment) statement; initialization is generally an assignment statement used to set the loop control variable. condition is a relational expression that determines when the loop exits. increment defines how the loop variable changes each time the loop is repeated. In for loops, the conditional test is always performed at the top of the loop, which means that the code inside the loop may not be executed at all if the condition is false to begin with, as in: x = 10; for(y=10;y!=x;++y) printf("%d",y); Variation 1 - The Comma Operator. A variant of the operator, as in: for(x=0,y=0;x+y<10;++x); for loop is made possible by the comma in which both x and y control the loop. Variation 2 - Missing Pieces of the Loop Definition. An interesting trait of the pieces of the loop definition need not be there. For example, in: for(x=0;x!=123; ) scanf("%d",&x); for loop is that each time the loop repeats, x is tested to see if it equals 123. The loop condition only becomes false, terminating the loop, when 123 is entered. Variation 3 - The Infinite Loop. If all of the pieces in the loop definition are missing, an infinite loop is created. However, the break statement may be used to break out of the loop, as in: 16 Essence of C for(;;) { ch = getchar(); if(ch == 'A') break; } Variation 4 - for Loops With No Bodies. The body of the for loop may also be empty. This improves the efficiency of some code. For example, the following removes leading spaces from the stream pointed to by str : for( ;*str==' ';str++); Time delay loops are another application of a loop with an empty body, eg: for(t=0;t<1000;t++); The while Loop The general form of the while loop is: while(condition) statement; where statement is either an empty statement, a single statement or a block of statements. The loop iterates while the condition, which is executed at the top of the loop, is true. Variation 1 - No Body. The following, which is an example of a while loop with no body, loops until the user types A: while((ch=getchar()) !='A'); The do-while Loop The do-while loop tests the condition at the bottom of the loop rather than at the top. This ensures that the loop will always execute at least once. In the following example, the loop will read numbers from the keyboard until it finds a number equal to or less than 100: do { scanf("%d",&num); } while num>100; Jump Statements The return Statement The return statement is used to return from a function. If return has a value associated with it, that value is returned by the function. If no return value is specified, either zero or a garbage value will be returned, depending on the compiler. The general form of the return statement is: return expression; The } which ends a function also causes the function to return. A function declared as contain a return statement that specifies a value. void may not The goto Statement The goto statement requires a label (an identifier followed by a colon), which must be in the same function as the goto . Essence of C 17 The break Statement The break statement has two uses: It can be used to terminate a case in a switch . It can be used to force immediate termination of a loop. The exit() Function The standard library function form of the exit function is: exit() causes immediate termination of the entire program. The general void exit(int return_code); The value of return_code is returned to the operating system. Zero is generally used as a return code to indicate normal program termination. Other arguments are used to indicate some sort of error. The continue Statement The continue statement forces the next iteration of the loop, skipping any code in between. For the for loop, continue causes the conditional test and then the increment portion of the loop to execute. For the while and do-while loops, program control passes to the conditional test. 18 Essence of C Essence of C 19 4 ARRAYS AND STRINGS Single Dimension Arrays, Two- and Mutliple-Dimension Arrays, Strings, Arrays of Strings An array is a collection of variables of the same type which are referenced by a common name. A specific element in an array is referenced by an index. The most common array in C is the string, which is simply an array of characters terminated by a null. In C, arrays and pointers are closely related; a discussion of one usually refers to the other. C has no bounds checking on arrays. Single Dimension Arrays The general form for declaring a single-dimension array is: type var_name[size]; In C, all arrays have zero as the index of their first element. Therefore a declaration of declares a ten-element array (p[0] through p[9] . char p[10]; Generating a Pointer to an Array A pointer to the first element in an array may be generated by simply specifying the array name, without any index. For example, given: int sample[10]; a pointer to the first element may be generated by simply using the name following code fragment assigns p the address of the first element of sample: int *p; int sample[10]; p = sample; sample . For example, the The address of the first element may also be specified using the &sample[0] produce the same result. The former is usually used. & operator. For example, sample and Passing Single-Dimension Arrays to Functions In C, an entire array cannot be passed as an argument to a function. However, a pointer to an array may be passed by specifying the array's name without an index. If a function receives a single dimension array, the formal parameter may be declared as either a pointer, a sized array, or an unsized array. Examples are: 20 Essence of C function(int *x) function(int x[10]) function(int x) // A pointer. // A sized array. // An unsized array. Strings A string is actually an array of characters. Because strings are terminated by a null ('\0' ), character arrays must be declared with one extra element (to hold the null). Although C does not have a string data type, it allows string constants. For example, "hello a string constant. C supports a wide range of string manipulation functions, including: Function strcpy(s1,s2) strcat(s1,s2) strlen(s1) strcmp(s1,s2) there" is Description Copies s2 into s1 . Concatenates s2 to s1 . Returns the length of s1 . Returns 0 (false) if s1 and s2 are the same. Returns less than 0 if s1<s2 Returns greater than 0 if s1>s2 Returns pointer to first occurrence ch in s1 . Returns pointer to first occurrence s2 in s1 . ! strchr(s1,ch) strstr(s1,s2) Since strcmp() returns false if the strings are equal, it is best to use the condition if the test is for equality. operator to reverse the Two-Dimensional Arrays In C, a two-dimensional array is declared as shown in the following example: int d[10][20]; Two-dimensional arrays are stored in a row-column matrix. The first index indicates the row. The row index can be thought of as a "pointer" to the correct row. When a two-dimensional array is used as an argument to a function, only a pointer to the first element is passed. However, the receiving function must define at least the length of the second dimension, e.g.: function(int x[20] Arrays of Strings Arrays of strings are created using a two-dimensional array. The left index determines the number of strings. Each string is accessed using only the left index (eg, gets(array[2] accesses the third string). Multi-Dimensional Arrays C allows arrays of more than two dimensions, the exact limit depending on the individual compiler. Indexing Pointers In C, pointers and arrays are closely related. As previously stated, an array name without an index is a pointer to the first element. For example, given the array char my_array[10];, my_array and &my_array[0] are identical. Conversely, any pointer variable may be indexed as if it were declared as an array. For example, in this program fragment: Essence of C 21 int *p, i[10]; p = i; p[5] = 100; (p+5) = 100 // assignment using index // assignment using pointer arithmetic both assignments achieve the same thing. Pointers are sometimes used to access arrays because pointer arithmetic is faster than array indexing. In a sense, a two-dimensional array is like an array of pointers that point to arrays of rows. Therefore, using a separate pointer variable is one easy way to access elements. For example, the following prints the contents of the specified row for the global array num: int num[10][10]; ... void print_row(int j) { int *p, t; p = &num[j][0]; for(t=0;t<10;++t) printf("%d ", *(p+t)); } // get address of first element in row j Array Initialization Arrays may be initialized at the time of declaration. The following example initializes a ten-element integer array: int i[10] = { 1,2,3,4,5,6,7,8,9,10 }; Character arrays which hold strings allow a shorthand initialization, e.g.: char str[9] = "I like C"; which is the same as: char str[9] = { 'I',' ','l','i','k','e',' ','C','\0' }; When the string constant method is used, the compiler automatically supplies the null terminator. Multi-dimensional arrays are initialized in the same way as single-dimension arrays, e.g.: int sgrs[6][2] = { 1,1, 2,4, 3,9, 4,16, 5,25 6,36 }; Unsized Array Initializations If unsized arrays are declared, the C compiler automatically creates an array big enough to hold all the initializers. This is called an unsized array. Examples declaration/initializations are as follows: char e1 = "read error\n"; char e2 = "write error\n"; int sgrs[2] = { 1,1, 2,4, 3,9 4,16, }; 22 Essence of C Essence of C 23 5 POINTERS Pointers, Pointer Operators, Pointer Expressions, Pointers and Arrays, Multiple Indirection, Pointer to Functions A pointer is a variable which holds a memory address, that address usually being the address of another variable. Pointers provide the means by which functions may modify their calling parameters. They also support C's dynamic allocation functions and improve the efficiency of certain routines. If a variable is going to hold a pointer, it must be declared as such. The general form of declaration is: type * name; where type is any valid C data type and name is the name of the pointer variable. The base type defines what type of variables the pointer can point to. All pointer arithmetic is done relative to the base type. Pointer Operators The & Operator The & operator is a unary operator (ie, an operator which requires only one operand) which returns the memory address of the operand. For example: m = &count; places the memory address of count into m . The * Operator The * operator is the complement of follows. For example: q = *m; & in that it returns the value of the variable at the address which places the value of count into q . Pointer Expressions Pointer Assignments As with any variable, a pointer may be used on the right side of an assignment operator to assign its value to another pointer. 24 Essence of C Pointer Arithmetic The only arithmetic operations which may be used on pointers are addition and subtraction. When, say, an int pointer is incremented (eg, p++; ), the address held by the pointer will be incremented by four, not by 1, the reason being that integers are (normally) four bytes long. That is, pointers are incremented and decremented relative to the length of their base type. Arithmetic operations are not limited to the increment and decrement operators. Integers may be added to, or subtracted from, pointers, as in: p = p + 12; Pointer Comparisons Pointers may be compared in a relational expression, as in: if(p < q) printf("p points to lower memory than q"); Pointers and Arrays C provides two methods for accessing array elements, namely, array indexing and pointer arithmetic. Pointer arithmetic can be faster than array indexing. In the following, p is set to point to the first element of str : char str[80], *p; p = str; Two ways to access the fifth element of str are as follows: str[4]; *(p+4); // Array indexing. // Pointer arithmetic. The following examples illustrate the two methods: // Index s as an Array void puts(char *s) { register int t; for(t=0;s[t];t++) } putchar(s[t]); } // Access s as a Pointer void puts(char *s) { while(*s) putchar(*s++); } Most professional C programmers would find the second method easier to read and understand. Arrays of Pointers Pointers may be arrayed like any other data type, e.g.: int *x[10]; Assigning the address of an int variable to the third element of this pointer array is achieved by: x[2] = &var; Essence of C 25 Finding the value of var is achieved by: a = *x[2]; An array of pointers may be passed to a function using the same method used to pass other arrays, i.e., by calling the function with the array name without any indexes, as in: void display_array(int *q) { int t; for(t=0;t<10;t++) printf("%d ",*q[t]); } in which q is not a pointer to integers but to an array of pointers to integers. Pointer arrays are often used to hold pointers to strings, as in: void syntax_error(int num) { static char *err = { "Cannot open file\n", "Read error\n", "Write error\n", } printf("%s", err[num]); } Multiple Indirection Multiple indirection is where a pointer points to another pointer that points to a target value. A variable which is a pointer to a pointer must be declared as such, as in: float **balance; Initializing Pointers Before a pointer is assigned a value, it contains an unknown value. If used in this state, it could crash the operating system. The value null should be assigned to a pointer that points nowhere. As previously illustrated, strings may be initialized as follows: char *p = "hello world\n"; The reason this sort of initialization works is because of the way the compiler operates. All C compilers create what is known as a string table. Therefore, the above declaration places the address of "hello world" , as stored in the string table, into p . p can then be used like any other string, as in: printf(p); Pointers to Functions Even though a function is not a variable, it has a location in memory which may be assigned to a pointer. A variable which is a pointer to a function must be declared as such, as in: int (*p)(); The address of a function is assigned to this variable by using the function's name without parentheses or arguments, e.g.: p = function_name; 26 Essence of C C's Dynamic Allocation Functions Global variables are allocated storage at compile time and local variables use the stack. Neither can be added during program execution. Dynamic allocation is the means by which a program can obtain memory during execution. The core of the dynamic allocation system consists of the functions malloc() (allocates memory from the heap) and free() (frees that memory). malloc() returns a pointer to the first byte of the allocated memory. Pointers provide the necessary support for C's dynamic allocation system. The dynamic allocation system supports linked lists, binary trees and dynamically allocated arrays. Dynamically Allocated Arrays and Pointers Memory may be allocated using malloc() and operated on as if it were an array. Such "arrays" are known as dynamically allocated arrays. Since any pointer may be indexed as if it were a singledimension array, the pointer returned by the malloc() function may be indexed in the normal way. Multi-dimensional arrays, however, pose a problem. Since the dimensions have not been defined in the program, a trick must be used: The pointer is passed as a parameter to a function, which allows the function to define the dimensions of the parameter receiving the pointer, e.g.: p = malloc(40*sizeof(int)); table(p); ... void table(int p[4][10]) // now the compiler has an array to work with { ... } Essence of C 27 6 FUNCTIONS General Form, Scope Rules, Arguments, Calling Functions With Arrays, Returning Values and Pointers, Recursion The General Form of a Function The general form of a function is: type_specifier function_name(parameter list) { body of function; } specifies the type of value that the function's return statement returns and the is a comma-separated list of variable names and types which receive the values of the arguments passed by the call to the function. type_specifier list parameter where Scope Rules of Functions The scope rules of a language are the rules which govern whether a piece of code knows about, or has access to, another piece of code or data. In C, a function's code cannot be accessed other than by a call to that function. The code and data defined within a function cannot interact with the code or data defined in another function because the two functions have a different scope. Function Arguments If a function is to use arguments, variables (known as formal parameters) must be declared to accept the values of the arguments. The type of the arguments used to call the function must be compatible with the type of its parameters. (The use of function prototypes can help catch errors in this regard.) Assignments may be made to formal parameters, and those parameters may be used in any valid C expression. Essentially, parameters are, and may be used like, local variables. Functions may be passed arguments by call-by-value or call-by-reference. Call -by -Value The call-by-value method copies the value of an argument into the formal parameter of the function. Therefore, changes made by the function to the parameters have no effect on the variables used to call it. 28 Essence of C Call -by -Reference The call-by-reference method copies the address of an argument into the formal parameter. Inside the function, this address is used to access the argument used in the call. In this way, changes made to the parameter affect the variable used in the call to the function. Calls-by-reference are achieved by passing a pointer as the argument. Of course, in this case, the parameters must be declared as pointer types. The following demonstrates this: void swap(int *x, int *y); void main(void) { int x,y; x = 10; y = 20; swap(&x,&y) } void swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } // function prototype // addresses passed Calling Functions With Arrays Generally, the C convention is the call-by-value. arguments. An exception is where arrays are passed as When an array is passed to a function, only the address is passed, not the entire array. When a function is called with just the array name, a pointer to the array is passed. (In C, an array name without an index is a pointer to the first element.) This means that the parameter declaration must be of a compatible pointer type. There are three ways to declare a parameter which is to receive an array pointer: As an array, for example: void display (int num[10]); in which case the compiler automatically converts it to a pointer. As an unsized array: void display (int num); which is also converted to a pointer. As a pointer: void display (int *num); which is allowed because, in C, any pointer may be indexed with as if it were an array. (This is the most common method.) On the other hand, an array element used as an argument is treated like any other simple variable, as shown in this code fragment: Essence of C 29 display(t[a]); ... void display(int num) { printf("%d", num; } argc and argv - Arguments to main() Information may be passed into the program from the command line using the two special built-in arguments argc and argv . The former holds the number of arguments and the latter is a pointer to an array of character pointers. The value in argc is always at least 1 because the program name counts as the first argument. An example of usage is as follows: void main(int argc, char *argv) { if(argc != 2) { printf("You forgot to type your name\n"); exit(1); } printf("Hello %s", argv[1]); } The names argc and programmer prefers. argv are traditional but arbitrary, and may be changed to anything the The return Statement causes an immediate exit from a function. It may also be used to return a value. All functions, except those of type void , return a value. (If no return statement is present, most compilers will cause a 0 to be returned.) return Returning Values As long as a function is not declared as void , it may be used as an operand in any C expression, e.g.: x = power(y); if(max(x,y) > 100) printf("greater"); for(ch = getchar();isdigit(ch); ); However, a function cannot be the target of an assignment. The purpose of most functions is to either perform a computation and return a value, manipulate information and return a success-or-failure (true or false) value, or perform a strictly procedural routine which produces no value (eg, the exit() function). Some functions which do not produce an interesting result return something anyway. For example, printf() returns the number of characters written, although it would be unusual to find a program which used this value. In other words, the returned value does not have to be used if it serves no purpose. Functions That Return Non-Integer Values Functions may be declared to return any valid C data type. If the return type is not specified, it automatically defaults to type int . If a program calls a function which, due to a programming error, returns a type different from that specified by the function, and if that call is made before the function is declared, the compiler will generate the wrong code for the function call. To prevent this, a special form of declaration statement is used at the top of the program to tell the compiler what value that function is returning. 30 Essence of C The traditional method for such declaration has now been replaced by the ANSI C method of the function prototype. The traditional method takes the form: type_specifier function_name(); The ANSI function prototype, on the other hand, takes the form: type function_name(type param_name1, type param_name2, ...,); the difference being that the ANSI form specifies the type of the arguments expected to be received by the function. This enables the compiler to identify any type mismatch between the calling arguments and the formal parameters. void must be used inside the parentheses of function prototypes for functions which receive no arguments. Returning Pointers Functions that return pointers are handled just like any other type of function, e.g.: char * match(char c, char *s) { while(c != *s && *s) s++; return(s); } Functions of Type void Declaring a function of type void prevents its use in expressions. A void function's prototype must be declared before it is called, otherwise the compiler will assume that it returns an integer and declare a type mismatch. What Does main() Return? The ANSI standard states that main() returns an integer to the calling process (generally the operating system). Returning a value from main() is the same as calling exit() with the same value. main() may be declared as void if it does not return a value. Recursion C supports recursion. In other words, in C, a function may call itself. Parameter Lists of Variable Length and Type In C, a function may be specified as having a varying number of parameters. In this case the parameter declaration must be ended with three periods, as in: function(int a, int b, ...); This form must also be used in the function's prototype. There must always be at least one actual argument. Efficiency Where execution speed is critical, in-line code should be used instead of the relevant function. In-line code is simply the function's statements used without a call to the function. In-line code is faster because the call itself takes time to execute and because the passed arguments must be placed on the stack, which also takes time. Essence of C 31 Libraries and Files Functions may be left in the main program or they may be placed in libraries or separate files. Technically, a library differs from a separately compiled file of functions in that, when a routine from a library is linked with the main program, only the functions used by that program are linked. Most C compilers include instructions for creating a library. 32 Essence of C Essence of C 33 7 CUSTOM DATA TYPES Structures, Bit-Fields, Unions, Enumerations, typedef C allows the programmer to create custom data types in five different ways. These are: The structure, which is a collection of variables under one name. The bit-field, a variation of the structure which allows easy access to the bits of a word. The union, which enables the same piece of memory to be defined as two or more types of variables. The enumeration, which is a list of symbols. Types created with typedef, which defines a new name for an existing type. Structures A structure is a collection of variables referenced under one name, providing a convenient means of keeping related information together. Definition A structure definition forms a "template" which may be used to create structure variables. variables which make up a structure are called structure elements. In the following, the C keyword struct tells the compiler that a structure template is being defined: struct { char char int }; addr name[30]; street[40]; postcode; The In this example, addr is known as the structure tag. Note that a structure definition is actually a statement; hence it is terminated by a semi-colon. Note also that only the form of the data has been defined at this stage and that no variable has yet been declared. Declaration An example of a structure variable declaration of type addr is: struct addr addr_info; 34 Essence of C Variables may also be declared as the structure is defined, as in: struct addr { char name[30]; char street[40]; int postcode; } addr_info, binfo, cinfo; If only one variable is needed, the structure tag is not needed, e.g.: struct { char name[30]; char street[40]; int postcode; } addr_info; Referencing Structure Elements Individual elements of a structure are referenced using the dot operator, e.g.: addr_info.postcode = 1234; printf("%d",addr_info.postcode); Structure Assignments ANSI C compilers allow the information in one structure to be assigned to another structure, as in: binfo=addr_info; Arrays of Structures The following example declares a 100-element array of type addr : struct addr addr_arr[100]; in which the postcode element of structure 3 would be accessed by: printf("%d", addr_info[2].postcode); Passing Structures to Functions When an element of a non-global structure is to be passed to a function, the value of that element is passed (unless that element is complex, such as an array of characters). For example, given: struct fred { char x; int y; char z[10]; } mike; each element would be passed like this: funct(mike.x); funct(mike.y); funct(mike.s); funct(mike.s[2]); // // // // passes passes passes passes character value of x integer value of y address of string character val of s[2] If the address of the element is required to be passed, the & operator is placed before the structure name. In the above example, this would apply except in funct(mike.s) ;. Essence of C 35 Passing Entire Structures to Functions When a structure is used as an argument to a function, the entire structure is passed using the standard call-by-value method. Of course, this means that changes made to the contents of the structure inside the function do not affect the structure used as the argument. Declaring Structure Pointers Assuming the previously defined structure data of that type: struct addr *addr_pointer; addr , the following declares addr_pointer as a pointer to Using Structure Pointers Structure pointers may be used to generate a call-by-reference to a function and to create linked lists and other dynamic data structures. Calls-by-reference should be used for all but the simplest structures so as to avoid overheads occasioned by the pushing and popping of all the structure elements onto and off the stack. To access the elements of a structure using a pointer, the -> operator is used, as in: ptr->postcode where ptr has been declared as a pointer to the type of structure and assigned the address of a variable of that type, and postcode is a structure element within that variable. Arrays and Structures Within Structures Structure elements may be arrays and structures. When a structure is an element of another structure, it is called a nested structure. Bit-Fields Preamble. Unlike most other languages, C has a built-in method for accessing a single bit within a byte. This can be useful because: If memory is tight, several Boolean (true/false) variables can be stored in one byte, saving memory. Certain devices transmit information encoded into bits within one byte. Certain encryption routines need to access individual bits within a byte. Tasks involving the manipulation of bits may, of course, be performed using C's bitwise operators. However, the bit-field provides a means of adding more structure and efficiency to the coding. Bit-Fields - A Type of Structure To access bits using bit-fields, C uses a method based on the structure. In fact, a bit-field is simply a special type of structure element which defines how long, in bits, the field is to be. The general form of a bit-field definition is: struct tag { type name1: length; type name2: length; type name3: length; } variable_list; 36 Essence of C A bit-field must be declared as either int , unsigned or signed . Bit-fields of length 1 should be declared as unsigned because a single bit cannot have a sign. An Example Application Bit-fields are frequently used when analyzing input from a hardware device. For example, the status port of a serial communications device might return a status byte like this: Bit 0 1 2 3 4 5 6 7 Meaning When Set Change in clear-to-send line. Change in data-set-ready. Trailing edge detected. Change in receive line. Clear-to-send. Data-set-ready. Telephone ringing. Received signal. Defining the Bit-Field. The foregoing can be represented in a status byte using the following bitfield definition/declaration: struct status_type { unsigned delta_cts: unsigned delta_dsr: unsigned tr_edge: unsigned delta_rec: unsigned cts: unsigned dsr: unsigned ring: unsigned rec_line: } status; 1; 1; 1; 1; 1; 1; 1; 1; Using this bit-field, the following routine would enable a program to determine whether it can send or receive data: status = get_port_status; if(status.cts) printf("Clear to send"); if(status.dsr) printf("Data set ready); Referencing Bit-Field Elements Values are assigned to a bit-field using the usual method for assigning values to structure elements, e.g.: status.ring = 0; As with structures, if an element is referenced through a pointer, the dot operator. -> operator is used in lieu of the Variations in Definition All bit-fields need not necessarily be named, which allows unused ones to be bypassed. For example, if, in the above example, access had only been required to cts and dsr , status_type could have been declared like this: struct status_type { unsigned: 4; unsigned cts: 1; unsigned dsr: 1; } status; Essence of C 37 Limitations Bit-field variables have certain limitations. For example: The address of a bit-field variable cannot be taken. Bit-field variables cannot be arrayed. Integer boundaries cannot be overlapped. There is no certainty, from machine to machine, as to whether the fields will run from right to left or from left to right. In other words, any code using bit-fields may have some machine dependencies. Mixing Normal and Bit-Field Structure Elements Normal and bit-field structure elements may be mixed, as in: struct emp { struct addr address; float pay; unsigned lay-off: 1; unsigned hourly: 1; unsigned deducts: 3; } // lay-off or active // hourly pay or wage // tax deductions which demonstrates the use of only one byte to store information which would otherwise require three bytes. Unions A union is a memory location which is shared by two or more different variables, generally of different types, at different times. The general form of a union definition is: union tag { type variable_name; type variable_name; type variable_name; . . } union_variables; An example definition is as follows: union u_type { int i; char ch; }; Declaration options are as for structures, ie, variable names may be placed at the end of the definition or a separate declaration statement may be used. In the example shown, both the integer i and the character though ch only requires the first of the bytes required by i . ch share the same memory location, even Accessing Union Elements Union elements are accessed using the same methods used for accessing structure elements. 38 Essence of C Usage The union aids in the production of machine-independent code because the compiler keeps track of the actual sizes of the variables which make up the union. Unions are frequently used when type conversions are needed because the data held in a union can be referred to in different ways. Enumerations An enumeration is a set of named integer constants which specify all the legal values a variable of that type may have. Enumerations are common in everyday life. For example, an enumeration of the coins in a currency might be: 10 cents, 25 cents, 50 cents, dollar Definition and Declaration Enumerations are defined much like structures. An example definition is as follows: enum coin { ten_cent, quart_doll, half_doll, dollar }; An example declaration is: enum coin money; Given this definition and declaration, the following statements are valid: money = quart_doll; if(money == quart_doll) printf("is 25 cents\n"); In an enumeration, each symbol stands for an integer value. For example, using the above definition and declaration: printf("%d %d", ten_cent, dollar); displays 0 3 on the screen. Initialization The symbols in an enumeration may be initialized, as in the following example: enum coin { ten_cent, quart_doll, half_doll=100, dollar }; which would place these values in the symbols: ten_cent quart_doll half_doll dollar 0 1 100 101 To avoid the rather tedious coding arising from the fact that symbols are integers and not strings, an array of strings may be declared for access by the enumeration, e.g.: char name = { "10 cents", "25 cents", "50 cents", "dollar" } money = quart_doll; printf("%s", name[money]); Essence of C 39 Note that this only works if no symbol is initialized, the reason being that the string array must be indexed starting at 0 . typedef typedef allows a new data type to be explicitly defined. Note that it does not actually create a new data class, but simply defines a new name for an existing type. This process helps in making machinedependent code more portable, since only the typedef statements need be changed. It can also aid in self-documenting a program by allowing the use of more descriptive names for the standard data types. For example, a new name for float could be created as follows: typedef float balance; which would allow a new float variable to be created by: balance overdue; 40 Essence of C Essence of C 41 8 CONSOLE INPUT/OUTPUT Reading and Writing Characters and Strings, Formatted Console Input/Output Introduction C is virtually unique as a language in that it has no keywords for input/output (I/O). Instead, I/O is accomplished through library functions. There are two types of I/O in C, viz, console I/O and file I/O . Technically, there is little distinction between the two, although they are, conceptually, quite different worlds. The standard C console I/O functions perform only TTY-based output; however, most compilers include in their libraries machine-specific screen control and graphics functions. It should be noted that, although the following pertains to input from the keyboard and output to the screen, the actual source and target of C's I/O functions are the standard input and standard output of the system, which means that this input and output may be re-directed to other devices. Reading and Writing Characters The following functions read characters from the keyboard and write characters to the screen: Function getchar() Operation Reads a character from the keyboard; waits for carriage return. Returns an integer, in which the low-order byte contains the character. Any key, including RETURN, TAB and ESC may be returned by getchar() . Note: Because it waits for a carriage return, getchar() is not useful in an interactive environment (except in the case of some compilers which actually implement getchar() as an interactive function, which is permitted by the ANSI standard). The ANSI standard does not, in fact, define any function guaranteed to provide interactive input, but many C compilers include alternative keyboard input functions which achieve this. Waits for a keypress, after which it returns immediately. Reads the character without an echo to the screen. (One of the common alternatives to getchar() . Not defined by ANSI.) Same as getch(), but echoes the character. Writes a character to the screen. Declared as using an integer parameter; however, only the low-order byte is output to the screen. Returns the character written, or EOF (-1) if an error occurs. getch() getche() putchar() 42 Essence of C Reading and Writing Strings The following functions read and output strings: Function gets() Operation Reads a string of characters and places them at the address pointed to by its character pointer argument. When RETURN is entered, a null terminator is placed at the end of the string and the function returns. Typing errors may be corrected with the BACKSPACE key. Writes its string argument to the screen, followed by a newline. Recognizes the same "backslash" codes as printf() (eg, \t for TAB). Cannot output numbers, but is faster than printf() in outputting strings. Returns EOF if an error occurs. puts() Formatted Console I/O The functions printf() and scanf() read and write data in various formats specified by the programmer. Both functions can operate on any of the built-in data types, including characters, strings and numbers. printf() The prototype for printf() is: int printf(char *control_string,argument_list); where control_string consists of two types of items, namely, characters which will be printed to the screen and format specifiers which control how the subsequent arguments are displayed. Format Specifiers The various format specifiers are as follows: Specifier %c %d %i %e %E %f %g %G %o %s %u %x %X %p %n %% Description Character. Signed decimal integer. Signed decimal integer. Scientific notation (lowercase e). Scientific notation (uppercase E). Decimal floating point. Uses %e or %f, whichever is shorter. Uses %E or %F , whichever is shorter. Unsigned octal. String of characters. Unsigned decimal integers. Unsigned hexadecimal (lowercase letters). Unsigned hexadecimal (uppercase letters). Pointer. The argument is an integer pointer into which the number of characters written so far is placed. Enables a program to perform dynamic formatting. Prints a %. The %n format specifier is different from the other specifiers. As an example of usage, this code fragment: int count; printf("this%nis a test\n", &count); printf("%d", count); would cause this is a test followed by 4 to be displayed. Essence of C 43 Format Modifiers Many format specifiers may take format modifiers, which slightly alter their meaning. These modifiers include the following: Minimum Field Width Specifier. The minimum field width specifier pads the output with spaces to ensure that it reaches a certain minimum width. This code: item=10.12304; printf("%f\n", item); printf("%10f\n", item); printf("%012f\n", item); produces this output: 10.123040 10.123040 00010.123040 Note that, in the third example, a zero has been used for the padding. The minimum field width specifier is most commonly used to produce tables of right-justified numbers. Precision Specifier. The precision specifier follows the minimum field width specifier (if there is one). It consists of a period followed by an integer. Its exact meaning depends on the type of data it is applied to, e.g.: Floating Point Types. When applied to floating-point data, it determines the number of decimal places to be displayed. For example, characters wide with 4 decimal places. %10.4f displays a number at least 10 Integer Types. When applied to integer types, it determines the minimum number of digits which will appear for each number. Leading zeros are added to achieve the required number of digits. Strings. When applied to strings, it specifies the maximum field length. For example, %5.7s displays a string at least 5, and not exceeding 7, characters long. If the string is longer than the maximum field width, the end characters will be truncated. As an example, this code: printf("%.4f\n", 123.1234567); printf("%12.4f\n", 123.1234567); printf("%8.10d\n", 1234); printf("%10.8d\n", 1234); printf("%10.15s\n", "This is a simple test"); printf("%9.6s\n", "Testing"); produces this output: 123.1235 123.1235 0000001234 00001234 This is a simpl Testin Left Justification ( -) By default, output is right-justified. That is, if the field width is larger than the data printed, the data will be placed at the right edge. Left justification may be forced by using a minus sign after the %. For example, %-10.2f left justifies a floating point number in a ten-character field. 44 Essence of C The l, h and L Modifiers The l and h modifiers may prefix the d , i , o , u , and x type specifiers. type follows. h tells printf() to display a short int . The L modifier may also prefix the floating point specifiers e , f , and follows. g l tells printf() that a long data double and indicates that a long The # Modifier Preceding g , f , or decimal digits. e specifiers with a # ensures that there will be a decimal point even if there are no Preceding the x specifier with a # ensures that the hexadecimal number will be printed with a 0x prefix. The * Modifier The minimum field width and precision specifiers are usually constants; however, they may also be provided by arguments to printf() . To achieve this, * is used as a placeholder. For example, in: printf("%*.*f", 10, 4, 1234.34); the minimum field width is 10, the precision is 4 and the value to be displayed is 1234.34. scanf() is the general purpose console input function. It can read in all of the built-in data types and automatically convert numbers into the proper internal format. The prototype is: scanf() int scanf(char *control_string, argument_list); scanf() returns the number of data items successfully assigned a value. If an error occurs, returned. EOF is Format Specifiers The input format specifiers tell scanf() what type of data is to be read next. The codes are as follows: Specifier %c %d %i %e %f %g %o %s %x %p %n %u % Description Read a single character. Read a decimal integer. Read a decimal integer. Read a floating-point number. Read a floating-point number. Read a floating-point number. Read an octal number. Read a string. Read a hexadecimal number. Read a pointer. Receive an integer value equal to the number of characters read so far. Read an unsigned integer. Scan for a set of characters. Reading Strings causes scanf() to read characters until it encounters a white-space character (RETURN, TAB or space). The characters read are placed in a character array pointed to be the corresponding argument, and null-terminated. %s Essence of C 45 Unlike gets() , which reads a string until RETURN is typed, scanf() reads a string until the first white space is encountered. The %n Specifier The %n specifier instructs scanf() to assign the number of characters read at the point at which the was encountered to the variable pointed to by the corresponding argument. %n Using a Scanset The ANSI standard added the new scanset feature. A scanset defines a set of characters which may be read and assigned to the corresponding character array. A scanset is defined by placing the characters inside square brackets prefixed with a % , as in the following example: %["XYZ"] will then continue to read characters and continue to put them into the array until it encounters a character not in the scanset. For example, given the following code: scanf() scanf(%d%[abcdefg]%s", &i, str, str2); printf(%d %s %s, i, str, str2); entering 123abcdtye followed by ENTER would display: 123 abcd tye because the t is not part of the scanset, causing it and the remaining characters to be put into str2 . If the first character in the set is a ^ , scanf() will accept any character not defined by the scanset. A range may be specified using a hyphen. Scansets are case sensitive. The Need For Addresses All the variables used to receive values through scanf() must be passed by their addresses, which means that all arguments must be pointers to the variables used as arguments. This is, of course, C's way of creating a call-by-reference, which is a way of allowing a function to alter the contents of its calling argument. This is illustrated in the following example: scanf("%d", &count); scanf("%s", str); The second line in the above code works because an array name, without an index, is the address of the first element of the array. Hence the & operator is not required. Format Modifiers An integer, placed between the % and the format, limits the number of characters read for that field. (Any characters in the input stream outside this limit will be placed in the relevant variable at the next scanf() call.) l placed in front of f, e and g causes scanf() to assign the data to a double. variable receiving the data is a long double . L tells scanf() that the Suppressing Input scanf() may be told to read a field but not assign it to any variable by preceding that field's format code with a * . For example, given: scanf("%d*c%d", &x, &y); 46 Essence of C the coordinate pair 10,10 could be entered. The comma would be read but not assigned to anything. Assignment suppression is particularly useful when part of what is input needs to be suppressed. Essence of C 47 48 Essence of C 9 DISK FILE INPUT/OUTPUT Streams and Files, File Pointer, Opening, Closing, Reading, Writing, Erasing, Random Access, Standard Streams Introduction As previously stated, there are no input/output (I/O) statements in C. Instead, all I/O operations take place through calls to functions in the standard C library, an approach which makes C's file system extremely powerful and flexible. C's I/O system is also unique in that data may be transferred in either: Its internal binary representation. Human-readable text format. ANSI C Versus UNIX The ANSI C standard defines a complete set of I/O functions which may be used to read and write data of any type. The old UNIX C standard contains two distinct systems of routines for I/O operations, viz: The buffered file system (or the formatted or high-level file system) which loosely parallels the one used by the ANSI standard. The unbuffered file system (or the UNIX-like or unformatted file system), which is not defined by the ANSI standard. Streams and Files The C I/O system supplies a consistent interface to the C programmer independent of the actual device being accessed. Put another way, the I/O system provides a level of abstraction between the programmer and the device being used. This abstraction is called the stream and the actual device is called the file. Streams Even though different devices are involved (terminals, disk drives, etc), the buffered file system transforms each into a logical device called a stream. Because streams are device-independent, the same function can write to a disk file or to another device, such as a console. There are two types of streams, ie: Essence of C 49 Text Streams. A text stream is a sequence of characters. In a text stream, certain character translations may occur (eg, a newline may be converted to a carriage return/line-feed pair). This means that there may not be a one-to-one relationship between the characters written and those in the external device. Binary Streams. A binary stream is a sequence of bytes with a one-to-one correspondence to those on the external device (ie, no translations occur). The number of bytes written or read is the same as the number on the external device. (However, an implementation-defined number of bytes may be appended to a binary stream (eg, to pad the information so that it fills a sector on a disk).) Files In C, a file may be anything from a disk file to a terminal or printer. A stream can be associated with a file by performing an open operation, following which information may be exchanged between the file and the program. All streams are the same but all files are not. For example, a disk file can support random access while a keyboard cannot. If a file can support random access (sometimes referred to as position requests), opening a file also initializes the file position indicator to the start of a file. This indicator is incremented as each character is read from, or written to, the file. The close operation disassociates a file from a specific stream. If the file was opened for output, the close operation will write the contents (if any) of the stream to the device. This process is usually called flushing the stream. All files are closed automatically when the program terminates, but not when it crashes. Each stream associated with a file has a file control structure of type FILE . File System Basics The ANSI file system comprises several interrelated functions. These are: Function fopen() fclose() putc() fputc() getc() fgetc() fseek() fprintf() fscanf() feof() ferror() rewind() remove() fflush() Description Opens a file. Closes a file. Writes a character. Writes a character. Reads a character. Reads a character. Seeks a specified byte in a file. Is to a file what printf() is to the console. Is to a file what scanf() is to a console. Returns TRUE if end-of-file is reached. Returns TRUE if an error is detected. Resets file position to beginning of file. Erases a file. Flushes a file. Most of these functions begin with the letter "f". This is a holdover from the UNIX C standard, which, as previously stated, defined two file systems. The UNIX I/O functions did not begin with a prefix and most of the formatted I/O system functions were prefixed with an "f". The ANSI standard maintained the convention in the interests of continuity. The header file stdio.h provides the prototypes for the I/O functions and defines these three types: typedef unsigned long size_t typedef unsigned long fpos_t typedef struct _FILE FILE 50 Essence of C stdio.h also defines the following: EOF SEEK_SET SEEK_CUR SEEK_END -1 0 1 2 // // // // value returned at end of file from beginning of file from current position from end of file The latter three are used with fseek() , which is the function which performs random access on a file. The File Pointer The file pointer is the common thread which unites the buffered I/O system. This is a pointer to information which defines various things about a file (including name, status and current position). In essence, the file pointer identifies a specific disk file and is used by the associated stream to direct the operation of the buffered I/O functions. A file pointer is a variable of type FILE that is defined in stdio.h. To obtain a file pointer variable, a statement like the following is used: FILE *fp; Opening a File opens a stream for use, links a file with that stream and returns a pointer associated with that file. The fopen() function has this prototype: fopen() FILE *fopen(const char * filename, const char * mode); where filename is a pointer to a string of characters that make a valid filename (and may include a path specification) and mode determines how the file will be opened. The legal values for mode are as follows: Value r w a rb wb ab r+ w+ a+ r+b w+b a+b Description Open a text file for reading. Create a text file for writing. Append to a text file. Open a binary file for reading. Create a binary file for writing. Append to a binary file. Open a text file for read/write. Create a text file for read/write. Append or create a text file for read/write. Open a binary file for read/write. Create a binary file for read/write. Append a binary file for read/write. As shown, a file may be opened in text or binary mode. In most implementations, in text mode, CR/LF sequences are translated to newline characters on input. On output, the reverse occurs. No such translation occurs on binary files. The following opens a file named TEST for writing: FILE *fp; fp = fopen("test","w"); However, because written as: fopen() returns a null pointer if an error occurs when a file is opened, this is better FILE *fp; if((fp = fopen("test","w")) == NULL) { printf("cannot open file\n"); exit(1); } Essence of C 51 Closing a File closes the stream, writes any data remaining in the disk buffer to the file, does a formal operating system level close on the file, and frees the associated file control block. fclose() has this prototype: fclose() int fclose(FILE *fp ); A return value of zero signifies a successful operation. Generally, has been prematurely removed or a disk is full. fclose() will fail only when a disk Writing a Character Characters are written using putc() or its equivalent fputc() . The prototype for putc() is: int putc(int ch , FILE *fp ); where ch is the character to be output. For historical reasons, ch is defined as an int , but only the low order byte is used. If the putc() operation is successful, it returns the character written, otherwise it returns EOF . Reading a Character Characters are read using getc() or its equivalent fgetc() . The prototype for getc() is: int getc(FILE *fp ); For historical reasons, getc() returns an integer, but the high order byte is zero. getc() returns an when the end of file has been reached. The following code reads a text file to the end: do { ch = getc(fp); } while(ch != EOF); EOF Using feof() As previously stated, the buffered file system can also operate on binary data. When a file is opened for binary input, an integer value equal to the EOF mark may be read, causing the input routine to indicate an EOF condition. To solve this problem, C includes the function feof() , which determines when the end of the file is reached when reading binary data. The prototype is: int feof(FILE *fp ); The following code reads a binary file until end of file is encountered: while(!feof(fp)) ch = getc(fp); Of course, this method can be applied to text files as well as binary files. Working With Strings and fgets() fputs() In addition to getc() and putc() , C supports the related functions and write character strings. They have the following prototypes: int fputs(const char *str, FILE *fp ); char *fgets(char *str, int length, FILE *fp ); fputs() and fgets() , which read 52 Essence of C The function fputs() works like puts() but writes the string to the specified stream. The fgets() function reads a string until either a newline character is read or length-1 characters have been read. If a newline is read, it will be part of the string (unlike gets() ). The resultant string will be nullterminated. rewind() rewind() resets the file position indicator to the beginning of the file. ferror() ferror() determines whether a file operation has produced an error. It returns TRUE if an error has occurred, otherwise it returns FALSE . ferror() should be called immediately after each file operation, otherwise an error may be lost. Erasing Files remove() erases a specified file. It returns zero if successful. Flushing a Stream fflush() flushes the contents of an output stream. fflush() writes the contents of any unbuffered data to the file associated with fp . It returns zero if successful. fread() and fwrite() To read and write data types which are longer than one byte, the ANSI standard provides fread() and fwrite(). These functions allow the reading and writing of blocks of any type of data. The prototypes are: size_t fread(void *buffer,size_t num_bytes, size_t count, FILE *fp ); size_t fwrite(const void *buffer,size_t num_bytes, size_t count, FILE *fp ); is a pointer to a region of memory which will receive the data from the file. For is a pointer to the information which will be written. The buffer may be simply the memory used to hold the variable, e.g., &l for a long integer. fwrite() , buffer For fread() , buffer The number of bytes to be read/written is specified by (each num_bytes in length) are read or written. num_bytes . count determines how many items count fread() returns the number of items read. This value may be less than reached or an error occurs. fwrite() returns the number of items written. if the end of file is One of the most useful applications of fread() and fwrite() involves reading and writing user-defined data types, especially structures. For example, given this structure: struct struct_type { float balance; char name[80]; } cust; the following statement writes the contents of cust: fwrite(&cust, sizeof(struct struct_type),1,fp); fseek() and Random Access I/O Random read and write operations may be performed with the help of position locator. The prototype is: int fseek(FILE *fp , long numbytes, int origin); fseek() , which sets the file Essence of C 53 in which numbytes is the number of bytes from the origin, which will become the new current position, and origin is one of the following macros defined in stdio.h: Origin Beginning of file Current position End-of-file fseek() Macro Name SEEK_SET SEEK_CUR SEEK_END returns 0 when successful and a non-zero value if an error occurs. fseek() may be used to seek in multiples of any type of data by simply multiplying the size of the data by the number of the item to be reached, eg: fseek(fp, 9*sizeof(struct list), SEEK_SET); which seeks the tenth address. fprint() and fscanf() fprintf() and fscanf() behave exactly like The prototypes are: printf() and scanf() except that they operate with files. int fprintf(FILE *fp , const char *control_string, ...); int fscanf(FILE *fp, const char *control_string, ...); Although these functions are often the easiest way to read and write assorted data, they are not always the most efficient. Because formatted ASCII data is being written as it would appear on the screen (instead of in binary), extra overhead is incurred with each call. If speed or file size is of concern, use fread() and fwrite() . The Standard Streams Whenever a C program starts execution, three streams are opened automatically. These are: Standard input (stdin ). Standard output (stdout ). Standard error (stderr ). Normally, these streams refer to the console, but they may be redirected by the operating system to some other device or environment. Because the standard streams are file pointers, they may be used to provide buffered I/O operations on the console, e.g.: putchar(char c) { putc(c,stdout); } Demonstration File I/O Programs The following programs demonstrate the use of C's file I/O functions. // // // // // // // ******************************************************************* DEMO OF FOPEN(), FCLOSE(), GETC(), PUTC() ------------------------------------------------------------------Specify filename on command line. Input charas on keyboard until $ is entered. File is then closed. File is then re-opened and read. ******************************************************************* #include <stdio.h> #include <stdlib.h> void main(int argc, char *argv) 54 Essence of C { FILE *fp; char ch; if(argc!=2) { printf("You forgot to enter the filename\n"); exit(1); } if((fp=fopen(argv[1], "w"))==NULL) { printf("Cannot open file\n"); exit(1); } do { ch=getchar(); putc(ch,fp); } while(ch!='$'); fclose(fp); if((fp=fopen(argv[1], "r"))==NULL) { printf("Cannot open file\n"); exit(1); } ch=getc(fp); while(ch!=EOF) { putchar(ch); ch=getc(fp); } fclose(fp); } // // // // // // // // // ******************************************************************* DEMO OF FEOF() TO CHECK FOR EOF CONDITION IN BINARY FILES ------------------------------------------------------------------Specify filenames for input and output on command line. The program copies the source file to the destination file. feof() checks for end of file condition. (Note: feof() can also be used for text files.) ******************************************************************* // close file // open file // open file // file pointer // get keyboard chars until `$' // read one chara // print on screen // read another chara // close file #include <stdio.h> #include <stdlib.h> void main(int argc, char *argv) { FILE *in, *out; char ch; if(argc!=3) { printf("You forgot to enter the filenames\n"); exit(1); } if((in=fopen(argv[1], "rb"))==NULL) { printf("Cannot open source file\n"); exit(1); } if((out=fopen(argv[2], "wb"))==NULL) { printf("Cannot open destination file\n"); exit(1); // open source file // for read binary // file pointers // open dest file // for write binary Essence of C 55 } while(!feof(in)) { ch=getc(in); if(!feof(in)) putc(ch,out); } fclose(in); fclose(out); } // // // // // // // // // ******************************************************************* DEMO OF FPUTS(), FGETC AND REWIND() ------------------------------------------------------------------Strings are entered from keyboard until blank line is entered. Strings are then written to as file called `testfile'. (Since gets() does not store the newline character, one is added before the string is written so that the file can be read more easily.) The file is then rewound, input and displayed. ******************************************************************* // here it is // and again // close files #include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { FILE *fp; char str[80]; if((fp=fopen("testfile", "w+"))==NULL) { printf("Cannot open file\n"); exit(1); } do { // file pointer // open file for // text read & write // get strings until CR and write to file printf("Enter a string (CR to quit):\n"); gets(str); strcat(str, "\n"); fputs(str,fp); } while(*str!='\n'); rewind(fp); while(!feof(fp) { fgets(str,79,fp); printf(str); } fclose(fp); } // // // // // // // // // ******************************************************************* DEMO OF FREAD() AND FWRITE() (FOR DATA TYPES LONGER THAN BYTE) ------------------------------------------------------------------Writes, then reads back, a double, an int and a long. (Notice how sizeof() is used to determine the length of each data type.) Note that these functions are useful for reading and writing user-defined data types, especially structures. ******************************************************************* // rewind // read and display file // close file #include <stdio.h> #include <stdlib.h> void main(void) { FILE *fp; double d=12.23; 56 Essence of C int i=101; long l=123023; if((fp=fopen("testfile", "wb+"))==NULL) { printf("Cannot open file\n"); exit(1); } // open for binary // read & write // parameters are: *buffer, number of bytes, count of items, // file pointer fwrite(&d, sizeof(double), 1, fp); fwrite(&i, sizeof(int), 1, fp); fwrite(&l, sizeof(long), 1, fp); rewind(fp); fread(&d, sizeof(double), 1, fp); fread(&i, sizeof(int), 1, fp); fread(&l, sizeof(long), 1, fp); printf("%2.3f fclose(fp); } // // // // // // // // ******************************************************************* DEMO OF FPRINTF() AND FSCANF() ------------------------------------------------------------------Reads a string and an integer from the keyboard, writes them to a file, then reads the file and displays the data. (Note that these functions are an easy way to write assorted data to files but are very slow compared with fread() and fwrite(). ******************************************************************* %d %ld\n",d,i,l); #include <stdio.h> #include <stdlib.h> #include <exec/io.h> void main(void) { FILE *fp; char s[80]; int t; if((fp=fopen("testfile", "w"))==NULL) { printf("Cannot open file\n"); exit(1); } printf("Enter a string and a number: "); // parameters are: FILE *fp, const char *control_string, ... fscanf(stdin,"%s%d", s, &t); fprintf(fp,"%s %d", s, t); fclose(fp); if((fp=fopen("testfile", "r"))==NULL) { printf("Cannot open file\n"); exit(1); } fscanf(fp,"%s%d",s,&t); fprintf(stdout, "%s %d\n", s, t); fclose(fp) } // open for text read // open for text write Essence of C 57 58 Essence of C 10 THE C PREPROCESSOR The C Preprocessor As defined by the ANSI standard, the C preprocessor contains the following directives: #if #ifdef #ifndef #else #elif #include define #undef #line #error #pragma #define defines an identifier (the macro name) and a string (the macro substitution) which will be substituted for the identifier each time the identifier is encountered in the source file. #define Once a macro name has been defined, it may be used as part of the definition of other macro names. If the string is longer than one line, it may be continued by placing a backslash on the end of the first line. By convention, C programmers use uppercase for defined identifiers. Example macro #define s are: #define TRUE #define FALSE 1 0 The macro name may have arguments, in which case every time the macro name is encountered, the arguments associated with it are replaced by the actual arguments found in the program, as in: #define ABS(a) (a)<0 ? -(a) : (a) ... printf("abs of -1 and 1: %d %d", ABS(-1), ABS(1)); Such macro substitutions in place of real functions increase the speed of the code at the price of increased program size. #error #error forces the compiler to stop compilation. It is used primarily for debugging. The general form is: #error error_message When the directive is encountered, the error message is displayed, possibly along with other information (depending on the compiler). Essence of C 59 #include #include instructs the compiler to read another source file, which must be included between double quotes or angle brackets. Examples are: #include "stdio.h" #include <stdio.h> which both instruct the compiler to read and compile the named header file. If a file name is enclosed in angle brackets, the file is searched for as specified by the creator of the compiler. If the name is enclosed in double quotes, the file is searched for in an implementationdefined manner, which generally means searching the current directory. (If the file is not found, the search is repeated as if the name had been enclosed in angle brackets.) Conditional Compilation - #if, #else, #elif, and #endif Several directives control the selective compilation of portions of the program code, viz, #elif , and #endif . #if , #else , The general form of #if is: #if constant_expression statement sequence #endif #else works much like the C keyword else. compilation chain. #elif means "else if" and establishes an if-else-if Amongst other things, #if provides an alternative method of "commenting out" code. For example, in the following: #if 0 printf("#d",total); #endif the compiler will ignore printf("#d",total); . #ifdef and #ifndef #Ifdef means "if defined", and is terminated by an #endif . #indef means "if not defined". #undef #undef removes a previously defined definition. #line changes the contents of __LINE__ (which contains the line number of the currently compiled code) and __FILE__ (which is a string which contains the name of the source file being compiled), both of which are predefined identifiers in the compiler. #line #pragma The #pragma directive is an implementation-defined directive which allows various instructions to be given to the compiler. 60 Essence of C The # and ## Preprocessor Operators The # and ## preprocessor operators are used when using a macro #define . The # operator turns the argument it precedes into a quoted string. For example, given: #define mkstr(s) # s the preprocessor turns the line printf(mkstr(I like C); into printf("I like C"); The ## operator concatenates two tokens. For example, given: #define concat(a, b) a ## b int xy=10; printf("%d",concat(x, y); the preprocessor turns the last line into: printf("%d", xy); Predefined Macro Names The ANSI standard specifies five built-in predefined macro names. These are: __LINE__ __FILE__ __DATE__ __TIME__ __STDC__ __LINE__ and __FILE__ have already been discussed. __DATE__ and __TIME__ contain strings of the form month/date/year and hour/minute/second which represent the date and time of compilation. contains the decimal constant 1, which means that the implementation conforms to the standard. If it contains any other number, the implementation varies from the standard. __STDC__ Essence of C 61 62 Essence of C 11 LIBRARIES, LINKING AND HEADER FILES The Linker The output of the compiler is relocatable object code. The linker has two functions: The first is to combine (link) various pieces of object code. The second is to resolve the address of jump and call instructions found in the relocatable format of the object file. The output of the linker is an executable file. Linking With Overlays Many C compiler manufacturers supply an overlay linker. An overlay linker works like a normal linker but can also create overlays. An overlay is a piece of object code that is stored in a disk file and loaded and executed only when needed. The place in memory where an overlay is loaded is called an overlay region. Overlays allow the creation of programs which are larger than available memory. Sometimes a better alternative to overlays is chaining, in which one program instructs the operating system to load and execute another program. Some compilers provide a chaining function, which is often called exec(). The C Standard Library The C standard library provides the object code for the functions provided with the compiler. The ANSI standard has defined both the content and the form of the C standard library; however, many compilers supply additional functions. Libraries Versus Object Files Libraries are similar to object files; however, the difference is that not all of the contents of the library is added to the program during linking. Unlike an object file, a library file stores the name of each function in the file, enabling the linker to look for the functions referenced in the program and add that code to the program. Essence of C 63 Header Files Many functions in the standard library work with their own specific data types, to which the program must have access. These types are defined in header files supplied with the compiler and must be #include d in any file which uses the specific functions to which they refer. If the header files conform to the ANSI standard, they also contain the function prototypes for the functions related to that file, providing stronger type checking during compilation. The standard header files defined by the ANSI standard are as follows: Header File ASSERT.H CTYPE.H ERRNO.H FLOAT.H LIMITS.H LOCALE.H MATH.H SETJMP.H SIGNAL.H STDARG.H STDDEF.H STDIO.H STDLIB.H STRING.H TIME.H Description Defines the assert() macro. Character handling. Error reporting. Defines implementation-dependent floating point values. Defines implementation-dependent various limits. Supports the setlocal() function. Various definitions used by the math library. Supports non-local jumps. Defines signal values. Supports variable-length argument lists. Defines some commonly used constants. Supports file I/O. Miscellaneous declarations. Supports string functions. Supports system time functions. Macros in Header Files Many of the C standard functions are actually macro definitions contained in a header file. 64 Essence of C ...
View Full Document

This note was uploaded on 02/07/2012 for the course CS 1372 taught by Professor Smith during the Spring '07 term at Georgia Institute of Technology.

Ask a homework question - tutors are online