BUY THIS BOOK
Add to Cart

Print Book $39.95


Add to Cart

Print+PDF $51.94

Add to Cart

PDF $31.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £28.50

What is this?

Looking to Reprint or License this content?


C in a Nutshell
C in a Nutshell By Peter Prinz, Tony Crawford
December 2005
Pages: 618

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Language Basics
This chapter describes the basic characteristics and elements of the C programming language.
C is a general-purpose, procedural programming language. Dennis Ritchie first devised C in the 1970s at AT&T Bell Laboratories in Murray Hill, New Jersey, for the purpose of implementing the Unix operating system and utilities with the greatest possible degree of independence from specific hardware platforms. The key characteristics of the C language are the qualities that made it suitable for that purpose:
  • Source code portability
  • The ability to operate "close to the machine"
  • Efficiency
As a result, the developers of Unix were able to write most of the operating system in C, leaving only a minimum of system-specific hardware manipulation to be coded in assembler.
C's ancestors are the typeless programming languages BCPL (the Basic Combined Programming Language), developed by Martin Richards; and B, a descendant of BCPL, developed by Ken Thompson. A new feature of C was its variety of data types : characters, numeric types, arrays, structures, and so on. Brian Kernighan and Dennis Ritchie published an official description of the C programming language in 1978. As the first de facto standard, their description is commonly referred to simply as "K&R." C owes its high degree of portability to a compact core language that contains few hardware-dependent elements. For example, the C language proper has no file access or dynamic memory management statements . In fact, there aren't even any statements for console input and output. Instead, the extensive standard C library provides the functions for all of these purposes.
This language design makes the C compiler relatively compact and easy to port to new systems. Furthermore, once the compiler is running on a new system, you can compile most of the functions in the standard library with no further modification, because they are in turn written in portable C. As a result, C compilers are available for practically every computer system.
Because C was expressly designed for system programming, it is hardly surprising that one of its major uses today is in programming embedded systems. At the same time, however, many developers use C as a portable, structured high-level language to write programs such as powerful word processor, database, and graphics applications.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Characteristics of C
C is a general-purpose, procedural programming language. Dennis Ritchie first devised C in the 1970s at AT&T Bell Laboratories in Murray Hill, New Jersey, for the purpose of implementing the Unix operating system and utilities with the greatest possible degree of independence from specific hardware platforms. The key characteristics of the C language are the qualities that made it suitable for that purpose:
  • Source code portability
  • The ability to operate "close to the machine"
  • Efficiency
As a result, the developers of Unix were able to write most of the operating system in C, leaving only a minimum of system-specific hardware manipulation to be coded in assembler.
C's ancestors are the typeless programming languages BCPL (the Basic Combined Programming Language), developed by Martin Richards; and B, a descendant of BCPL, developed by Ken Thompson. A new feature of C was its variety of data types : characters, numeric types, arrays, structures, and so on. Brian Kernighan and Dennis Ritchie published an official description of the C programming language in 1978. As the first de facto standard, their description is commonly referred to simply as "K&R." C owes its high degree of portability to a compact core language that contains few hardware-dependent elements. For example, the C language proper has no file access or dynamic memory management statements . In fact, there aren't even any statements for console input and output. Instead, the extensive standard C library provides the functions for all of these purposes.
This language design makes the C compiler relatively compact and easy to port to new systems. Furthermore, once the compiler is running on a new system, you can compile most of the functions in the standard library with no further modification, because they are in turn written in portable C. As a result, C compilers are available for practically every computer system.
Because C was expressly designed for system programming, it is hardly surprising that one of its major uses today is in programming embedded systems. At the same time, however, many developers use C as a portable, structured high-level language to write programs such as powerful word processor, database, and graphics applications.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Structure of C Programs
The procedural building blocks of a C program are functions, which can invoke one another. Every function in a well-designed program serves a specific purpose. The functions contain statements for the program to execute sequentially, and statements can also be grouped to form block statements, or blocks. As the programmer, you can use the ready-made functions in the standard library, or write your own whenever no standard function fulfills your intended purpose. In addition to the standard C library, there are many specialized libraries available, such as libraries of graphics functions. However, by using such nonstandard libraries, you limit the portability of your program to those systems to which the libraries themselves have been ported.
Every C program must define at least one function of its own, with the special name main(): this is the first function invoked when the program starts. The main() function is the program's top level of control, and can call other functions as subroutines.
Example 1-1 shows the structure of a simple, complete C program. We will discuss the details of declarations, function calls, output streams and more elsewhere in this book. For now, we are simply concerned with the general structure of the C source code. The program in Example 1-1 defines two functions, main() and circularArea(). The main() function calls circularArea() to obtain the area of a circle with a given radius, and then calls the standard library function printf() to output the results in formatted strings on the console.
Example 1-1. A simple C program
// circle.c: Calculate and print the areas of circles

#include <stdio.h>                // Preprocessor directive

double circularArea( double r );  // Function declaration (prototype form)

int main()                        // Definition of main() begins
{
  double radius = 1.0, area = 0.0;

  printf( "    Areas of Circles\n\n" );
  printf( "     Radius          Area\n"
          "-------------------------\n" );

  area = circularArea( radius );
  printf( "%10.1f     %10.2f\n", radius, area );

  radius = 5.0;
  area = circularArea( radius );
  printf( "%10.1f     %10.2f\n", radius, area );

  return 0;
}

// The function circularArea() calculates the area of a circle
// Parameter:    The radius of the circle
// Return value: The area of the circle

double circularArea( double r )      // Definition of circularArea() begins
{
  const double pi = 3.1415926536;    // Pi is a constant
  return  pi * r * r;
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Source Files
The function definitions, global declarations and preprocessing directives make up the source code of a C program. For small programs, the source code is written in a single source file. Larger C programs consist of several source files . Because the function definitions generally depend on preprocessor directives and global declarations, source files usually have the following internal structure:
  1. Preprocessor directives
  2. Global declarations
  3. Function definitions
C supports modular programming by allowing you to organize a program in as many source and header files as desired, and to edit and compile them separately. Each source file generally contains functions that are logically related, such as the program's user interface functions. It is customary to label C source files with the filename suffix .c .
Examples 1-2 and 1-3 show the same program as Example 1-1, but divided into two source files.
Example 1-2. The first source file, containing the main() function
// circle.c: Prints the areas of circles.
// Uses circulararea.c for the math

#include <stdio.h>
double circularArea( double r );

int main()
{
  /* ... As inExample 1-1 ... */
}
Example 1-3. The second source file, containing the circularArea() function
// circulararea.c: Calculates the areas of circles.
// Called by main() in circle.c

double circularArea( double r )
{
  /* ... As inExample 1-1 ... */
}
When a program consists of several source files, you need to declare the same functions and global variables, and define the same macros and constants, in many of the files. These declarations and definitions thus form a sort of file header that is more or less constant throughout a program. For the sake of simplicity and consistency, you can write this information just once in a separate header file, and then reference the header file using an #include directive in each source code file. Header files are customarily identified by the filename suffix .h . A header file explicitly included in a C source file may in turn include other files.
Each C source file, together with all the header files included in it, makes up a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Comments
You should use comments generously in the source code to document your C programs. There are two ways to insert a comment in C: block comments begin with /* and end with */, and line comments begin with // and end with the next new line character.
You can use the /* and */ delimiters to begin and end comments within a line, and to enclose comments of several lines. For example, in the following function prototype, the ellipsis (...) signifies that the open() function has a third, optional parameter. The comment explains the usage of the optional parameter:
    int open( const char *name, int mode, ... /* int permissions */ );
You can use // to insert comments that fill an entire line, or to write source code in a two-column format, with program code on the left and comments on the right:
    const double pi = 3.1415926536;     // Pi is constant
These line comments were officially added to the C language by the C99 standard, but most compilers already supported them even before C99. They are sometimes called "C++-style" comments, although they originated in C's forerunner, BCPL.
Inside the quotation marks that delimit a character constant or a string literal, the characters /* and // do not start a comment. For example, the following statement contains no comments:
    printf( "Comments in C begin with /* or //.\n" );
The only thing that the preprocessor looks for in examining the characters in a comment is the end of the comment; thus it is not possible to nest block comments. However, you can insert /* and */ to comment out part of a program that contains line comments:
    /* Temporarily removing two lines:
      const double pi = 3.1415926536;     // Pi is constant
      area = pi * r * r                   // Calculate the area
       Temporarily removed up to here */
If you want to comment out part of a program that contains block comments, you can use a conditional preprocessor directive (described in Chapter 14):
    #if 0
      const double pi = 3.1415926536;     /* Pi is constant     */
      area = pi * r * r                   /* Calculate the area */
    #endif
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Character Sets
C makes a distinction between the environment in which the compiler translates the source files of a program—the translation environment —and the environment in which the compiled program is executed, the execution environment. Accordingly, C defines two character sets : the source character set is the set of characters that may be used in C source code, and the execution character set is the set of characters that can be interpreted by the running program. In many C implementations, the two character sets are identical. If they are not, then the compiler converts the characters in character constants and string literals in the source code into the corresponding elements of the execution character set.
Each of the two character sets includes both a basic character set and extended characters . The C language does not specify the extended characters, which are usually dependent on the local language. The extended characters together with the basic character set make up the extended character set .
The basic source and execution character sets both contain the following types of characters:
The letters of the Latin alphabet
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
The decimal digits
0 1 2 3 4 5 6 7 8 9
The following 29 punctuation marks
! " # % & ' () * + , − . / : ; < = > ? [ \ ] ^ _ { | } ~
The five whitespace characters
Space, horizontal tab, vertical tab, new line, and form feed
The basic execution character set also includes four nonprintable characters : the null character, which acts as the termination mark in a character string; alert; backspace; and carriage return. To represent these characters in character and string literals, type the corresponding escape sequences beginning with a backslash: \0 for the null character, \a for alert, \b for backspace, and \r for carriage return. See Chapter 3 for more details.
The actual numeric values of characters—the character codes —may vary from one C implementation to another. The language itself imposes only the following conditions:
  • Each character in the basic character set must be representable in one byte.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Identifiers
The term identifier refers to the names of variables, functions, macros, structures and other objects defined in a C program. Identifiers can contain the following characters:
  • The letters in the basic character set, a-z and A-Z. Identifiers are case-sensitive.
  • The underscore character, _.
  • The decimal digits 0-9, although the first character of an identifier must not be a digit.
  • Universal character names that represent the letters and digits of other languages.
The permissible universal characters are defined in Annex D of the C standard, and correspond to the characters defined in the ISO/IEC TR 10176 standard, minus the basic character set.
Multibyte characters may also be permissible in identifiers . However, it is up to the given C implementation to determine exactly which multibyte characters are permitted and what universal character names they correspond to.
The following 37 keywords are reserved in C, each having a specific meaning to the compiler, and must not be used as identifiers:
auto
enum
restrict
unsigned
break
extern
return
void
case
float
short
volatile
char
for
signed
while
const
goto
sizeof
_Bool
continue
if
static
_Complex
default
inline
struct
_Imaginary
do
int
switch
double
long
typedef
else
register
union
The following examples are valid identifiers:
x dollar Break error_handler scale64
The following are not valid identifiers:
1st_rank switch y/n x-ray
If the compiler supports universal character names, then α is also an example of a valid identifier, and you can define a variable by that name:
    doubleα = 0.5;
Your source code editor might save the character α in the source file as the universal character \u03B1.
When choosing identifiers in your programs, remember that many identifiers are already used by the C standard library. These include the names of standard library functions, which you cannot use for functions you define or for global variables. See Chapter 15 for details.
The C compiler provides the predefined identifier _ _func_ _, which you can use in any function to access a string constant containing the name of the function. This is useful for logging or for debugging output; for example:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
How the C Compiler Works
Once you have written a source file using a text editor, you can invoke a C compiler to translate it into machine code. The compiler operates on a translation unit consisting of a source file and all the header files referenced by #include directives. If the compiler finds no errors in the translation unit, it generates an object file containing the corresponding machine code. Object files are usually identified by the filename suffix .o or .obj . In addition, the compiler may also generate an assembler listing (see Part III).
Object files are also called modules. A library, such as the C standard library, contains compiled, rapidly accessible modules of the standard functions.
The compiler translates each translation unit of a C program—that is, each source file with any header files it includes—into a separate object file. The compiler then invokes the linker, which combines the object files, and any library functions used, in an executable file. Figure 1-1 illustrates the process of compiling and linking a program from several source files and libraries. The executable file also contains any information that the target operating system needs to load and start it.
Figure 1-1: From source code to executable file
The compiling process takes place in eight logical steps. A given compiler may combine several of these steps, as long as the results are not affected. The steps are:
  1. Characters are read from the source file and converted, if necessary, into the characters of the source character set. The end-of-line indicators in the source file, if different from the new line character, are replaced. Likewise, any trigraph sequences are replaced with the single characters they represent. (Digraphs, however are left alone; they are not converted into their single-character equivalents.)
  2. Wherever a backslash is followed immediately by a newline character, the preprocessor deletes both. Since a line end character ends a preprocessor directive, this processing step lets you place a backslash at the end of a line in order to continue a directive, such as a macro definition, on the next line.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Types
Programs have to store and process different kinds of data, such as integers and floating-point numbers, in different ways. To this end, the compiler needs to know what kind of data a given value represents.
In C, the term object refers to a location in memory whose contents can represent values. Objects that have names are also called variables . An object's type determines how much space the object occupies in memory, and how its possible values are encoded. For example, the same pattern of bits can represent completely different integers depending on whether the data object is interpreted as signed—that is, either positive or negative—or unsigned, and hence unable to represent negative values.
The types in C can be classified as follows:
  • Basic type
    • Standard and extended integer types
    • Real and complex floating-point types
  • Enumerated types
  • The type void
  • Derived types
    • Pointer types
    • Array types
    • Structure types
    • Union types
    • Function types
The basic types and the enumerated types together make up the arithmetic types . The arithmetic types and the pointer types together are called the scalar types . Finally, array types and structure types are referred to collectively as the aggregate types . (Union types are not considered aggregate, because only one of their members can store a value at any given time.)
A function type describes the interface to a function; that is, it specifies the type of the function's return value, and may also specify the types of all the parameters that are passed to the function when it is called.
All other types describe objects. This description may or may not include the object's storage size: if it does, the type is properly called an object type ; if not, it is an incomplete type . An example of an incomplete type might be an externally defined array variable:
    extern float fArr[ ];     // External declaration
This line declares fArr as an array whose elements have type float. However, because the array's size is not specified here, fArr's type is incomplete. As long as the global array fArr is defined with a specified size at another location in the program—in another source file, for example—this declaration is sufficient to let you use the array in its present scope. (For more details on external declarations, see Chapter 11.)
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Typology
The types in C can be classified as follows:
  • Basic type
    • Standard and extended integer types
    • Real and complex floating-point types
  • Enumerated types
  • The type void
  • Derived types
    • Pointer types
    • Array types
    • Structure types
    • Union types
    • Function types
The basic types and the enumerated types together make up the arithmetic types . The arithmetic types and the pointer types together are called the scalar types . Finally, array types and structure types are referred to collectively as the aggregate types . (Union types are not considered aggregate, because only one of their members can store a value at any given time.)
A function type describes the interface to a function; that is, it specifies the type of the function's return value, and may also specify the types of all the parameters that are passed to the function when it is called.
All other types describe objects. This description may or may not include the object's storage size: if it does, the type is properly called an object type ; if not, it is an incomplete type . An example of an incomplete type might be an externally defined array variable:
    extern float fArr[ ];     // External declaration
This line declares fArr as an array whose elements have type float. However, because the array's size is not specified here, fArr's type is incomplete. As long as the global array fArr is defined with a specified size at another location in the program—in another source file, for example—this declaration is sufficient to let you use the array in its present scope. (For more details on external declarations, see Chapter 11.)
This chapter describes the basic types, enumerations and the type void. The derived types are described in Chapters 7 through 10.
Some types are designated by a sequence of more than one keyword, such as unsigned short. In such cases, the keywords can be written in any order. However, there is a conventional keyword order, which we use in this book.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Integer Types
There are five signed integer types . Most of these types can be designated by several synonyms, which are listed in Table 2-1.
Table 2-1: Standard signed integer types
Type
Synonyms
signed char
int
signed, signed int
short
short int, signed short, signed short int
long
long int, signed long, signed long int
long long (C99)
long long int, signed long long, signed long long int
For each of the five signed integer types in Table 2-1, there is also a corresponding unsigned type that occupies the same amount of memory, with the same alignment: in other words, if the compiler aligns signed int objects on even-numbered byte addresses, then unsigned int objects are also aligned on even addresses. These unsigned types are listed in Table 2-2.
Table 2-2: Unsigned standard integer types
Type
Synonyms
_Bool
bool (defined in stdbool.h )
unsigned char
unsigned int
unsigned
unsigned short
unsigned short int
unsigned long
unsigned long int
unsigned long long
unsigned long long int
C99 introduced the unsigned integer type _Bool to represent Boolean truth values. The Boolean value true is coded as 1, and false is coded as 0. If you include the header file stdbool.h in a program, you can also use the identifiers bool, true, and false, which are familiar to C++ programmers. The macro bool is a synonym for the type _Bool, and true and false are symbolic constants equal to 1 and 0.
The type char is also one of the standard integer types. However, the one-word type name char is synonymous either with signed char or with unsigned char, depending on the compiler. Because this choice is left up to the implementation, char, signed char, and unsigned char are formally three different types.
If your program relies on char being able to hold values less than zero or greater than 127, you should be using either signed char or unsigned char instead.
You can do arithmetic with character variables. It's up to you to decide whether your program interprets the number in a char variable as a character code or as something else. For example, the following short program treats the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Floating-Point Types
C also includes special numeric types that can represent nonintegers with a decimal point in any position. The standard floating-point types for calculations with real numbers are as follows:
float
For variables with single precision
double
For variables with double precision
long double
For variables with extended precision
A floating-point value can be stored only with a limited precision, which is determined by the binary format used to represent it and the amount of memory used to store it. The precision is expressed as a number of significant digits. For example, a "precision of six decimal digits" or "six-digit precision" means that the type's binary representation is precise enough to store a real number of six decimal digits, so that its conversion back into a six-digit decimal number yields the original six digits. The position of the decimal point does not matter, and leading and trailing zeros are not counted in the six digits. The numbers 123,456,000 and 0.00123456 can both be stored in a type with six-digit precision.
In C, arithmetic operations with floating-point numbers are performed internally with double or greater precision. For example, the following product is calculated using the double type.
    float height = 1.2345, width = 2.3456;  // Float variables have single
                                            // precision.
    double area = height * width;           // The actual calculation is
                                            // performed with double
                                            // (or greater) precision.
If you assign the result to a float variable, the value is rounded as necessary. For more details on floating-point math, see the section "math.h" in Chapter 15.
C defines only minimal requirements for the storage size and the binary format of the floating-point types . However, the format commonly used is the one defined by the International Electrotechnical Commission (IEC) in the 1989 standard for binary floating-point arithmetic, IEC 60559. This standard is based in turn on the Institute of Electrical and Electronics Engineers' 1985 standard IEEE 754. Compilers can indicate that they support the IEC floating-point standard by defining the macro
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Complex Floating-Point Types (C99)
C99 supports mathematical calculations with complex numbers. The 1999 standard introduced complex floating-point types and extended the mathematical library to include complex arithmetic functions. These functions are declared in the header file complex.h , and include for example the trigonometric functions csin(), ctan(), and so on (see Chapter 15).
A complex number z can be represented in Cartesian coordinates as z = x + y × i, where x and y are real numbers, and i is the imaginary unit, defined by the equation i2 = -1. The number x is called the real part and y the imaginary part of z.
In C, a complex number is represented by a pair of floating-point values for the real and imaginary parts. Both parts have the same type, whether float, double, or long double. Accordingly, these are the three complex floating-point types:
  • float _Complex
  • double _Complex
  • long double _Complex
Each of these types has the same size and alignment as an array of two float, double, or long double elements.
The header file complex.h defines the macros complex and I. The macro complex is a synonym for the keyword _Complex. The macro I represents the imaginary unit i, and has the type const float _Complex:
    #include <complex.h>
    // ...
    double complex z = 1.0 + 2.0 * I;
    z *= I;      // Rotate z through 90° counterclockwise around the origin.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Enumerated Types
Enumerations are integer types that you define in a program. The definition of an enumeration begins with the keyword enum, possibly followed by an identifier for the enumeration, and contains a list of the type's possible values, with a name for each value:
    enum [identifier] { enumerator-list };
The following example defines the enumerated type enum color:
    enum color { black, red, green, yellow, blue, white=7, gray };
The identifier color is the tag of this enumeration. The identifiers in the list—black, red, and so on—are the enumeration constants , and have the type int. You can use these constants anywhere within their scope—as case constants in a switch statement, for example.
Each enumeration constant of a given enumerated type represents a certain value, which is determined either implicitly by its position in the list, or explicitly by initialization with a constant expression. A constant without an initialization has the value 0 if it is the first constant in the list, or the value of the preceding constant plus one. Thus in the previous example, the constants listed have the values 0, 1, 2, 3, 4, 7, 8.
Within an enumerated type's scope, you can use the type in declarations:
enum color bgColor = blue,         // Define two variables
               fgColor = yellow;       // of type enum color.
    void setFgColor( enum color fgc ); // Declare a function with a parameter
                                       // of type enum color.
An enumerated type always corresponds to one of the standard integer types. Thus your C programs may perform ordinary arithmetic operations with variables of enumerated types. The compiler may select the appropriate integer type depending on the defined values of the enumeration constants. In the previous example, the type char would be sufficient to represent all the values of the enumerated type enum color.
Different constants in an enumeration may have the same value:
    enum { OFF, ON, STOP = 0, GO = 1, CLOSED = 0, OPEN = 1 };
As the preceding example also illustrates, the definition of an enumerated type does not necessarily have to include a tag. Omitting the tag makes sense if you want only to define constants, and not declare any variables of the given type. Defining integer constants in this way is generally preferable to using a long list of
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Type void
The type specifier void indicates that no value is available. Consequently, you cannot declare variables or constants with this type. You can use the type void for the purposes described in the following sections.
A function with no return value has the type void. For example, the standard function perror() is declared by the prototype:
    void perror( const char * );
The keyword void in the parameter list of a function prototype indicates that the function has no parameters:
    FILE *tmpfile( void );
As a result, the compiler issues an error message if you try to use a function call such as tmpfile("name.tmp"). If the function were declared without void in the parameter list, the C compiler would have no information about the function's parameters, and hence be unable to determine whether the function call is correct.
A void expression is one that has no value. For example, a call to a function with no return value is an expression of type void:
    char filename[ ] = "memo.txt";
    if ( fopen( filename, "r" ) == NULL )
      perror( filename );             // A void expression.
The cast operation (void)expression explicitly discards the value of an expression, such as the return value of a function:
    (void)printf("I don't need this function's return value!\n");
A pointer of type void * represents the address of an object, but not its type. You can use such quasi-typeless pointers mainly to declare functions that can operate on various types of pointer arguments, or that return a "multipurpose" pointer. The standard memory management functions are a simple example:
    void *malloc( size_t size );
    void *realloc( void *ptr, size_t size );
    void free( void *ptr );
As Example 2-3 illustrates, you can assign a void pointer value to another object pointer type, or vice versa, without explicit type conversion.
Example 2-3. Using the type void
// usingvoid.c: Demonstrates uses of the type void
// -------------------------------------------------------
#include <stdio.h>
#include <time.h>
#include <stdlib.h>  // Provides the following function prototypes:
                     // void srand( unsigned int seed );
                     // int rand( void );
                     // void *malloc( size_t size );
                     // void free( void *ptr );
                     // void exit( int status );

enum { ARR_LEN = 100 };

int main()
{
  int i,                                // Obtain some storage space.
      *pNumbers = malloc(ARR_LEN * sizeof(int));

  if ( pNumbers == NULL )
  {
    fprintf(stderr, "Insufficient memory.\n");
    exit(1);
  }

  srand( (unsigned)time(NULL) );        // Initialize the
                                        // random number generator.

  for ( i=0; i < ARR_LEN; ++i )
    pNumbers[i] = rand() % 10000;          // Store some random numbers.

  printf("\n%d random numbers between 0 and 9999:\n", ARR_LEN );
  for ( i=0; i < ARR_LEN; ++i )         // Output loop:
  {
    printf("%6d", pNumbers[i]);         // Print one number per loop iteration
    if ( i % 10 == 9 ) putchar('\n');   // and a newline after every 10 numbers.
  }
  free( pNumbers );                     // Release the storage space.
  return 0;
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Literals
In C source code, a literal is a token that denotes a fixed value, which may be an integer , a floating-point number, a character, or a string. A literal's type is determined by its value and its notation.
The literals discussed here are different from compound literals , which were introduced in the C99 standard. Compound literals are ordinary modifiable objects, similar to variables. For a full description of compound literals and the special operator used to create them, see Chapter 5.
An integer constant can be expressed as an ordinary decimal numeral, or as a numeral in octal or hexadecimal notation. You must specify the intended notation by a prefix.
A decimal constant begins with a nonzero digit. For example, 255 is the decimal constant for the base-10 value 255.
A number that begins with a leading zero is interpreted as an octal constant. Octal (or base eight) notation uses only the digits from 0 to 7. For example, 047 is a valid octal constant representing 4 × 8 + 7, and is equivalent with the decimal constant 39. The decimal constant 255 is equal to the octal constant 0377.
A hexadecimal constant begins with the prefix 0x or 0X. The hexadecimal digits A to F can be upper- or lowercase. For example, 0xff, 0Xff, 0xFF, and 0XFF represent the same hexadecimal constant, which is equivalent to the decimal constant 255.
Because the integer constants you define will eventually be used in expressions and declarations, their type is important. The type of a constant is determined at the same time as its value is defined. Integer constants such as the examples just mentioned usually have the type int. However, if the value of an integer constant is outside the range of the type int, then it must have a bigger type. In this case, the compiler assigns it the first type in a hierarchy that is large enough to represent the value. For decimal constants, the type hierarchy is:
    int, long, long long
For octal and hexadecimal constants , the type hierarchy is:
    int, unsigned int, long, unsigned long, long long, unsigned long long
For example, on a 16-bit system, the decimal constant
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Integer Constants
An integer constant can be expressed as an ordinary decimal numeral, or as a numeral in octal or hexadecimal notation. You must specify the intended notation by a prefix.
A decimal constant begins with a nonzero digit. For example, 255 is the decimal constant for the base-10 value 255.
A number that begins with a leading zero is interpreted as an octal constant. Octal (or base eight) notation uses only the digits from 0 to 7. For example, 047 is a valid octal constant representing 4 × 8 + 7, and is equivalent with the decimal constant 39. The decimal constant 255 is equal to the octal constant 0377.
A hexadecimal constant begins with the prefix 0x or 0X. The hexadecimal digits A to F can be upper- or lowercase. For example, 0xff, 0Xff, 0xFF, and 0XFF represent the same hexadecimal constant, which is equivalent to the decimal constant 255.
Because the integer constants you define will eventually be used in expressions and declarations, their type is important. The type of a constant is determined at the same time as its value is defined. Integer constants such as the examples just mentioned usually have the type int. However, if the value of an integer constant is outside the range of the type int, then it must have a bigger type. In this case, the compiler assigns it the first type in a hierarchy that is large enough to represent the value. For decimal constants, the type hierarchy is:
    int, long, long long
For octal and hexadecimal constants , the type hierarchy is:
    int, unsigned int, long, unsigned long, long long, unsigned long long
For example, on a 16-bit system, the decimal constant 50000 has the type long, since the greatest possible int value is 32,767, or 215 − 1.
You can also influence the types of constants in your programs explicitly by using suffixes . A constant with the suffix l or L has the type long (or a larger type if necessary, in accordance with the hierarchies just mentioned). Similarly, a constant with the suffix ll or LL has at least the type long long. The suffix u or U can be used to ensure that the constant has an unsigned type. The
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Floating-Point Constants
Floating-point constants can be written either in decimal or in hexadecimal notation. These notations are described in the next two sections.
An ordinary floating-point constant consists of a sequence of decimal digits containing a decimal point. You may also multiply the value by a power of 10, as in scientific notation : the power of 10 is represented simply by an exponent, introduced by the letter e or E. A floating-point constant that contains an exponent does not need to have a decimal point. Table 3-2 gives a few examples of decimal floating-point constants .
Table 3-2: Examples of decimal floating-point constants
Floating-point constant
Value
10.0
10
2.34E5
2.34 × 105
67e-12
67.0 × 10−12
The decimal point can also be the first or last character . Thus 10. and .234E6 are permissible numerals. However, the numeral 10 with no decimal point would be an integer constant, not a floating-point constant.
The default type of a floating-point constant is double. You can also append the suffix F or f to assign a constant the type float, or the suffix L or l to give a constant the type long double, as this example shows:
    float  f_var = 123.456F;              // Initialize a float variable.

    long double ld_var = f_var * 987E7L;  // Initialize a long double variable
                                          // with the product of a
                                          // multiplication performed with
                                          // long double precision.
The C99 standard introduced hexadecimal floating-point constants , which have a key advantage over decimal floating-point numerals: if you specify a constant value in hexadecimal notation, it can be stored in the computer's binary floating-point format exactly, with no rounding error, whereas values that are "round numbers" in decimal notation—like 0.1—may be repeating fractions in binary, and have to be rounded for representation in the internal format. (For an example of rounding with floating-point numbers, see Example 2-2.)
A hexadecimal floating-point constant consists of the prefix
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Character Constants
A character constant consists of one or more characters enclosed in single quotation marks. Some examples:
    'a'   'XY'   '0'   '*'
All the characters of the source character set are permissible in character constants , except the single quotation mark ', the backslash \, and the newline character. To represent these characters, you must use escape sequences:
    '\''   '\\'   '\n'
All the escape sequences that are permitted in character constants are described in the upcoming section "Escape sequences."
Character constants have the type int, unless they are explicitly defined as wide characters, with type wchar_t, by the prefix L. If a character constant contains one character that can be represented in a single byte, then its value is the character code of that character in the execution character set. For example, the constant 'a' in ASCII encoding has the decimal value 97. The value of character constants that consist of more than one character can vary from one compiler to another.
The following code fragment tests whether the character read is a digit between 1 and 5, inclusive:
    #include <stdio.h>
    int c = 0;

    /* ... */

    c = getchar();                          // Read a character.
    if ( c != EOF && c > '0' && c < '6' )   // Compare input to character
                                            // constants.
    {
      /* This block is executed if the user entered a digit from 1 to 5. */
    }
If the type char is signed, then the value of a character constant can also be negative, because the constant's value is the result of a type conversion of the character code from char to int. For example, ISO 8859-1 is a commonly used 8-bit character set, also known as the ISO Latin 1 or ANSI character set . In this character set, the currency symbol for pounds sterling, £, is coded as hexadecimal A3:
    int c = '\xA3';                         // Symbol for pounds sterling
    printf("Character: %c     Code: %d\n", c, c);
If the execution character set is ISO 8859-1, and the type char is signed, then the printf statement in the preceding example generates the following output:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
String Literals
A string literal consists of a sequence of characters (and/or escape sequences) enclosed in double quotation marks. Example:
    "Hello world!\n"
Like character constants, string literals may contain all the characters in the source character set. The only exceptions are the double quotation mark ", the backslash \, and the newline character, which must be represented by escape sequences. The following printf statement first produces an alert tone, then indicates a documentation directory in quotation marks, substituting the string literal addressed by the pointer argument doc_path for the conversion specification %s:
    char doc_path[128] = ".\\share\\doc";
    printf("\aSee the documentation in the directory \"%s\"\n", doc_path);
A string literal is a static array of char that contains character codes followed by a string terminator, the null character \0 (see also Chapter 8). The empty string "" occupies exactly one byte in memory, which holds the terminating null character. Characters that cannot be represented in one byte are stored as multibyte characters.
As illustrated in the previous example, you can use a string literal to initialize a char array. A string literal can also be used to initialize a pointer to char:
    char *pStr = "Hello, world!";     // pStr points to the first character, 'H'
In such an initializer, the string literal represents the address of its first element, just as an array name would.
In Example 3-1, the array error_msg contains three pointers to char, each of which is assigned the address of the first character of a string literal.
Example 3-1. Sample function error_exit()
#include <stdlib.h>
#include <stdio.h>
void error_exit(unsigned int error_n)  // Print a last error message
{                                      // and exit the program.char * error_msg[ ] = { "Unknown error code.\n",
                         "Insufficient memory.\n",
                         "Illegal memory access.\n" };
  unsigned int arr_len = sizeof(error_msg)/sizeof(char *);

  if ( error_n >= arr_len )
     error_n = 0;
  fputs( error_msg[error_n], stderr );
  exit(1);
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: Type Conversions
In C, operands of different types can be combined in one operation. For example, the following expressions are permissible:
    double dVar = 2.5;   // Define dVar as a variable of type double.
    dVar *= 3;           // Multiply dVar by an integer constant.
    if ( dVar < 10L )    // Compare dVar with a long-integer constant.
      { /* ... */ }
When the operands have different types, the compiler tries to convert them to a uniform type before performing the operation. In certain cases, furthermore, you must insert type conversion instructions in your program. A type conversion yields the value of an expression in a new type, which can be either the type void (meaning that the value of the expression is discarded: see "Expressions of Type void" in Chapter 2), or a scalar type—that is, an arithmetic type or a pointer. For example, a pointer to a structure can be converted into a different pointer type. However, an actual structure value cannot be converted into a different structure type.
The compiler provides implicit type conversions when operands have mismatched types, or when you call a function using an argument whose type does not match the function's corresponding parameter. Programs also perform implicit type conversion as necessary when initializing variables or otherwise assigning values to them. If the necessary conversion is not possible, the compiler issues an error message.
You can also convert values from one type to another explicitly using the cast operator (see Chapter 5):
    (type_name) expression
In the following example, the cast operator causes the division of one integer variable by another to be performed as a floating-point operation:
    int sum = 22, count = 5;
    double mean = (double)sum / count;
Because the cast operator has precedence over division, the value of sum in this example is first converted to type double. The compiler must then implicitly convert the divisor, the value of count, to the same type before performing the division.
You should always use the cast operator whenever there is a possibility of losing information, as in a conversion from
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Conversion of Arithmetic Types
Type conversions are always possible between any two arithmetic types , and the compiler performs them implicitly wherever necessary. The conversion preserves the value of an expression if the new type is capable of representing it. This is not always the case. For example, when you convert a negative value to an unsigned type, or convert a floating-point fraction from type double to the type int, the new type simply cannot represent the original value. In such cases the compiler generally issues a warning.
When arithmetic operands have different types, the implicit type conversion is governed by the types' conversion rank . The types are ranked according to the following rules:
  • Any two unsigned integer types have different conversion ranks. If one is wider than the other, then it has a higher rank.
  • Each signed integer type has the same rank as the corresponding unsigned type. The type char has the same rank as signed char and unsigned char.
  • The standard integer types are ranked in the order:
        _Bool < char < short < int < long < long long
  • Any standard integer type has a higher rank than an extended integer type of the same width. (Extended integer types are described in the section "Integer Types with Exact Width (C99)" in Chapter 2.)
  • Every enumerated type has the same rank as its corresponding integer type (see "Enumerated Types" in Chapter 2).
  • The floating-point types are ranked in the following order:
        float < double < long double
  • The lowest-ranked floating-point type, float, has a higher rank than any integer type.
  • Every complex floating-point type has the same rank as the type of its real and imaginary parts.
In any expression, you can always use a value whose type ranks lower than int in place of an operand of type int or unsigned int. You can also use a bit-field as an integer operand (bit-fields are discussed in Chapter 10). In these cases, the compiler applies integer promotion : any operand whose type ranks lower than int is automatically converted to the type int, provided int is capable of representing all values of the operand's original type. If
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Conversion of Nonarithmetic Types
Pointers and the names of arrays and functions are also subject to certain implicit and explicit type conversions. Structures and unions cannot be converted, although pointers to them can be converted to and from other pointer types.
An array or function designator is any expression that has an array or function type. In most cases, the compiler implicitly converts an expression with an array type, such as the name of an array, into a pointer to the array's first element. The array expression is not converted into a pointer only in the following cases:
  • When the array is the operand of the sizeof operator
  • When the array is the operand of the address operator &
  • When a string literal is used to initialize an array of char or wchar_t
The following examples demonstrate the implicit conversion of array designators into pointers, using the conversion specification %p to print pointer values:
    #include <stdio.h>

    int *iPtr = 0;                      // A pointer to int, initialized with 0.
    int iArray[ ] = { 0, 10, 20 };       // An array of int, initialized.

    int array_length = sizeof(iArray) / sizeof(int); // The number of elements:
                                                     // in this case, 3.

    printf("The array starts at the address %p.\n", iArray);

    *iArray = 5;                      // Equivalent to iArray[0] = 5;

    iPtr = iArray + array_length - 1; // Point to the last element of iArray:
                                      // Equivalent to
                                      // iPtr = &iArray[array_length-1];

    printf("The last element of the array is %d.\n", *iPtr);
In the initialization of array_length in this example, the expression sizeof(iArray) yields the size of the whole array, not the size of a pointer. However, the same identifier iArray is implicitly converted to a pointer in the other three statements in which it appears:
  • As an argument in the first printf() call.
  • As the operand of the dereferencing operator *.
  • In the pointer arithmetic operations and assignment to iPtr (see also "Modifying and Comparing Pointers" in Chapter 9).