0 ratings0% found this document useful (0 votes) 240 views36 pagesBasic Types
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content,
claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
con
Basic Types
‘Make no mistake about it: Computers process numbers—
‘not symbols. We measure our understanding (and control)
‘by the extent fo which we can arithmetize an activity,
So far, we've used only two of C’s basic (built-in) types: int and float. (We've
also seen _Bool, which is a basic type in C99.) This chapter deseribes the rest of
the basic types and discusses important issues about types in general. Section 7.1
reveals the full range of integer types, which include long integers, short integers,
and unsigned integers. Section 7.2 introduces the double and long double
types, which provide a larger range of values and greater precision than £1oat.
Section 7.3 covers the char type, which we'll need in order to work with charac-
ter data. Section 7.4 tackles the thorny topic of converting a value of one type to an
equivalent value of another. Section 7.5 shows how to use typedef to define new
type names. Finally, Section 7.6 describes the sizeof operator, which measures
the amount of storage required for a type.
Integer Types
C supports two fundamentally different kinds of numeric types: integer types and
floating types. Values of an integer type are whole numbers, while values of a
floating type can have a fractional part as well. The integer types, in turn, are
divided into two categories: signed and unsigned.
Signed and Unsigned Integers
The leftmost bit of a signed integer (known as the sign bif) is 0 if the number is
positive or zero, 1 if it's negative. Thus, the largest 16-bit integer has the binary rep-
resentation
125126
Chapter 7 Basic Types
0099999999999111
which has the value 32,767 (2"° - 1). The largest 32-bit integer is
OVTT44499099919900909900099191111
which has the value 2,147,483,647 (2°" — 1). An integer with no sign bit (the left-
‘most bit is considered part of the number's magnitude) is said to be unsigned. The
largest 16-bit unsigned integer is 65,535 (2° - 1), and the largest 32-bit unsigned
integer is 4,294,967,295 (2° - 1).
By default, integer variables are signed in C—the leftmost bit is reserved for the
sign. To tell the compiler that a variable has no sign bit, we declare it to be
unsigned. Unsigned numbers are primarily useful for systems programming and
low-level, machine-dependent applications. We'll discuss typical applications for
unsigned numbers in Chapter 20; until then, we'll generally avoid them.
C’s integer types come in different sizes. The int type is usually 32 bits, but
may be 16 bits on older CPUs. Since some programs require numbers that are too
large to store in int form, C also provides long integers. At times, we may need to
conserve memory by instructing the compiler to store a number in less space than
normal; such a number is called a short integer.
To construct an integer type that exactly meets our needs, we can specify that
a variable is long or short, signed or unsigned. We can even combine
specifiers (e.g., long unsigned int). However, only the following six combi-
nations actually produce different types:
short int
unsigned short int
int
unsigned int
long int
unsigned long int
Other combinations are synonyms for one of these six types. (For example, Long,
signed int is the same as long int, since integers are always signed unless
otherwise specified.) Incidentally, the order of the specifiers doesn’t matter;
unsigned short int is the same as short unsigned int
C allows us to abbreviate the names of integer types by dropping the word
int. For example, unsigned short int may be abbreviated to unsigned
short, and long int may be abbreviated to just Long. Omitting int is a
widespread practice among C programmers, and some newer C-based languages
(including Java) actually require the programmer to write short or Long rather
than short int or long int. For these reasons, I'll often omit the word int
when it’s not strictly necessary.7.1 Integer Types 127
The range of values represented by each of the six integer types varies from
one machine to another. However, there are a couple of rules that all compilers
must obey. First, the C standard requires that short int, int, and long int
each cover a certain minimum range of values (see Section 23.2 for details). Sec-
ond, the standard requires that int not be shorter than short int, and long
int not be shorter than int. However, it's possible that short int represents
the same range of values as int; also, int may have the same range as long
int.
Table 7.1 shows the usual range of values for the integer types on a 16-bit
machine; note that short int and int have identical ranges.
Table 7.1 Type Smallest Value Largest Value
Integer Types on a short int 32,768 32,767
16-bit Machine unsigned short int 0 65,535
int 32,768 32,767
unsigned int 0 65,535
long int -2,147,483,648 2,147,483,647
unsigned long int
Table 7.2 shows the usual ranges on a 32-bit machine; here int and long int
have identical ranges.
‘Table 7.2 Type Smallest Value Largest Value
Integer Types on a short int 32,768 32,767
32-bit Machine unsigned short int 0 65,535
int -2,147,483,648 2,147,483,647
unsigned int 0 4,294,967,295
long int —2,147,483,648 — 2,147,483,647
unsigned long int 0 4,294,967,295,
In recent years, 64-bit CPUs have become more common. Table 7.3 shows typical
ranges for the integer types on a 64-bit machine (especially under UNIX).
Table 7.3 Type Smallest Value Largest Value
Integer Types ona | short int 32,768 32,767
64-bit Machine | unsigned short int 0 65,535
int -2,147,483,648 2,147,483,647
unsigned int 0 4,294,967,295
long int ~9,223,372,036,854,775,808 _9,223,372,036,854,775,807
unsigned long int 0 18,446,744,073,709,551,615
Once more, let me emphasize that the ranges shown in Tables 7.1, 7.2, and 7.3
aren't mandated by the C standard and may vary from one compiler to another,
‘One way to determine the ranges of the integer types for a particular implementa-
to check the <1imits.h> header, which is part of the standard library.
This header defines macros that represent the smallest and largest values of each
integer type.
header>2a2 tion i128 Chapter7 Basic Types
@
signed chartype>73
unsigned char ypo>79
Bool ype 5.2
Integer Types in C99
€99 provides two additional standard integer types, long long int and
unsigned long long int. These types were added because of the growing
need for very large integers and the ability of newer processors to support 64-bit
arithmetic. Both Long Long types are required to be at least 64 bits wide, so the
range of long long int values is typically 2 (-9,223,372,036,854,775,808)
to 2° — 1 (9,223,372,036,854,775,807), and range of unsigned long long
int values is usually 0 to 2 — 1 (18,446,744,073,709,55 11,615).
The short int, int, long int, and long long int types (along with
the signed char type) are called standard signed integer types in C99. The
unsigned short int, unsigned int, unsigned long int, and
unsigned long long int types (along with the unsigned char type and
the _Bool type) are called standard unsigned integer types.
In addition to the standard integer types, the C99 standard allows implementa-
tion-defined extended integer types, both signed and unsigned. For example, a
compiler might provide signed and unsigned 128-bit integer types.
Integer Constants
Let's turn our attention to constants—numbers that appear in the text of a pro-
gram, not numbers that are read, written, or computed. C allows integer constants
to be written in decimal (base 10), octal (base 8), or hexadecimal (base 16).
Octal and Hexadecimal Numbers
An octal number is written using only the digits 0 through 7. Each position in an
octal number represents a power of 8 (just as each position in a decimal number
represents a power of 10). Thus, the octal number 237 represents the decimal num-
ber 2x 8°+3x8'+7 x8 = 128 +24+7 = 159.
A hexadecimal (or hex) number is written using the digits 0 through 9 plus the
letters A through F, which stand for 10 through 15, respectively. Each position in a
hex number represents a power of 16; the hex number 1AF has the decimal value
1x 16? + 10x 16! + 15 x 16° = 256 + 160 + 15 = 431,
= Decimal constants contain digits between 0 and 9, but must not begin with a
7010:
15 255 32767
= Octal constants contain only digits between 0 and 7, and must begin with a
ze10:
017 0377 «0777777.1 Integer Types 129
= Hexadecimal constants contain digits between 0 and 9 and letters between a
and £, and always begin
Oxf Oxff Ox7fff
‘The letters in a hexadecimal constant may be either upper or lower case:
Oxff OxfF OxPE OxFF OX£E OXEF OXFE OxFF
Keep in mind that octal and hexadecimal are nothing more than an alternative
way of writing numbers; they have no effect on how the numbers are actually
stored. (Integers are always stored in binary, regardless of what notation we've
used to express them.) We can switch from one notation to another at any time, and
even mix them: 10 + 015 + 0x20 has the value 55 (decimal). Octal and hex are
most convenient for writing low-level programs; we won't use these notations
much until Chapter 20.
The type of a decimal integer constant is normally int. However, if the value
of the constant is too large to store as an int, the constant has type long int
instead, In the unlikely case that the constant is too large to store as a long int,
the compiler will try unsigned long int as a last resort. The rules for deter-
mining the type of an octal or hexadecimal constant are slightly different: the com-
piler will go through the types int, unsigned int, long int, andunsigned
long int until it finds one capable of representing the constant,
To force the compiler to treat a constant as a long integer, just follow it with
the letter L (or 1
1SL 0377 Ox7£££L
To indicate that a constant is unsigned, put the letter U (or u) after it;
15U 03770 Ox7£ffU
1 and U may be used in combination to show that a constant is both long and
unsigned: Oxff£f££f£UL. (The order of the L and U doesn’t matter, nor does
their case.)
Integer Constants in C99
In C99, integer constants that end with either LL or 11 (the case of the two letters
must match) have type Long long int. Adding the letter U (or u) before or after
the LL or 11 denotes a constant of type unsigned long long int.
C99"s general rules for determining the type of an integer constant are a bit
different from those in C89, The type of a decimal constant with no suffix (U, u, L,
1, LL, of 11) is the “smallest” of the types int, long int, or long long int
that can represent the value of that constant, For an octal or hexadecimal constant,
however, the list of possible types is int, unsigned int, long int,
unsigned long int, long long int, and unsigned long long int, in
that order. Any suffix at the end of a constant changes the list of possible types. For130
Chapter 7 Basic Types
example, a constant that ends with U (or u) must have one of the types unsigned
int, unsigned long int, or unsigned long long int. A decimal con-
stant that ends with I (or 1) must have one of the types Long int or long long
int. There’s also a provision for a constant to have an extended integer type if it's,
too large to represent using one of the standard integer types.
Integer Overflow
When arithmetic operations are performed on integers, it’s possible that the result
will be too large to represent, For example, when an arithmetic operation is per-
formed on two int values, the result must be able to be represented as an int. If
the result can’t be represented as an int (because it requires too many bits), we
say that overflow has occurred,
The behavior when integer overflow occurs depends on whether the operands
were signed or unsigned. When overflow occurs during an operation on signed
integers, the program’s behavior is undefined. Recall from Section 4.4 that the con-
sequences of undefined behavior may vary. Most likely the result of the operation
will simply be wrong, but the program could crash or exhibit other undesirable
behavior.
When overflow occurs during an operation on unsigned integers, though, the
result is defined: we get the correct answer modulo 2", where n is the number of
bits used to store the result. For example, if we add 1 to the unsigned 16-bit num-
ber 65,535, the result is guaranteed to be 0.
Reading and Writing Integers
Suppose that a program isn’t working because one of its int variables is over-
flowing. Our first thought is to change the type of the variable from int to long
int. But we're not done yet; we need to see how the change will affect the rest of
the program. In particular, we must check whether the variable is used in a call of
printf or scanf. If so, the format string in the call will need to be changed,
since the 4 conversion works only for the int type.
Reading and writing unsigned, short, and long integers requires several new
conversion specifiers:
= When reading or writing an unsigned integer, use the letter u, 0, or x instead
of d in the conversion specification. If the u specifier is present, the number is
read (or written) in decimal notation; o indicates octal notation, and > indi-
cates hexadecimal notation,
unsigned int u;
scanf("tu", gu); /* reads u in base 10 */
printf("$u", u); | /* writes u in base 10 */
scanf("$o", &u); /* reads u in base 8 */
printf("fo", u); /* writes u in base 8 */PROGRAM
sum2.c
7.1 Integer Types 131
scanf("$x", &u) ; /* reads u in base 16 */
printé("$x", u); /# writes u in base 16 */
= When reading or writing a short integer, put the letter h in front of 4, 0, u, or
Err
short s;
scanf("thd", gs);
print£("thd", 5);
= When reading or writing a /ong integer, put the letter 1 (“ell,” not “one”) in
front of d, 0, u, or x:
long 1;
scanf("$1d", &1);
printé("t1d", 1);
= When reading ot writing a long long integer (C99 only), put the letters 1 in
front of d, 0, u, or x:
long long 11;
seanf("$1ld", &11);
print£("$1ld", 11);
Summing a Series of Numbers (Revisited)
In Section 6.1, we wrote a program that sums a series of integers entered by the
user, One problem with this program is that the sum (or one of the input numbers)
might exceed the largest value allowed for an int. variable. Here’s what might
happen if the program is run on a machine whose integers are 16 bits long:
This program sums a series of integers.
Enter integers (0 to terminate): 10000 20000 30000 o
The sum is: -5536
The sum was 60,000, which wouldn’t fit in an int variable, so overflow occurred.
When overflow occurs with signed numbers, the outcome is undefined. In this
‘case, we got an apparently meaningless number. To improve the program, let's
switch to Long variables.
/* Sums a series of numbers (using long variables) */
#include
int main(void)
long n, sum = 0
printf ("This program sums a series of integers.\n");132
Chapter 7
Basic Types
printé ("Enter integers (0 to terminate): ");
scanf("t1d", &n);
while (n != 0) {
scant ("¥1d", &n);
printf£("The sum is: $1d\n", sum);
return 0;
}
The change was fairly simple: we declared n and sum to be Long variables
instead of int variables, then we changed the conversion specifications in scan£
and printf to $1d instead of $4.
7.2 Floating Types
The integer types aren’t suitable for all applications, Sometimes we'll need vari-
ables that can store numbers with digits after the decimal point, or numbers that are
exceedingly large or small. Numbers like these are stored in floating-point format,
(so called because the decimal point “floats”). C provides three floating types, cor-
responding to different floating-point formats:
float Single-precision floating-point
double Double-precision floating-point
long double — Extended-precision floating-point
£loat is suitable when the amount of precision isn’t critical (calculating tempera-
tures to one decimal point, for example). double provides greater precision—
enough for most programs. Long double, which supplies the ultimate in preci-
sion, is rarely used.
The C standard doesn’t state how much precision the Float, double, and
long double types provide, since different computers may store floating-point
numbers in different ways. Most modern computers follow the specifications in
IBEE Standard 754 (also known as IEC 60559), so we'll use it as an example.
The IEEE Floating-Point Standard
IEEE Standard 754, developed by the Institute of Electrical and Electronics Engi-
neers, provides two primary formats for floating-point numbers: single precision (32
bits) and double precision (64 bits). Numbers are stored in a form of scientific nota
tion, with each number having three parts: a sign, an exponent, and a fraction.
‘The number of bits reserved for the exponent determines how large (or small) num-
bers can be, while the number of bits in the fraction determines the precision. In
single-precision format, the exponent is 8 bits long, while the fraction occupies 23subormal numbers 28-4
‘Table 7.4
Floating Type
float .h> header »23 7
@
complex ypes 27.3
7.2 Floating Types 133
bits. As a result, a single-precision number has a maximum value of approximately
3.40 x 10%, with a precision of about 6 decimal digits.
The IEEE standard also describes two other formats, single extended precision
and double extended precision. The standard doesn't specify the number of bits in
these formats, although it requires that the single extended type occupy at least 43
bits and the double extended type at least 79 bits. For more information about the
IEEE standard and floating-point arithmetic in general, see “What every computer
scientist should know about floating-point arithmetic” by David Goldberg (ACM
Computing Surveys, vol. 23, no. 1 (March 1991): 5-48).
Table 7.4 shows the characteristics of the floating types when implemented
according to the IEEE standard. (The table shows the smallest positive normalized
values. Subnormal numbers can be smaller.) The Long double type isn’t shown
in the table, since its length varies from one machine to another, with 80 bits and
ig the most common sizes,
Type __ Smallest Positive Value Largest Value Precision
float 1.17549 x 10-8 3.40282 x 10° 6 digits |
double 2.22507 x 10% 1.79769 x 10°* 15 digi
On computers that don’t follow the IEEE standard, Table 7.4 won't be valid.
In fact, on some machines, £Loat may have the same set of values as double, or
double may have the same values as long double. Macros that define the
characteristics of the floating types can be found in the header.
In C99, the floating types are divided into two categories. The £loat, dou-
ble, and long double types fall into one category, called the real floating
types. Floating types also include the complex types (Eloat _Complex, dou-
ble Complex, and long double _Comp1ex), which are new in C99,
Floating Constants
Floating constants can be written in a variety of ways. The following constants, for
example, are all valid ways of writing the number 57.0:
57.0 57. 57.0e0 57EO 5.7e1 5.7e+1 .57e2 570.e-1
A floating constant must contain a decimal point and/or an exponent; the exponent
indicates the power of 10 by which the number is to be scaled. If an exponent is
present, it must be preceded by the letter E (or e). An optional + or ~ sign may
appear after the E (or e).
By default, floating constants are stored as double-precision numbers. In other
words, when a C compiler finds the constant 57.0 in a program, it arranges for the
number to be stored in memory in the same format as a double variable. This
rule generally causes no problems, since double values are converted automati-
cally to £Loat when necessary.134 Chapter7 Basic Types
B 6
Q
ASCII character set > Appenaty E
On occasion, it may be necessary to force the compiler to store a floating con-
stant in float or long double format, To indicate that only single precision is
desired, put the letter F (or £) at the end of the constant (for example, 57. OF). To
indicate that a constant should be stored in long double format, put the letter L
(or 1) at the end (57. OL).
C99 has a provision for writing floating constants in hexadecimal, Such a con-
stant begins with 02 or OX (like a hexadecimal integer constant), This feature is
rarely used,
Reading and Wri
As we've discussed, the conversion specifications $e, $£, and %g are used for
reading and writing single-precision floating-point numbers. Values of types dou~
ble and long double require slightly different conversions:
ig Floating-Point Numbers
= When reading a value of type double, put the letter 1 in front of e, £, or g:
double d;
seanf ("$l£", &d);
‘Note: Use 1 only in a scanf format string, not a printé string. In a
printé format string, the e, £, and g conversions can be used to write either
float or double values. (C99 legalizes the use of $1e, $1f, and $1g in
calls of print f, although the 1 has no effect.)
= When reading or writing a yalue of type long double, put the letter L in
front of e, £, or
long double 1d;
scan£("$L£", &ld);
printf ("*L£", 1d);
Character Types
‘The only remaining basic type is char, the character type. The values of type
char can vary from one computer to another, because different machines may
have different underlying character sets.
Character Sets
Today's most popular character set is ASCII (American Standard Code for Informa-
tion Interchange), a 7-bit code capable of representing 128 characters. In ASCII,
the digits 0 to 9 are represented by the codes 0110000-0111001, and the upper-
case letters A to Z are represented by 1000001-1011010. ASCII is often extended7.8 Character Types 185
to a 256-character code known as Latin-1 that provides the characters necessary
for Western European and many African languages.
A variable of type char can be assigned any single character:
char ch;
ch = 'a'; /* lower-case a */
ch = ‘At; /* upper-case A */
ch = '0'; /* zero ae
‘oh. e 8 /* space ay:
Notice that character constants are enclosed in single quotes, not double quotes.
Operations on Characters
Working with characters in C is simple, because of one fact: C treats characters as
small integers. After all, characters are encoded in binary, and it doesn’t take much
imagination to view these binary codes as integers, In ASCII, for example, charac
ter codes range from 0000000 to 1111111, which we can think of as the integers
from 0 to 127. The character 'a' has the value 97, "A" has the value 65, "0" has
the value 48, and ' * has the value 32. The connection between characters and
integers in C is so strong that character constants actually have int type rather
than char type (an interesting fact, but not one that will often matter to us).
When a character appears in a computation, C simply uses its integer value.
Consider the following examples, which assume the ASCII character set:
char ch;
int i;
d= tats /* iis now 97 +/
ch = 65; /* ch is now ‘A! +/
ch=ch+1; /* ch is now 'B! */
cht+; /* ch is now 'c! */
Characters can be compared, just as numbers can. The following if statement
checks whether ch contains a lower-case letter; if so, it converts ch to upper case.
ch && ch
Slate OR
ig (at
ch =
tz)
Comparisons such as 'a' <= ch are done using the integer values of the charac-
ters involved, These values depend on the character set in use, so programs that use
. >, and >= to compare characters may not be portable.
The fact that characters have the same properties as numbers has some advan-
tages. For example, we can easily write a £or statement whose control variable
steps through all the upper-case letters:
for (ch = 'A'; ch <= 'Z!; ch++)136
Chapter7 Basic Types
portability tip
‘enumerate types > 165
@
Bool ype >s2
On the other hand, treating characters as numbers can lead to various programming
errors that won't be caught by the compiler, and lets us write meaningless expres-
sions such as ta" * "b' / ‘c!. It can also hamper portability, since our pro-
grams may be based on assumptions about the underlying character set. (Our £0"
loop, for example, assumes that the letters from A to Z have consecutive codes.)
Signed and Unsigned Characters
Since C allows characters to be used as integers, it shouldn’t be surprising that the
char type—like the integer types—exists in both signed and unsigned versions.
Signed characters normally have values between ~128 and 127, while unsigned
characters have values between 0 and 255.
The C standard doesn’t specify whether ordinary char is a signed or an
unsigned type; some compilers treat it as a signed type, while others treat it as an
unsigned type. (Some even allow the programmer to select, via a compiler option,
whether char should be signed or unsigned.)
Most of the time, we don’t really care whether char is signed or unsigned.
Once in a while, though, we do, especially if we're using a character variable to
store a small integer. For this reason, C allows the use of the words signed and
unsigned to modify char:
signed char sch;
unsigned char uch;
Don't assume that char is either signed or unsigned by default. If it mat-
ters, use signed char or unsigned char instead of char.
In light of the close relationship between characters and integers, C89 uses the
term integral types to refer to both the integer types and the character types. Enu-
merated types are also integral types.
C99 doesn’t use the term “integral types.” Instead, it expands the meaning of
integer types” to include the character types and the enumerated types. C99"s
_Bool type is considered to be an unsigned integer type
Arithmetic Types
‘The integer types and floating types are collectively known as arithmetic types.
Here’s a summary of the arithmetic types in C89, divided into categories and sub-
categories:
= Integral types
e@ char
© Signed integer types (signed char, short int, int, long int)
‘ Unsigned integer types (unsigned char, unsigned short int,
unsigned int, unsigned long int)7.3 Character Types 137
© Enumerated types
= Floating types (float, double, long double)
GD 699 has a more complicated hierarchy for its arithmetic typ.
= Integer types
e char
© Signed integer types, both standard (signed char, short int, int,
long int, long long int) and extended
‘© Unsigned integer types, both standard (unsigned char, unsigned
short int, unsigned int, unsigned long int, unsigned
long long int, Bool) and extended
© Enumerated types
= Floating types
Real floating types (float, double, long double)
¢ Complex types (Eloat _Complex, double Complex, long dou-
ble _Complex)
Escape Sequences
A character constant is usually one character enclosed in single quotes, as we've
seen in previous examples. However, certain special characters—including the
new-line charactet—can't be written in this way, because they’re invisible (non-
printing) or because they can’t be entered from the keyboard. So that programs can
deal with every character in the underlying character set, C provides a special nota-
tion, the escape sequence,
‘There are two kinds of escape sequences: character escapes and numeric
escapes. We saw a partial list of character escapes in Section 3.1; Table 7.5 gives
the complete set.
‘Table 7.5 ‘Name Escape Sequence
Character Escapes Alert (bell) \a
| Backspace \b
Form feed \t
New line \n
| Carriage return \r
Horizontal tab \c
Vertical tab Ww
Backslash \\
‘Question mark \? |
Single quote NG
Double quote ME
‘The \a, \b, \£, \x, \t, and \v escapes represent common ASCII control
GRY characters. The \n escape represents the ASCII line-feed character. The \\ escape
allows a character constant or string to contain the \ character. The \' escape138 Chapter7 Basic Types
tigraph soquoncos m2.
@
Universal characternames >25.4
allows a character constant to contain the * character, while the \" escape allows a
string (o contain the " character. The \? escape is rarely used,
Character escapes are handy, but they have a problem: the list of character
escapes doesn’t include all the nonprinting ASCII characters, just the most com-
‘mon. Character escapes are also useless for representing characters beyond the
basic 128 ASCII characters, Numeric escapes, which can represent any character,
are the solution to this problem,
To write a numeric escape for a particular character, first look up the charac-
ter’s octal or hexadecimal value in a table like the one in Appendix E. For example,
the ASCII escape character (decimal value: 27) has the value 33 in octal and 1B in
hex. Either of these codes can be used to write an escape sequenc:
= An octal escape sequence consists of the \ character followed by an octal
number with at most three digits. (This number must be representable as an
unsigned character, so its maximum value is normally 377 octal.) For exam-
ple, the escape character could be written \33 or \033. Octal numbers in
escape sequences—unlike octal constants—don’t have to begin with 0.
= A hexadecimal escape sequence consists of \x followed by a hexadecimal
number, Although C places no limit on the number of digits in the hexadeci-
mal number, it must be representable as an unsigned character (hence it can’t
exceed FF if characters are eight bits long). Using this notation, the escape
character would be written \x1b or \x1B, The x must be in lower case, but
the hex digits (such as b) can be upper or lower case.
When used as a character constant, an escape sequence must be enclosed in
single quotes, For example, a constant representing the escape character would be
written "\33' (or '\x1b"). Escape sequences tend to get a bit cryptic, so it’s
often a good idea to give them names using #def ine:
#define ESC '\33' /* ASCII escape character +/
Escape sequences can be embedded in strings as well, as we saw in Section 3.1.
Escape sequences aren’t the only special notations for representing charac-
ters. Trigraph sequences provide a way to represent the characters #, [, \,], *. {,
|, }, and ~, which may not be available on keyboards in some countries. C99
adds universal character names, which resemble escape sequences. Unlike escape
‘sequences, however, universal character names are allowed in identifiers.
Character-Handling Functions
Earlier in this section, we saw how to write an if statement that converts a lower-
case letter to upper-case:
if (ta! <= ch && ch <
ch = ch - 'a' + ‘aA!
os
This isn’t the best method, though. A faster—and more portable—way to convert
case is to call C’s toupper library function:7.3 Character Types 139
ch
toupper(ch); /* converts ch to upper case */
When it’s called, toupper checks whether its argument (ch in this case) is a
lower-case letter. If so, it returns the corresponding upper-case letter. Otherwise,
toupper returns the value of the argument. In our example, we've used the
assignment operator to store the return value of toupper back into the ch vari-
able, although we could just as easily have done something else with it—stored it
in another variable, say, or tested it in an if statement:
if (toupper(ch) == 'A') .
Programs that call toupper need to have the following #include directive
at the top:
#include
toupper isn’t the only useful character-handling function in the C library. See-
tion 23.5 describes them all and gives examples of their use.
Reading and Writing Characters using scanf and printf
The %c conversion specification allows scanf and print £ to read and write sin-
gle characte!
char ch;
scanf("%c", &ch); /* reads a single character */
printf ("%c", ch); /* writes a single character */
scanf doesn’t skip white-space characters before reading a character. If the
next unread character is a space, then the variable ch in the previous example will
contain a space after scan£ returns. To force scanf to skip white space before
reading a character, put a space in its format string just before $c:
scanf(" tc", &ch); /* skips white space, then reads ch */
Recall from Section 3.2 that a blank in a scanf format string means “skip zero or
more white-space characters.”
Since scanf doesn’t normally skip white space, it’s easy to detect the end of
an input line: check to see if the character just read is the new-line character. For
example, the following loop will read and ignore all remaining characters in the
current input line:
do {
scanf("%c", &ch);
} while (ch != '\n');
When scanf is called the next time, it will read the first character on the next
input line.140
Chapter 7 Basic Types
macros = 14.9
Reading and Writing Characters using getchar and putchar
C provides other ways to read and write single characters, In particular, we can use
the getchar and putchar functions instead of calling scanf and print
putchar writes a single character:
putchar (ch) ;
Each time get.char is called, it reads one character, which it returns. In order to
save this character, we must use assignment to store it in a variabl
ch = getchar(); /* reads a character and stores it in ch */
get char actually returns an int value rather than a char value (the reason will
be discussed in later chapters). As a result, it’s not unusual for a variable to have
type int rather than chax if it will be used to store a character read by get char.
Like scanf, get char doesn’t skip white-space characters as it reads,
Using get char and put char (rather than scanf and print £) saves time
when the program is executed. get char and put char are fast for two reasons.
First, they're much simpler than scang and printf, which are designed to read
and write many kinds of data in a variety of formats. Second, getchar and
putchar are usually implemented as macros for additional speed.
get char has another advantage over scan£: because it returns the charac-
ter that it reads, getchar lends itself to various C idioms, including loops that
search for a character or skip over all occurrences of a character. Consider the
scanf loop that we used to skip the rest of an input line:
do {
scant ("tc", &ch);
} while (ch t= '\n");
Rewriting this loop using get char gives us the following:
do {
ch = getchar();
} while (ch != ‘\n');
Moving the call of get char into the controlling expression allows us to condense
the loop:
while ((ch = getchar()) != '\n')
This loop reads a character, stores it into the variable ch, then tests if ch is not
equal to the new-line character, If the test succeeds, the loop body (which is empty)
is executed, then the loop test is performed once more, causing a new character to
be read. Actually, we don’t even need the ch variable; we can just compare the
return value of get char with the new-line character:idiom
idiom
PROGRAM
7.3 Character Types 141
while (getchar()
= '\n') /* skips rest of line */
The resulting loop is a well-known C idiom that's cryptic but worth learning.
getchar is useful in loops that skip characters as well as loops that search
for characters. Consider the following statement, which uses get char to skip an
indefinite number of blank characters:
while ((ch = getchar())
11) /* skips blanks */
When the loop terminates, ch will contain the first nonblank character that
getchar encountered.
Be careful if you mix get char and scané in the same program. scanf has a
tendency to leave behind characters that it has “peeked” at but not read, including
the new-line character. Consider what happens if we try to read a number first, then
acharacter:
printf ("Enter an integer: ");
scanf("$d", &i);
printf ("Enter a command: ");
command = getchar () ;
The call of scang will leave behind any characters that weren't consumed during
the reading of 4, including (but not limited to) the new-line character. getchar
will fetch the first leftover character, which wasn't what we had in mind.
Determining the Length of a Message
To illustrate how characters are read, let's write a program that calculates the
length of a message. After the user enters the message, the program displays the
length:
Enter a message: Brevity is the soul of wit.
Your message was 27 character(s) long.
‘The length includes spaces and punctuation, but not the new-line character at the
end of the message.
We'll need a loop whose body reads a character and increments a counter. The
loop will terminate as soon as a new-line character turns up. We could use either
scanf or getchar to read characters; most C programmers would choose
getchar. Using a straightforward whi le loop, we might end up with the follow-
ing program,142 Chapter7 Basic Types
length.c
length2.c
7.4
/* Determines the length of a message */
#include
int main (void)
{
char ch;
int len = 0;
print£("Enter a message: ");
ch = getchar();
while (ch t= '\n') {
lens+;
ch = getchar();
printf ("Your message was td character(s) long.\n", len);
return 0;
Recalling our discussion of idioms involving whi Le loops and get char, we
realize that the program can be shortened:
/* Determines the length of a message */
#include
int main (void)
{
int len = 0;
print£ ("Enter a message: ");
while (getchar() ‘\n')
lent+;
printf ("Your message was ¥d character(s) long.\n", len);
return 0;
Type Conversion
‘Computers tend to be more restrictive than C when it comes to arithmetic. For a
computer to perform an arithmetic operation, the operands must usually be of the
same size (the same number of bits) and be stored in the same way. A computer
may be able to add two 16-bit integers directly, but not a 16-bit integer and a 32-bit
integer or a 32-bit integer and a 32-bit floating-point number.
C, on the other hand, allows the basic types to be mixed in expressions. We
can combine integers, floating-point numbers, and even characters i
expression. The C compiler may then have to generate instructions that convert7.4 Type Conversion 143
some operands to different types so that the hardware will be able to evaluate the
expression. If we add a 16-bit short and a 32-bit int, for example, the compiler
will arrange forthe short value to be converted to 32 bits. If we add an int and
a float, the compiler will arrange for the int to be converted to £1oat format.
This conversion is a little more complicated, since int and float values are
stored in different ways.
Because the compiler handles these conversions automatically, without the
programmer's involvement, they're known as implicit conversions. C also allows
the programmer to perform explicit conversions, using the cast operator. Pl dis-
cuss implicit conversions first, postponing explicit conversions until later in the
section, Unfortunately, the rules for performing implicit conversions are somewhat
complex, primarily because C has so many different arithmetic type
Implicit conversions are performed in the following situations:
= When the operands in an arithmetic or logical expression don’t have the same
type. (C performs what are known as the uswal arithmetic conversions.)
= When the type of the expression on the right side of an assignment doesn’t
match the type of the variable on the left side.
= When the type of an argument in a function call doesn’t match the type of the
corresponding parameter.
= When the type of the expre:
function’s return type.
on in a return statement doesn’t match the
‘We'll discuss the first two cases now and save the others for Chapter 9.
‘The Usual Arithmetic Conversions
‘The usual arithmetic conversions are applied to the operands of most binary opera-
tors, including the arithmetic, relational, and equality operators. For example, let's
say that £ has type £loat and i has type int. The usual arithmetic conversions
will be applied to the operands in the expression £ + i, because their types aren't
the same. Clearly it’s safer to convert i to type float (matching £’s type) rather
than convert £ to type int (matching i’s type). An integer can always be con-
verted to float; the worst that can happen is a minor loss of precision. Convert-
ing a floating-point number to int, on the other hand, would cost us the fractional
part of the number. Worse still, we'd get a completely meaningless result if the
original number were larger than the largest possible integer or smaller than the
smallest integer.
The strategy behind the usual arithmetic conversions is to convert operands to
the “narrowest” type that will safely accommodate both values. (Roughly speak-
ing, one type is narrower than another if it requires fewer bytes to store.) The types
of the operands can often be made to match by converting the operand of the nar-
rower type to the type of the other operand (this act is known as promotion).
‘Among the most common promotions are the integral promotions, which convert
‘character or short integer to type int (or to unsigned int in some cases).144 Chapter7 Basic Types
‘We can divide the rules for performing the usual arithmetic conversions into
two cases:
= The type of either operand is a floating type. Use the following diagram to
promote the operand whose type is narrower:
long double
T
double
f
float
That is, if one operand has type Long double, then convert the other oper-
and to type long double, Otherwise, if one operand has type double, con-
vert the other operand to type double. Otherwise, if one operand has type
float, convert the other operand to type float. Note that these rules cover
mixtures of integer and floating types: if one operand has type long int, for
example, and the other has type double, the long int operand is converted
to double.
«= Neither operand type is a floating type. First perform integral promotion on
both operands (guaranteeing that neither operand will be a character or short
integer). Then use the following diagram to promote the operand whose type
is narrower:
unsigned long int
tT
long int
th
unsigned int
+
int
There's one special case, but it occurs only when long int and unsigned int
have the same length (32 bits, say). Under these circumstances, if one operand has
type Long int and the other has type unsigned int, both are converted to
unsigned long int.
A When a signed operand is combined with an unsigned operand, the signed operand
is converted to an unsigned value. The conversion involves adding or subtracting a
multiple of 1 + 1, where n is the largest representable value of the unsigned type.
This rule can cause obscure programming errors.
Suppose that the int variable i has the value -10 and the unsigned int
variable u has the value 10. If we compare i and u using the < operator, we might
expect to get the result 1 (true). Before the comparison, however, i is converted to
unsigned int. Since a negative number can’t be represented as an unsigned
integer, the converted value won't be ~10. Instead, the value 4,294,967,296 is
added (assuming that 4,294,967,295 is the largest unsigned int value), giving7.4 Type Conversion 145
a converted value of 4,294,967,286, The comparison i < u will therefore produce
0. Some compilers produce a warning message such as “comparison between
signed and unsigned” when @ program attempts to compare a signed number with
an unsigned number.
Because of traps like this one, it’s best to use unsigned integers as little as pos-
sible and, especially, never mix them with signed integers.
The following example shows the usual arithmetic conversions in action:
char ¢;
short int s;
int i;
unsigned int u;
long int 1;
unsigned long int ul;
float £
double
long double 14;
ieite /* © is converted to int /
isi+s; /* s is converted to int */
usu i; 7/* i is converted to unsigned int */
lel+u; 7* a is converted to long int */
ul = ul +1; /* 1 is converted to unsigned long int */
= +ul; /* ul is converted to float */
a +f; 7/* £ is converted to double +f
1 d+; /*d is converted to long double */
Conversion During Assignment
The usual arithmetic conversions don’t apply to assignment. Instead, C follows the
simple rule that the expression on the right side of the assignment is converted to
the type of the variable on the left side. If the variable’s type is at least as “wide” as
the expression’s, this will work without a snag. For example:
char ¢;
int i;
float £;
double d;
/* c is converted to int */
/* i is converted to float */
/* £ is converted to double */
Other cases are problematic. Assigning a floating-point number to an integer
variable drops the fractional part of the number:
int i;
i= 942.97; /* i is now 842 */
i= -842.97; /* i is now -842 */146
Chapter 7 Basic Types
@
Bool ype 52
Moreover, assigning a value to a variable of a narrower type will give a meaning-
less result (or worse) if the value is outside the range of the variable’s type:
10000; /*** WRONG ***/
1.0620; /*** WRONG ***/
1,0€100; /#** WRONG ***/
A “narrowing” assignment may elicit a warning from the compiler or from tools
such as Lint.
IU’s a good idea to append the £ suffix to a floating-point constant if it will be
assigned to a float variable, as we've been doing since Chapter 2:
£ = 3.141598;
Without the suffix, the constant 3.14159 would have type double, possibly
causing a warning message.
Implicit Conversions in C99
‘The rules for implicit conversions in C99 are somewhat different from the rules in
C89, primarily because C99 has additional types (Bool, long long types,
extended integer types, and complex types).
For the purpose of defining conversion rules, C99 gives each integer type an
“integer conversion rank.” Here are the ranks from highest to lowest:
1. long long int, unsigned long long int
2, long int, unsigned long int
3. int, unsigned int
4. short int, unsigned short int
5. char, signed char, unsigned char
6. _Bool
For simplicity, I'm ignoring extended integer types and enumerated types.
In place of 89's integral promotions, C99 has “integer promotions,” which
involve converting any type whose rank is less than int and unsigned int to
int (provided that all values of the type can be represented using int) or else to
unsigned int
As in C89, the C99 rules for performing the usual arithmetic conversions ean
= The type of either operand is a floating type. As long as neither operand has a
complex type, the rules are the same as before, (The conversion rules for com-
plex types will be discussed in Section 27.3.)
= Neither operand type is a floating type. First perform integer promotion on
both operands. If the types of the two operands are now the same, the process
ends. Otherwise, use the following rules, stopping at the first one that applies:
© If both operands have signed types or both have unsigned types, convert thecast expression
7.4 Type Conversion 147
operand whose type has lesser integer conversion rank to the type of the
operand with greater rank.
If the unsigned operand has rank greater or equal to the rank of the type of
the signed operand, convert the signed operand to the type of the unsigned
operand.
If the type of the signed operand can represent all of the values of the type
of the unsigned operand, convert the unsigned operand to the type of the
signed operand.
Otherwise, convert both operands to the unsigned type corresponding to the
type of the signed operand.
Incidentally, all arithmetic types can be converted to _Bool type. The result
of the conversion is 0 if the original value is 0; otherwise, the result is 1.
Casting
Although C’s implicit conversions are convenient, we sometimes need a greater
degree of control over type conversion. For this reason, C provides casts. A cast
expression has the form
( type-name ) expression
1ype-name specifies the type to which the expression should be converted,
‘The following example shows how to use a cast expression to compute the
fractional part of a float value:
float £, frac_part;
frac_part = £ - (int) f;
‘The cast expression (int) £ represents the result of converting the value of £ to
type int. C’s usual arithmetic conversions then require that (int) £ be con-
verted back to type Float before the subtraction can be performed. The differ-
ence between £ and (int) £ is the fractional part of £, which was dropped
during the c:
Cast expressions enable us to document type conversions that would take
place anyway:
i= (int) £; /* £ is converted to int */
‘They also enable us to overrule the compiler and force it to do conversions that we
want. Consider the following example:
float quotient;
int dividend, divisor;
quotient = dividend / divisor;148
Chapter 7 Basic Types
As it’s now written, the result of the division—an integer—will be converted to
float form before being stored in quotient. We probably want dividend
and divisor converted to float before the division, though, so that we get a
‘more exact answer. A cast expression will do the trick:
quotient = (float) dividend / divisor;
divisor doesn’t need a cast, since casting dividend to float forces the
compiler to convert divisor to float also.
Incidentally, C regards ( type-name ) as a unary operator. Unary operators
have higher precedence than binary operators, so the compiler interprets
(float) dividend / divisor
as
((float) dividend) / divisor
If you find this confusing, note that there are other ways to accomplish the same
effect:
quotient = dividend / (float) divisor;
or.
quotient = (float) dividend / (float) divisor;
Casts are sometimes necessary to avoid overflow. Consider the following
example:
long i;
int j = 1000;
i= 4+ 4) /* overflow may occur +*/
At first glance, this statement looks fine, The value of j * j is 1,000,000, and 4 is
a Long, so it can easily store values of this size, right? The problem is that when
two int values are multiplied, the result will have int type. But j * 3 is too
Jarge to represent as an int on some machines, causing an overflow. Fortunately,
using a cast avoids the problem:
i= (long) j * 4;
Since the cast operator takes precedence over *, the first j is converted to long
type, forcing the second j to be converted as well. Note that the statement
i= (long) (3 * 3); /##* WRONG ##+/
wouldn't work, since the overflow would already have occurred by the time of the
cast.7.5
7.5 Type Definitions 149
Type Definitions
In Section 5.2, we used the #def ine directive to create a macro that could be
used as a Boolean type:
iidefine BOOL int
There's a better way to set up a Boolean type, though, using a feature known as
type definition:
typedef int Bool;
Notice that the name of the type being defined comes last. Note also that I've capi-
talized the word Bool. Capitalizing the first letter of a type name isn’t required:
it’s just a convention that some C programmers employ.
Using typedef to define Bool causes the compiler to add Bool to the list
of type names that it recognizes. 8001 can now be used in the same way as the
built-in type names—in variable declarations, cast expressions, and elsewhere. For
example, we might use Bool to declare variables
Bool flag; /* same as int flag; */
The compiler treats Bool as a synonym for int; thus, £1ag is really nothing
more than an ordinary int variable.
Advantages of Type Definitions
Type definitions can make a program more understandable (assuming that the pro-
grammer has been careful to choose meaningful type names). For example, sup-
pose that the variables cash_in and cash_out will be used to store dollar
amounts. Declaring Dollars as
typedef float Dollars;
and then writing
Dollars cash_in, cash_out;
is more informative than just writing
float cash_in, cash_out;
Type definitions can also make a program easier to modify. If we later decide
that Dollars should really be defined as double, all we need do is change the
type definition:
typedef double Dollars;150
Chapter7 Basic Types
portability tip
The declarations of Dollars variables need not be changed. Without the type
definition, we would need to locate all £1oat variables that store dollar amounts
(not necessarily an easy task) and change their declarations.
‘Type Definitions and Portability
Type definitions are an important tool for writing portable programs. One of the
problems with moving a program from one computer to another is that types may
have different ranges on different machines. If i is an int variable, an assignment
like
i = 100000;
is fine on a machine with 32-bit integers, but will fail on a machine with 16-bit
integers.
For greater portability, consider using typedee to define new names for
integer types.
‘Suppose that we're writing a program that needs variables capable of storing
product quantities in the range 0-50,000, We could use Long variables for this
purpose (since they're guaranteed to be able to hold numbers up to at least
2,147,483,647), but we'd rather use int. variables, since arithmetic on int. values
may be faster than operations on long values; also, int variables may take up
less space.
Instead of using the int type to declare quantity variables, we can define our
‘own “quantity” type:
typedef int Quantity;
and use this type to declare variables:
Quantity q;
When we transport the program to a machine with shorter integers, we'll change
typedef long Quantity;
This technique doesn’t solve all our problems, unfortunately, since changing the
definition of Quantity may affect the way Quantity variables are used. At the
very least, calls of print and scanf that use Quant ity variables will need to
be changed, with $d conversion specifications replaced by 1d,
The C library itself uses typedef to create names for types that can vary
from one C implementation to another; these types often have names that end with
_t, such as ptrdiff_t,size_t,andwchar_t. The exact definitions of these
types will vary, but here are some typical examples:@
estdint hs header ®27.1
76
sizeof expression
7.6 The sizeof Operator 151
typedef long int ptrdiff_t;
typedef unsigned long int size_t;
typedef int wchar_t;
In C99, the header uses typedef to define names for integer
types with a particular number of bits. For example, int 32_t is a signed integer
type with exactly 32 bits. Using these types is an effective way to make programs
more portable.
The sizeof Operator
The sizeof operator allows a program to determine how much memory is
required to store values of a particular type. The value of the expression
sizeof ( type-name )
is an unsigned integer representing the number of bytes required to store a value
belonging to nype-name. sizeof (char) is always 1, but the sizes of the other
types may vary. On a 32-bit machine, sizeof (int) is normally 4. Note that
sizeof is a rather unusual operator, since the compiler itself can usually deter-
mine the value of a sizeof expression.
The sizeof operator can also be applied to constants, variables, and expres-
sions in general, If i and j are int variables, then sizeof (i) is 4 ona 32-bit
machine, as is sizeof (i + j). When applied to an expression—as opposed to a
type—sizeof doesn’t require parentheses; we could write sizeof i instead of
sizeof (i). However, parentheses may be needed anyway because of operator
precedence. The compiler would interpret sizeof i + j as (sizeof i) +4,
because sizeof—a unary operator—takes precedence over the binary + opera-
tor. To avoid problems, I always use parentheses in sizeof expressions.
Printing a sizeof value requires care, because the type of a sizeof expres-
sion is an implementation-defined type named size_t. In C89, it's best to con-
vert the value of the expression to a known type before printing it. size_t is
‘guaranteed to be an unsigned integer type, s0 it's safest to cast a sizeof expres-
sion to unsigned Long (the largest of C89s unsigned types) and then print it
using the €1u conversion:
printf ("Size of int: %lu\n", (unsigned long) sizeof (int));
In.C99, the size_t type can be larger than unsigned long. However, the
print function in C99 is capable of displaying size_t values directly, without
needing a cast. The trick is to use the letter z in the conversion specification, fol-
lowed by one of the usual integer codes (typically u):
print£ ("Size of int: %zu\n", sizeof(int)); /* C99 only */152
Chapter 7 Basic Types
2
Q&A
Section 7.1 says that %o and %2 are used to write unsigned integers in octal
and hex notation. How do I write ordinary (signed) integers in octal or hex?
[p. 130]
‘You can use $0 and #2 to print a signed integer as long as its value isn’t negative.
These conversions cause printf to treat a signed integer as though it were
unsigned; in other words, print £ will assume that the sign bit is part of the num-
ber’s magnitude. As long as the sign bit is 0, there’s no problem. If the sign bit is 1,
print will print an unexpectedly large number.
But what if the number is negative? How can I write it in octal or hex?
There's no direct way to print a negative number in octal or hex. Fortunately, the
need to do so is pretty rare, You can, of course, test whether the number is negative
and print a minus sign yourself:
if (i < 0)
printf ("-tx", -i);
else
printf ("$x", i);
Why are floating constants stored in double form rather than £1oat form?
[p. 133]
For historical reasons, C gives preference to the double type; float is treated
as a second-class citizen. Consider, for instance, the discussion of float in Ker-
nighan and Ritchie's The C Programming Language: “The main reason for using
float is to save storage in large arrays, or, less often, to save time on machines
where double-precision arithmetic is particularly expensive.” C originally man-
dated that all floating-point arithmetic be done in double precision. (C89 and C99
have no such requirement.)
What do hexadecimal floating constants look like, and what are they good
for? [p. 134]
A hexadecimal floating constant begins with 0x or 0X and must contain an expo-
nent, which is preceded by the letter P (or p). The exponent may have a sign, and
the constant may end with £, ®, 1, or L. The exponent is expressed in decimal, but
represents a power of 2, not a power of 10. For example, 021. Bp3 represents the
number 1.6875 x 2° = 13.5. The hex digit B corresponds to the bit pattern 1011.
The B occurs to the right of the period, so each 1 bit represents a negative power of
2, Summing these powers of 2 (271 + 2° + 24) yields 6875.
Hexadecimal floating constants are primarily useful for specifying constants
that require great precision (including mathematical constants such as ¢ and m).
Hex numbers have a precise binary representation, whereas a constant written in
decimal may be subject to a tiny rounding error when converted to binary. Hexa-ir
vatabielength argument ists
26.1
Q&A 153
decimal numbers are also useful for defining constants with extreme values, such
as the values of the macros in the <£1oat .h> header. These constants are easy to
write in hex but difficult to write in decimal.
Why do we use %1£ to read a double value but %f to print it? [p. 134]
This is a tough question to answer. First, notice that scanf and printf are
‘unusual functions in that they aren’t restricted to a fixed number of arguments. We
say that scan and print have variable-length argument lists. When functions
with variable-length argument lists are called, the compiler arranges for float
arguments to be converted automatically to type double. As a result, print£
can’t distinguish between float and double arguments. This explains why &£
works for both float and double arguments in calls of printf.
scanf, on the other hand, is passed a pointer to a variable. ¢£ tells scant to
store a Float value at the address passed to it, while 1£ tells scané to store a
double value at that address, The distinction between float and double is
crucial here. If given the wrong conversion specification, scané will likely store
the wrong number of bytes (not to mention the fact that the bit pattern for a float
isn’t the same as that for a double).
‘What's the proper way to pronounce char? [p. 134]
‘There’s no universally accepted pronunciation, Some people pronounce char in
the same way as the first syllable of “character.” Others say “char,” as in
char broiled;
When does it matter whether a character variable is signed or unsigned? [p.
136)
If we store only 7-bit characters in the variable, it doesn’t matter, since the sign bit
will be zero. If we plan to store 8-bit characters, however, we'll probably want the
variable to have unsigned char type. Consider the following example:
ch = '\xdb';
Ich has been declared to have type char, the compiler may choose to treat it
signed character (many compilers do). As long as ch is used only as a character,
there won't be any problem. But if ch is ever used in a context that requires the
compiler to convert its value to an integer, we're likely to have trouble: the result-
ing integer will be negative, since ch’s sign bit is 1.
Here's another situation: In some kinds of programs, it’s customary to use
char variables to store one-byte integers. If we're writing such a program, we'll
have to decide whether each variable should be signed char or unsigned
char, just as we must decide whether ordinary integer variables should have type
int or unsigned int.
I don’t understand how the new-line character can be the ASCII line-feed
character, When a user enters input and presses the Enter key, doesn’t the
program read this as a carriage-return character or a carriage return plus a
line feed? {p. 137]154
Chapter 7 Basic Types
A
*Q
A:
tanh sequences »25.3
Q
*Q
Nope. As part of C’s UNIX heritage, it always regards the end of a line as being
marked by a single line-feed character, (In UNIX text files, a single line-feed char-
‘acter—but no carriage return—appears at the end of each line.) The C library takes
care of translating the user’s keypress into a line-feed character. When a program
reads from a file, the I/O library translates the file’s end-of-line marker (whatever it
may be) into a single line-feed character. The same transformations occur—in
Feverse—when output is written to the screen or to a file, (See Section 22.1 for
details.)
Although these translations may seem confusing, they serve an important pur-
pose: insulating programs from details that may vary from one operating system to
another.
What's the purpose of the \? escape sequence? [p. 138]
The \? escape is related to trigraph sequences, which begin with ??. If you should
put 2? in a string, there’s a possibility that the compiler will mistake it for the
beginning of a trigraph. Replacing the second ? by \? fixes the problem.
If get char is faster, why would we ever want to use scanf to read individ-
ual characters? [p. 140]
Although it’s not as fast as get. char, the scanf function is more flexible. As we
saw previously, the "%c" format string causes scanf to read the next input char-
acter; " $c" causes it to read the next non-white-space character. Also, scan£ is
g00d at reading characters that are mixed in with other kinds of data, Let's say that
our input data consists of an integer, then a single nonnumeric character, then
another integer, By using the format string "&4%c%d", we can get scan£ to read
all three items.
Under what circumstances do the integral promotions convert a character or
short integer to unsigned int? [p. 143]
The integral promotions yield an unsigned int if the int type isn’t large
enough to include all possible values of the original type. Since characters are usu-
ally cight bits long, they are almost always converted to int, which is guaranteed
to be at least 16 bits long. Signed short integers can always be converted to int as
well. Unsigned short integers are problematic, If short integers have the same
length as ordinary integers (as they do on a 16-bit machine), then unsigned short
integers will have to be converted to unsigned int, since the largest unsigned
short integer (65,535 on a 16-bit machine) is larger than the largest int (32,767).
Exactly what happens if I assign a value to a variable that’s not large enough
to hold it? [p. 146]
Roughly speaking, if the value is of an integral type and the variable is of an
unsigned type, the extra bits are thrown away; if the variable has a signed type, the
result is implementation-defined. Assigning a floating-point number to a vari-
able—integer or floating—that's too small to hold it produces undefined behavior:
anything can happen, including program termination,Oo
A:
“Oo
A
atninteng rays 83
Section 7.1 1
Section 7.2 2.
Exercises 155
Why does C bother to provide type definitions? Isn’t defining a BOOL macro
just as good as defining a Bool type using typedef? [p. 149]
There are two important differences between type definitions and macro defini-
tions. First, type definitions are more powerful than macro definitions. In particu-
lar, array and pointer types can’t be defined as macros. Suppose that we try to use a
macro to define a “pointer to integer” type:
#define PTR_TO_INT int *
‘The declaration
PTR_TO_INT p, q, x;
will become
int * p, a
after preprocessing. Unfortunately, only p is a pointer; q and r are ordinary integer
variables. Type definitions don’t have this problem.
Second, typedef names are subject to the same scope rules as variables; a
typedef name defined inside a function body wouldn’t be recognized outside the
function. Macro names, on the other hand, are replaced by the preprocessor wher-
ever they appear.
‘You said that compilers “can usually determine the value of a sizeof expres-
sion.” Can’t a compiler always determine the value of a sizeof expression?
{p. 151)
In C89, yes, In C99, however, there’s one exception. The compiler can’t determine
the size of a variable-length array, because the number of elements in the array
may change during the execution of the program.
Exercises
Give the decimal value of each of the following integer constants.
(a) 077
(b) 0x77
(©) OxABC
Which of the following are not legal constants in C? Classify each legal constant as either
integer or floating-point.
(a) 01082
(b) 32.1845
(©) 0730
(a) 100_000
(©) 3.978e-2156 Chapter7 Basic Types
Section 7.3
Section 7.4
@
@ 4.
Which of the following are not legal types in C?
(a) short unsigned int
(b) short float
(© long double
(@) unsigned long
Icis
variable of type cha, which one of the follo
(@) i t=c; /* i has type int +/
(es2*e-1;
(©) putchar(c) ;
(@) printé(c);
Which one of the following is not a legal way to write the number 65? (Assume that the
character set is ASCIL)
(@) ‘at
(b) ob1000001
(©) 0102
(@) Ox42,
For each of the following items of data, specify which one of the types char, short, int,
or Long is the smallest one guaranteed to be large enough to store the item,
(@) Days in a month
(b) Days ina year
(©) Minutes in a day
(d) Seconds ina day
For each of the following character escapes, give the equivalent octal escape. (Assume that
the character set is ASCII.) You may wish to consult Appendix E, which lists the numerical
codes for ASCII characters.
(@ \b
(b) \n
© \r
@ \t
Repeat Exercise 7, but give the equivalent hexadecimal escape.
‘Suppose that i and 3 are variables of type int. What is the type of the expression 4 / 5 +
tat?
‘Suppose that 4 is a variable of type int. j is a variable of type Long, and k isa variable of
type unsigned int. What is the type of the expression i+ (int) j * k?
Suppose that i is a variable of type int, £ is a variable of type float, and dis a variable
of type double. What is the type of the expression i * £ / a?
Suppose that i is a variable of type int, £ is a variable of type float, and d is a variable
of type double. Explain what conversions take place during the execution of the following
statement:
a-i+é;Section 7.5
13,
15.
Programming Projects 157
Assume that a program contains the following declarations:
chars si
short s = 2;
int i = -3;
long m = 5;
float £ = 6.5£;
double d = 7.5;
Give the value and the type of each expression listed below.
@eri @f/e @f-a
sem @a/s () (int) £
Does the following statement always compute the fractional part of £ correctly (assuming
that £ and £rac_part are float variables)?
frac_part - £ - (int) £;
If not, what's the problem?
Use typedef to create types named Int 8, Int16, and Int32. Define the types so that
they represent 8-bi t, and 32-bit integers on your machine.
Programming Projects
‘The square2 .c program of Section 6.3 will fail (usually by printing strange answers) if
i * d exceeds the maximum int value. Run the program and determine the smallest value
‘of n that causes failure, Try changing the type of i to short and running the program
again. (Don’t forget to update the conversion specifications in the call of print £!) Then
try Long, From these experiments, what can you conclude about the number of bits used to
store integer types on your machine?
Modify the square2 . c program of Section 6.3 so that it pauses after every 24 squares and
displays the following message:
Press Enter to continue...
Afier displaying the message, the program should use getchar to read a character
getchar won't allow the program to continue until the user presses the Enter key.
Modify the sum2 . c program of Section 7.1 to sum a series of double values.
Write a program that translates an alphabetic phone number into numeric form:
Enter phone number: CALLATT
2255288
(In case you don’t have a telephone nearby. here are the letters on the keys: 2=ABC, 3=DEF,
4=GHI, 5=JKL, 6=MNO, 7=PRS, 8=TUV, 9=WXY,) If the original phone number contains
nonalphabetic characters (digits or punctuation, for example), leave them unchanged:
Enter phone number: 1-800-COL-LECT
1-800-265-5328
‘You may assume that any letters entered by the user are upper case.158
Chapter 7 Basic Types
@s
i.
In the SCRABBLE Crossword Game, players form words using small tiles, each containing,
a letter and a face value. The face value varies from one letter to another, based on the let-
ter’s rarity. (Here are the face values: 1: AEILNORSTU, 2: DG, 3: BCMP, 4: FHVWY, 5: K,
8: JX, 10: QZ.) Write a program that computes the value of a word by summing the values.
of its letters:
Enter a word: pitfall
Scrabble value: 12
‘Your program should allow any mixture of lower-case and upper-case letters in the word.
Hint: Use the toupper library function,
Write a program that prints the values of sizeof (int), sizeof (short)
sizeof (long), sizeof (float), sizeof (double) and sizeof (long dou-
ble)
Modify Programming Project 6 from Chapter 3 so that the user may add, subtract, multiply,
or divide two fractions (by entering either +, -. *, or / between the fractions).
Modify Programming Project from Chapter 5 so that the user enters a time using the 12
hour clock. The input will have the form hours: minutes followed by either A, P, AM, or PM
(either lower-case or upper-case). White space is allowed (but not required) between the
‘numerical time and the AM/PM indicator, Examples of valid input:
2:15P
P15PM
15p
15pm
115 P
15 PM
15 p
215 pm
‘You may assume that the input has one of these forms; there is no need to test for errors.
Write a program that asks the user for a 12-hour time, then displays the time in 24-hour
form:
Enter a 12-hour time: 9:12 PM
Equivalent 24-hour time: 21:11
‘See Programming Project 8 for a description of the input format,
Write a program that counts the number of vowels (a, ¢, i, o, and w) in a sentence:
Enter a sentence: And that's the way it is.
Your sentence contains 6 vowels.
Write a program that takes a first name and last name entered by the user and displays the
last name, a comma, and the first initial, followed by a period:
Enter a first and last name: Lloyd Fosdick
Fosdick, L.
‘The user’s input may contain extra spaces before the first name, between the first and last
names, and after the last name.
Write a program that evaluates an expression:
Enter an expression: 1+2.5*3
Value of expression: 10.513.
14.
Programming Projects 159
The operands in the expression are floating-point numbers; the operators are +, -, *, and /
The expression is evaluated from left to right (no operator takes precedence over any other
operator).
Write a program that calculates the average word length for a sentence:
Enter a sentence: It was deja vu all over again.
Average word length: 3.4
For simplicity, your program should consider a punctuation mark to be part of the word to
which itis attached, Display the average word length to one decimal place.
Write a program that uses Newton’s method to compute the square root of a positive float-
ing-point number:
Enter a positive number:
Square root: 1.73205
3
Let x be the number entered by the user. Newton’s method requires an initial guess y for the
square root of x (we'll use y = 1). Successive guesses are found by computing the average of
y and x/y. The following table shows how the square root of 3 would be found:
Average of
x y aly y and x/y
Ane 2 2
3.2 aS 1.75
ae Tote 1.71429 1.73214
3° (1.73214 1.73196 1.73205
3. 1.73205 1.73205 1.73205
Note that the values of y get progressively closer to the true square root of x. For greater
accuracy, your program should use variables of type double rather than £1oat. Have the
program terminate when the absolute value of the difference between the old value of y and
the new value of y is less than the product of .00001 and y. Hint: Call the £abs function to
find the absolute Value of a double. (You'll need to include the header at the
beginning of your program in order to use fabs.)
Write a program that computes the factorial of a positive integer:
Enter a positive integer: 6
Factorial of 6: 720
(a) Use a short variable to store the value of the factorial. What is the largest value of n
for which the program correctly prints the factorial of n?
(b) Repeat part (a), using an int variable instead.
(c) Repeat part (a), using a Long variable instead,
(@) Repeat part (a), using a Long long variable instead (if your compiler supports the
long long type).
(e) Repeat part (a), using a £1oat variable instead.
(£) Repeat part (a), using a double variable instead.
(g) Repeat part (a), using a long double variable instead.
In cases (¢)-(g), the program will display a close approximation of the factorial, not neces-
sarily the exact value.