Search for notes by fellow students, in your own course and all over the country.

Browse our notes for titles which look like what you need, you can preview any of the notes via a sample of the contents. After you're happy these are the notes you're after simply pop them into your shopping cart.

My Basket

You have nothing in your shopping cart yet.

Title: C language
Description: Full knowledge of c language..

Document Preview

Extracts from the notes are below, to see the PDF you'll receive please use the links above


Table of Contents
Preface
...
1
Introduction
...
1
1
...
1
1
...
3
1
...
7
1
...
8
1
...
8
1
...
1 File Copying
...
5
...
9
1
...
3 Line Counting
...
5
...
11
1
...
12
1
...
14
1
...
16
1
...
17
1
...
19
Chapter 2 - Types, Operators and Expressions
...
1 Variable Names
...
2 Data Types and Sizes
...
3 Constants
...
4 Declarations
...
5 Arithmetic Operators
...
6 Relational and Logical Operators
...
7 Type Conversions
...
8 Increment and Decrement Operators
...
9 Bitwise Operators
...
10 Assignment Operators and Expressions
...
11 Conditional Expressions
...
12 Precedence and Order of Evaluation
...
1
3
...
1
3
...
1
3
...
2
3
...
3
3
...
4
3
...
6
3
...
7
3
...
8
Chapter 4 - Functions and Program Structure
...
1 Basics of Functions
...
2 Functions Returning Non-integers
...
3 External Variables
...
4 Scope Rules
...
5 Header Files
...
6 Static Variables
...
7 Register Variables
...
8 Block Structure
...
9 Initialization
...
10 Recursion
...
11 The C Preprocessor
...
11
...
17
4
...
2 Macro Substitution
...
11
...
19
Chapter 5 - Pointers and Arrays
...
1 Pointers and Addresses
...
2 Pointers and Function Arguments
...
3 Pointers and Arrays
...
4 Address Arithmetic
...
5 Character Pointers and Functions
...
6 Pointer Arrays; Pointers to Pointers
...
7 Multi-dimensional Arrays
...
8 Initialization of Pointer Arrays
...
9 Pointers vs
...
17
5
...
18
5
...
22
5
...
24
Chapter 6 - Structures
...
1 Basics of Structures
...
2 Structures and Functions
...
3 Arrays of Structures
...
4 Pointers to Structures
...
5 Self-referential Structures
...
6 Table Lookup
...
7 Typedef
...
8 Unions
...
9 Bit-fields
...
1
7
...
1
7
...
2
7
...
3
7
...
4
7
...
7
7
...
9
7
...
10
7
...
11
7
...
1 String Operations
...
8
...
12
7
...
3 Ungetc
...
8
...
12
7
...
5 Storage Management
...
8
...
13
7
...
7 Random Number generation
...
1
8
...
1
8
...
1
8
...
3
8
...
5
8
...
5
8
...
8
8
...
12
Appendix A - Reference Manual
...
1 Introduction
...
2 Lexical Conventions
...
2
...
1
A
...
2 Comments
...
2
...
1
A
...
4 Keywords
...
2
...
2
A
...
6 String Literals
...
3 Syntax Notation
...
4 Meaning of Identifiers
...
4
...
4
A
...
2 Basic Types
...
4
...
5
A
...
4 Type Qualifiers
...
5 Objects and Lvalues
...
6 Conversions
...
6
...
6
A
...
2 Integral Conversions
...
6
...
6
A
...
4 Floating Types
...
6
...
7
A
...
6 Pointers and Integers
...
6
...
8
A
...
8 Pointers to Void
...
7 Expressions
...
7
...
8
A
...
2 Primary Expressions
...
7
...
9
A
...
4 Unary Operators
...
7
...
12
A
...
6 Multiplicative Operators
...
7
...
13
A
...
8 Shift Operators
...
7
...
14
A
...
10 Equality Operators
...
7
...
15
A
...
12 Bitwise Exclusive OR Operator
...
7
...
15
A
...
14 Logical AND Operator
...
7
...
15
A
...
16 Conditional Operator
...
7
...
16
iii

Table of Contents
Appendix A - Reference Manual
A
...
18 Comma Operator
...
7
...
17
A
...
17
A
...
1 Storage Class Specifiers
...
8
...
18
A
...
3 Structure and Union Declarations
...
8
...
22
A
...
5 Declarators
...
8
...
23
A
...
7 Initialization
...
8
...
28
A
...
9 Typedef
...
8
...
29
A
...
29
A
...
1 Labeled Statements
...
9
...
30
A
...
3 Compound Statement
...
9
...
31
A
...
5 Iteration Statements
...
9
...
32
A
...
32
A
...
1 Function Definitions
...
10
...
34
A
...
35
A
...
1 Lexical Scope
...
11
...
35
A
...
36
A
...
1 Trigraph Sequences
...
12
...
36
A
...
3 Macro Definition and Expansion
...
12
...
38
A
...
5 Conditional Compilation
...
12
...
40
A
...
7 Error Generation
...
12
...
40
A
...
9 Null directive
...
12
...
41
A
...
41
Appendix B - Standard Library
...
1
...
1
B
...
2 Formatted Output
...
1
...
4
B
...
4 Character Input and Output Functions
...
1
...
6
B
...
6 File Positioning Functions
...
1
...
6
B
...
h>
...
3 String Functions: ...
7
B
...
h>
...
5 Utility Functions: ...
9
iv

Table of Contents
Appendix B - Standard Library
B
...
h>
...
7 Variable Argument Lists: ...
11
B
...
h>
...
9 Signals: ...
12
B
...
h>
...
11 Implementation-defined Limits: ...
h>
...
1

v

Preface
The computing world has undergone a revolution since the publication of The C Programming Language in
1978
...
During this time, C has changed too, although only modestly, and it has spread far beyond its
origins as the language of the UNIX operating system
...
In 1983, the American National
Standards Institute (ANSI) established a committee whose goal was to produce ``an unambiguous and
machine-independent definition of the language C'', while still retaining its spirit
...

The standard formalizes constructions that were hinted but not described in the first edition, particularly
structure assignment and enumerations
...
It specifies a standard library, with an extensive set of functions for
performing input and output, memory management, string manipulation, and similar tasks
...

This Second Edition of The C Programming Language describes C as defined by the ANSI standard
...
For the most part, this makes no significant difference; the most visible change is the new form
of function declaration and definition
...

We have tried to retain the brevity of the first edition
...
We have improved the exposition of critical features, such as pointers, that are central to C
programming
...
For
instance, the treatment of complicated declarations is augmented by programs that convert declarations into
words and vice versa
...

Appendix A, the reference manual, is not the standard, but our attempt to convey the essentials of the standard
in a smaller space
...
Appendix B is a summary of the facilities of the
standard library
...
Appendix C is a concise
summary of the changes from the original version
...
With a decade
more experience, we still feel that way
...

We are deeply indebted to friends who helped us to produce this second edition
...
We are grateful for careful reading by Al Aho, Dennis Allison, Joe Campbell, G
...
Emlin, Karen
Fortgang, Allen Holub, Andrew Hume, Dave Kristol, John Linderman, Dave Prosser, Gene Spafford, and
Chris van Wyk
...
Dave Prosser answered many
detailed questions about the ANSI standard
...
Rich
Drechsler helped greatly with typesetting
...


Preface

1

2

Preface

Brian W
...
Ritchie
Index -- Preface to the first edition
Compiled by tmdcjsl(skidrow8123@hotmail
...
C is not a ``very high level'' language, nor a ``big'' one, and is not
specialized to any particular area of application
...

C was originally designed for and implemented on the UNIX operating system on the DEC PDP-11, by
Dennis Ritchie
...
Production compilers also exist for
several other machines, including the IBM System/370, the Honeywell 6000, and the Interdata 8/32
...

This book is meant to help the reader learn how to program in C
...
Most of the
treatment is based on reading, writing and revising examples, rather than on mere statements of rules
...
All examples have been
tested directly from the text, which is in machine-readable form
...

The book is not an introductory programming manual; it assumes some familiarity with basic programming
concepts like variables, assignment statements, loops, and functions
...

In our experience, C has proven to be a pleasant, expressive and versatile language for a wide variety of
programs
...
We hope that this book will
help you to use it well
...
In particular, Mike Bianchi, Jim Blue, Stu Feldman, Doug McIlroy Bill Roome,
Bob Rosin and Larry Rosler all read multiple volumes with care
...

Brian W
...
Ritchie
Back to the Preface -- Index -- Introduction
Compiled by tmdcjsl(skidrow8123@hotmail
...
It has been closely associated with the UNIX operating system
where it was developed, since both the system and most of the programs that run on it are written in C
...

Many of the important ideas of C stem from the language BCPL, developed by Martin Richards
...

BCPL and B are ``typeless'' languages
...
The fundamental types
are characters, and integers and floating point numbers of several sizes
...
Expressions are formed from operators
and operands; any expression, including an assignment or a function call, can be a statement
...

C provides the fundamental control-flow constructions required for well-structured programs: statement
grouping, decision making (if-else), selecting one of a set of possible values (switch), looping with the
termination test at the top (while, for) or at the bottom (do), and early loop exit (break)
...
Any function may be called
recursively
...
Function
definitions may not be nested but variables may be declared in a block-structured fashion
...
Variables may be internal to a
function, external but known only within a single source file, or visible to the entire program
...

C is a relatively ``low-level'' language
...
These may
be combined and moved about with the arithmetic and logical operators implemented by real machines
...

There are no operations that manipulate an entire array or string, although structures may be copied as a unit
...
Finally, C itself provides
no input/output facilities; there are no READ or WRITE statements, and no built-in file access methods
...
Most C implementations
have included a reasonably standard collection of such functions
...

Although the absence of some of these features may seem like a grave deficiency, (``You mean I have to call
a function to compare two character strings?''), keeping the language down to modest size has real benefits
...
A programmer can
reasonably expect to know and understand and indeed regularly use the entire language
...
In 1983, the American National Standards Institute (ANSI) established a committee to provide a
modern, comprehensive definition of C
...
Most of the features of the standard are already supported by modern compilers
...
The language is relatively little changed; one of the
goals of the standard was to make sure that most existing programs would remain valid, or, failing that, that
compilers could produce warnings of new behavior
...
A
function declaration can now include a description of the arguments of the function; the definition syntax
changes to match
...

There are other small-scale language changes
...
Floating-point computations may now be done in single
precision
...
The preprocessor is more
elaborate
...

A second significant contribution of the standard is the definition of a library to accompany C
...
A collection of standard headers provides uniform
access to declarations of functions in data types
...
Most of the library is closely modeled on the ``standard I/O library'' of the
UNIX system
...
Again, most programmers will not see much change
...
The standard library functions are only
called explicitly, so they can be avoided if they are not needed
...

Although C matches the capabilities of many computers, it is independent of any particular machine
architecture
...
The standard makes portability issues explicit, and prescribes a set of
constants that characterize the machine on which the program is run
...
The original
definition of C frowned on, but permitted, the interchange of pointers and integers; this has long since been
eliminated, and the standard now requires the proper declarations and explicit conversions that had already
been enforced by good compilers
...
Compilers
will warn of most type errors, and there is no automatic conversion of incompatible data types
...

C, like any other language, has its blemishes
...
Nonetheless, C has proven to ben an extremely effective and expressive language
for a wide variety of programming applications
...
Chapter 1 is a tutorial on the central part of C
...
The tutorial does assume a working knowledge of the basic elements of programming; there is
no explanation of computers, of compilation, nor of the meaning of an expression like n=n+1
...

Chapters 2 through 6 discuss various aspects of C in more detail, and rather more formally, than does Chapter
2

Introduction

Introduction

3

1, although the emphasis is still on examples of complete programs, rather than isolated fragments
...
Chapter 3 threats control flow: if-else,
switch, while, for, etc
...
Chapter 5 discusses pointers and
address arithmetic
...

Chapter 7 describes the standard library, which provides a common interface to the operating system
...

Chapter 8 describes an interface between C programs and the UNIX operating system, concentrating on
input/output, the file system, and storage allocation
...

Appendix A contains a language reference manual
...
That document, however, is intended foremost for compiler writers
...
Appendix B is a summary of the standard library, again for users rather than implementers
...
In cases of doubt, however, the standard and one's
own compiler remain the final authorities on the language
...
com)

Back to Introduction -- Index -- Chapter 2

Introduction

3

Chapter 1 - A Tutorial Introduction
Let us begin with a quick introduction in C
...
At this point, we are not trying to
be complete or even precise (save that the examples are meant to be correct)
...
We
are intentionally leaving out of this chapter features of C that are important for writing bigger programs
...

This approach and its drawbacks
...
And because the examples do not use the full
power of C, they are not as concise and elegant as they might be
...
Another drawback is that later chapters will necessarily repeat some of this chapter
...

In any case, experienced programmers should be able to extrapolate from the material in this chapter to their
own programming needs
...

Both groups can use it as a framework on which to hang the more detailed descriptions that begin in Chapter
2
...
1 Getting Started
The only way to learn a new programming language is by writing programs in it
...
With these mechanical details mastered,
everything else is comparatively easy
...
As a specific example, on the UNIX
operating system you must create the program in a file whose name ends in ``
...
c, then
compile it with the command
cc hello
...
out
...
out by typing the command
a
...

Chapter 1 - A Tutorial Introduction

1

2

Chapter 1 - A Tutorial Introduction

Now, for some explanations about the program itself
...
A function contains statements that specify the computing operations to be done, and variables
store values used during the computation
...
Our example is a function named main
...
This means that every program must have a main somewhere
...
The first line of the program,
#include ...
The standard library is described in Chapter 7 and Appendix B
...
The parentheses after the function name surround the argument list
...

"hello, world\n");
}

main calls library function printf
to print this sequence of characters
\n represents the newline character

The first C program
The statements of a function are enclosed in braces { }
...
printf is a library function that prints output, in this
case the string of characters between the quotes
...
For the moment our only use of character strings will be as arguments for printf and other
functions
...
If you leave out the \n (a worthwhile experiment), you will find that
there is no line advance after the output is printed
...

printf never supplies a newline character automatically, so several calls may be used to build up an output
line in stages
...
h>
main()
{
printf("hello, ");
printf("world");

2

Chapter 1 - A Tutorial Introduction

Chapter 1 - A Tutorial Introduction

3

printf("\n");
}

to produce identical output
...
An escape sequence like \n provides a general and
extensible mechanism for representing hard-to-type or invisible characters
...
There is a complete
list in Section 2
...

Exercise 1-1
...
Experiment with leaving out parts of the
program, to see what error messages you get
...
Experiment to find out what happens when prints's argument string contains \c, where c is
some character not listed above
...
2 Variables and Arithmetic Expressions
The next program uses the formula oC=(5/9)(oF-32) to print the following table of Fahrenheit temperatures
and their centigrade or Celsius equivalents:
1
20
40
60
80
100
120
140
160
180
200
220
240
260
280
300

-17
-6
4
15
26
37
48
60
71
82
93
104
115
126
137
148

The program itself still consists of the definition of a single function named main
...
It introduces several new ideas, including comments,
declarations, variables, arithmetic expressions, loops , and formatted output
...
h>
/* print Fahrenheit-Celsius table
for fahr = 0, 20,
...
, 300 */

are a comment, which in this case explains briefly what the program does
...
Comments may
appear anywhere where a blank, tab or newline can
...
A declaration announces the properties of variables; it consists of a name and a list of
variables, such as
int fahr, celsius;
int lower, upper, step;

The type int means that the variables listed are integers; by contrast with float, which means floating
point, i
...
, numbers that may have a fractional part
...

A float number is typically a 32-bit quantity, with at least six significant digits and magnitude generally
between about 10-38 and 1038
...
There are also arrays, structures and unions of these
basic types, pointers to them, and functions that return them, all of which we will meet in due course
...
Individual statements are terminated by semicolons
...

}

The while loop operates as follows: The condition in parentheses is tested
...
Then the
condition is re-tested, and if true, the body is executed again
...
There are no further
statements in this program, so it terminates
...
The indentation
emphasizes the logical structure of the program
...
We recommend
writing only one statement per line, and using blanks around operators to clarify grouping
...
We have chosen one of several popular
styles
...

Most of the work gets done in the body of the loop
...
Since 5 and 9 are integers
...

This example also shows a bit more of how printf works
...
Its first argument is a string of characters to be printed,
with each % indicating where one of the other (second, third,
...
For instance, %d specifies an integer argument, so the statement
printf("%d\t%d\n", fahr, celsius);

causes the values of the two integers fahr and celsius to be printed, with a tab (\t) between them
...
; they must match up properly by number and type, or you will get wrong answers
...
printf is
just a useful function from the standard library of functions that are normally accessible to C programs
...

In order to concentrate on C itself, we don't talk much about input and output until chapter 7
...
If you have to input numbers, read the discussion of the function scanf
in Section 7
...
scanf is like printf, except that it reads input instead of writing output
...
The simpler one is that the output
isn't very pretty because the numbers are not right-justified
...
For instance, we
might say
printf("%3d %6d\n", fahr, celsius);

to print the first number of each line in a field three digits wide, and the second in a field six digits wide, like
this:
0
20

-17
-6

Chapter 1 - A Tutorial Introduction

5

6

Chapter 1 - A Tutorial Introduction
40
60
80
100

...
8oC, not -17
...
This requires some changes in the program
...
h>
/* print Fahrenheit-Celsius table
for fahr = 0, 20,
...
0/9
...
0);
printf("%3
...
1f\n", fahr, celsius);
fahr = fahr + step;
}
}

This is much the same as before, except that fahr and celsius are declared to be float and the formula
for conversion is written in a more natural way
...
A decimal point in a constant indicates that it is floating point,
however, so 5
...
0 is not truncated because it is the ratio of two floating-point values
...
If an arithmetic operator has
one floating-point operand and one integer operand, however, the integer will be converted to floating point
before the operation is done
...
Nevertheless, writing floating-point constants with explicit decimal points even when they have
integral values emphasizes their floating-point nature for human readers
...
For now, notice that the
assignment
fahr = lower;

and the test
while (fahr <= upper)

also work in the natural way - the int is converted to float before the operation is done
...
0f says that a floating-point number (here fahr) is to be printed
at least three characters wide, with no decimal point and no fraction digits
...
1f describes another number
(celsius) that is to be printed at least six characters wide, with 1 digit after the decimal point
...


7

-17
...
7
4
...
2f specifies two characters after the decimal point, but the width is not constrained; and
%f merely says to print the number as floating point
...
2f
print as floating point, 2 characters after decimal point
%6
...

Exercise 1-3
...

Exercise 1-4
...


1
...
Let's try a variation on the
temperature converter
...
1f\n", fahr, (5
...
0)*(fahr-32));
}

This produces the same answers, but it certainly looks different
...
The lower and upper limits and the step
size appear only as constants in the for statement, itself a new construction, and the expression that computes
the Celsius temperature now appears as the third argument of printf instead of a separate assignment
statement
...
Since the third argument of printf must
be a floating-point value to match the %6
...

The for statement is a loop, a generalization of the while
...
Within the parentheses, there are three parts, separated by semicolons
...
The second part is the test or condition that controls the loop:
fahr <= 300

This condition is evaluated; if it is true, the body of the loop (here a single ptintf) is executed
...
The loop terminates if the condition has become false
...
The
initialization, condition and increment can be any expressions
...
The for is usually
appropriate for loops in which the initialization and increment are single statements and logically related,
since it is more compact than while and it keeps the loop control statements together in one place
...
Modify the temperature conversion program to print the table in reverse order, that is, from 300
degrees to 0
...
4 Symbolic Constants
A final observation before we leave temperature conversion forever
...
One way to deal with magic numbers is to
give them meaningful names
...
The name has the same form as a variable name: a sequence of letters and
digits that begins with a letter
...

#include ...
1f\n", fahr, (5
...
0)*(fahr-32));
}

The quantities LOWER, UPPER and STEP are symbolic constants, not variables, so they do not appear in
declarations
...
Notice that there is no semicolon at the end of a #define
line
...
5 Character Input and Output
We are going to consider a family of related programs for processing character data
...

The model of input and output supported by the standard library is very simple
...
A text stream is a
sequence of characters divided into lines; each line consists of zero or more characters followed by a newline
character
...

8

Chapter 1 - A Tutorial Introduction

Chapter 1 - A Tutorial Introduction

9

The standard library provides several functions for reading or writing one character at a time, of which
getchar and putchar are the simplest
...
That is, after
c = getchar();

the variable c contains the next character of input
...

The function putchar prints a character each time it is called:
putchar(c);

prints the contents of the integer variable c as a character, usually on the screen
...


1
...
1 File Copying
Given getchar and putchar, you can write a surprising amount of useful code without knowing anything
more about input and output
...
html">Chapter 2
...
Verify that the expression getchar() != EOF is 0 or 1
...
Write a program to print the value of EOF
...
5
...

#include ...
You could instead write nc = nc + 1 but
Chapter 1 - A Tutorial Introduction

9

10

Chapter 1 - A Tutorial Introduction

++nc is more concise and often more efficient
...
The
operators ++ and -- can be either prefix operators (++nc) or postfix operators (nc++); these two forms have
different values in expressions, as will be shown in Chapter 2, but ++nc and nc++ both increment nc
...

The character counting program accumulates its count in a long variable instead of an int
...
Although on some machines, int and long are the same size, on others an int is 16 bits,
with a maximum value of 32767, and it would take relatively little input to overflow an int counter
...

It may be possible to cope with even bigger numbers by using a double (double precision float)
...

"%
...
0f suppresses the printing of the decimal point and the
fraction part, which is zero
...
But the
grammatical rules of C require that a for statement have a body
...
We put it on a separate line to make it visible
...
This is
important
...
If there is nothing to do, nothing is done, even if that means never going through
the loop body
...
The while and for
statements help ensure that programs do reasonable things with boundary conditions
...
5
...
As we mentioned above, the standard library ensures that an input text
stream appears as a sequence of lines, each terminated by a newline
...
h>
/* count lines in input */
main()
{
int c, nl;
nl = 0;
while ((c = getchar()) != EOF)
if (c == '\n')
++nl;
printf("%d\n", nl);
}

The body of the while now consists of an if, which in turn controls the increment ++nl
...
We have again indented to show what is controlled by what
...
EQ
...
A word of caution:
newcomers to C occasionally write = when they mean ==
...

A character written between single quotes represents an integer value equal to the numerical value of the
character in the machine's character set
...
So, for example, 'A' is a character constant; in the ASCII character set its value is 65,
the internal representation of the character A
...

The escape sequences used in string constants are also legal in character constants, so '\n' stands for the
value of the newline character, which is 10 in ASCII
...
The topic of strings versus characters is discussed further in Chapter 2
...
Write a program to count blanks, tabs, and newlines
...
Write a program to copy its input to its output, replacing each string of one or more blanks by a
single blank
...
Write a program to copy its input to its output, replacing each tab by \t, each backspace by
\b, and each backslash by \\
...


1
...
4 Word Counting
The fourth in our series of useful programs counts lines, words, and characters, with the loose definition that a
word is any sequence of characters that does not contain a blank, tab or newline
...

"%d %d %d\n", nl, nw, nc);
}

Every time the program encounters the first character of a word, it counts one more word
...
We prefer the symbolic constants IN and OUT to the literal values 1 and 0 because
they make the program more readable
...

You'll also find that it's easier to make extensive changes in programs where magic numbers appear only as
symbolic constants
...
This is not a special case, but a consequence of the fact that an assignment is an
expression with the value and assignments associated from right to left
...
(Recall that the escape sequence \t is a visible
representation of the tab character
...
Expressions connected by && or || are evaluated left to right, and it is guaranteed that
evaluation will stop as soon as the truth or falsehood is known
...
This isn't particularly important here, but is significant in
more complicated situations, as we will soon see
...
The general form is
if (expression)
statement1
else
statement2

One and only one of the two statements associated with an if-else is performed
...
Each statement can be a single statement or several in
braces
...

Exercise 1-11
...
Write a program that prints its input one word per line
...
6 Arrays
Let is write a program to count the number of occurrences of each digit, of white space characters (blank, tab,
newline), and of all other characters
...

There are twelve categories of input, so it is convenient to use an array to hold the number of occurrences of
each digit, rather than ten individual variables
...
h>
/* count digits, white space, others */
main()
{
int c, i, nwhite, nother;
int ndigit[10];
nwhite = nother = 0;
for (i = 0; i < 10; ++i)
ndigit[i] = 0;
while ((c = getchar()) != EOF)
if (c >= '0' && c <= '9')
++ndigit[c-'0'];
else if (c == ' ' || c == '\n' || c == '\t')
++nwhite;
else
++nother;
printf("digits =");
for (i = 0; i < 10; ++i)
printf(" %d", ndigit[i]);
printf(", white space = %d, other = %d\n",
nwhite, nother);
}

The output of this program on itself is
digits = 9 3 0 0 0 0 0 0 0 1, white space = 123, other = 345

12

Chapter 1 - A Tutorial Introduction

Chapter 1 - A Tutorial Introduction

13

The declaration
int ndigit[10];

declares ndigit to be an array of 10 integers
...
, ndigit[9]
...

A subscript can be any integer expression, which includes integer variables like i, and integer constants
...
For example, the
test
if (c >= '0' && c <= '9')

determines whether the character in c is a digit
...
, '9' have consecutive increasing values
...

By definition, chars are just small integers, so char variables and constants are identical to ints in
arithmetic expressions
...

The decision as to whether a character is a digit, white space, or something else is made with the sequence
if (c >= '0' && c <= '9')
++ndigit[c-'0'];
else if (c == ' ' || c == '\n' || c == '\t')
++nwhite;
else
++nother;

The pattern
if (condition1)
statement1
else if (condition2)
statement2

...

else
statementn

occurs frequently in programs as a way to express a multi-way decision
...
(Any statement can be several statements enclosed in braces
...
If the final else and
statement are omitted, as in the word count program, no action takes place
...

Chapter 1 - A Tutorial Introduction

13

14

Chapter 1 - A Tutorial Introduction

As a matter of style, it is advisable to format this construction as we have shown; if each if were indented
past the previous else, a long sequence of decisions would march off the right side of the page
...
For contrast, we will present a switch version of this program in Section 3
...

Exercise 1-13
...
It is easy to draw the
histogram with the bars horizontal; a vertical orientation is more challenging
...
Write a program to print a histogram of the frequencies of different characters in its input
...
7 Functions
In C, a function is equivalent to a subroutine or function in Fortran, or a procedure or function in Pascal
...
With properly designed functions, it is possible to ignore how a job is
done; knowing what is done is sufficient
...

So far we have used only functions like printf, getchar and putchar that have been provided for us;
now it's time to write a few of our own
...
That is, the value of power(2,5) is 32
...
(The standard library contains a function pow(x,y) that computes xy
...

#include ...
If the source program appears in several files, you may have to say more to compile and
load it than if it all appears in one, but that is an operating system matter, not a language attribute
...

The function power is called twice by main, in the line
printf("%d %d %d\n", i, power(2,i), power(-3,i));

Each call passes two arguments to power, which each time returns an integer to be formatted and printed
...
(Not all functions produce an integer value; we
will take this up in Chapter 4
...
The names used
by power for its parameters are local to power, and are not visible to any other function: other routines can
use the same names without conflict
...

We will generally use parameter for a variable named in the parenthesized list in a function
...

The value that power computes is returned to main by the return: statement
...

And the calling function can ignore a value returned by a function
...
Since main is a function like
any other, it may return a value to its caller, which is in effect the environment in which the program was
executed
...
In the interests of simplicity, we have omitted return statements from our
main functions up to this point, but we will include them hereafter, as a reminder that programs should return
status to their environment
...
This
declaration, which is called a function prototype, has to agree with the definition and uses of power
...

parameter names need not agree
...

A note of history: the biggest change between ANSI C and earlier versions is how functions are declared and
defined
...
(The body of the function is the same as before
...
Indeed, since by default power would have been assumed to return an int, the entire declaration
might well have been omitted
...
The old style of declaration and definition still works in ANSI C, at least for a
transition period, but we strongly recommend that you use the new form when you have a compiler that
supports it
...
15
...
2 to use a function for conversion
...
8 Arguments - Call by Value
One aspect of C functions may be unfamiliar to programmers who are used to some other languages,
particulary Fortran
...
'' This means that the called function is
given the values of its arguments in temporary variables rather than the originals
...

Call by value is an asset, however, not a liability
...
For example, here is a version of power that makes use of this property
...
Whatever is done to n inside power has no
effect on the argument that power was originally called with
...
The caller
must provide the address of the variable to be set (technically a pointer to the variable), and the called
function must declare the parameter to be a pointer and access the variable indirectly through it
...

The story is different for arrays
...
By
subscripting this value, the function can access and alter any argument of the array
...


1
...
To illustrate the use of character arrays and
functions to manipulate them, let's write a program that reads a set of text lines and prints the longest
...
One piece gets a new line, another
saves it, and the rest controls the process
...
Accordingly, let us first write a
separate function getline to fetch the next line of input
...
At the minimum, getline has to return a signal about possible end of file; a more useful design
would be to return the length of the line, or zero if end of file is encountered
...
Every text line has at least one character; even a line containing
only a newline has length 1
...
This suggests a
second function, copy, to copy the new line to a safe place
...
Here is the result
...

main and getline communicate through a pair of arguments and a returned value
...
The purpose of
supplying the size of an array in a declaration is to set aside storage
...
getline uses return to send a value back to the caller, just as
the function power did
...

Some functions return a useful value; others, like copy, are used only for their effect and return no value
...

getline puts the character '\0' (the null character, whose value is zero) at the end of the array it is
creating, to mark the end of the string of characters
...


The %s format specification in printf expects the corresponding argument to be a string represented in this
form
...

It is worth mentioning in passing that even a program as small as this one presents some sticky design
problems
...
By testing the
length and the last character returned, main can determine whether the line was too long, and then cope as it
wishes
...

There is no way for a user of getline to know in advance how long an input line might be, so getline
checks for overflow
...

Exercise 1-16
...

Exercise 1-17
...

Exercise 1-18
...

Exercise 1-19
...
Use it to write a program
that reverses its input a line at a time
...
10 External Variables and Scope
The variables in main, such as line, longest, etc
...
Because they are
declared within main, no other function can have direct access to them
...
Each local variable in a
function comes into existence only when the function is called, and disappears when the function is exited
...
We will use the term automatic henceforth to refer to these local variables
...
)
Because automatic variables come and go with function invocation, they do not retain their values from one
call to the next, and must be explicitly set upon each entry
...

As an alternative to automatic variables, it is possible to define variables that are external to all functions, that
is, variables that can be accessed by name by any function
...
) Because external variables are globally accessible, they
can be used instead of argument lists to communicate data between functions
...

An external variable must be defined, exactly once, outside of any function; this sets aside storage for it
...
The
declaration may be an explicit extern statement or may be implicit from context
...
This
requires changing the calls, declarations, and bodies of all three functions
...
Syntactically, external definitions are just
like definitions of local variables, but since they occur outside of functions, the variables are external
...

In certain circumstances, the extern declaration can be omitted
...
The extern declarations in main, getline and copy are thus redundant
...

If the program is in several source files, and a variable is defined in file1 and used in file2 and file3, then
extern declarations are needed in file2 and file3 to connect the occurrences of the variable
...
The suffix
...
The functions of the standard library, for example, are declared in headers like ...

This topic is discussed at length in Chapter 4, and the library itself in Chapter 7 and Appendix B
...
But for compatibility with older
C programs the standard takes an empty list as an old-style declaration, and turns off all argument list
checking; the word void must be used for an explicitly empty list
...

You should note that we are using the words definition and declaration carefully when we refer to external
variables in this section
...

By the way, there is a tendency to make everything in sight an extern variable because it appears to
simplify communications - argument lists are short and variables are always there when you want them
...
Relying too heavily on external variables
is fraught with peril since it leads to programs whose data connections are not all obvious - variables can be
changed in unexpected and even inadvertent ways, and the program is hard to modify
...

At this point we have covered what might be called the conventional core of C
...
These exercises suggest programs of somewhat greater complexity than the ones
earlier in this chapter
...
Write a program detab that replaces tabs in the input with the proper number of blanks to
space to the next tab stop
...
Should n be a variable or a
20

Chapter 1 - A Tutorial Introduction

Chapter 1 - A Tutorial Introduction

21

symbolic parameter?
Exercise 1-21
...
Use the same tab stops as for detab
...
Write a program to ``fold'' long input lines into two or more shorter lines after the last
non-blank character that occurs before the n-th column of input
...

Exercise 1-23
...
Don't forget to handle quoted
strings and character constants properly
...

Exercise 1-24
...
Don't forget about quotes, both single and double, escape sequences, and
comments
...
)
Back to Introduction -- Index -- Chapter 2
Compiled by tmdcjsl(skidrow8123@hotmail
...
Declarations list the variables to
be used, and state what type they have and perhaps what their initial values are
...
Expressions combine variables and constants to produce new values
...
These building blocks are
the topics of this chapter
...
There are
now signed and unsigned forms of all integer types, and notations for unsigned constants and
hexadecimal character constants
...
String constants may be concatenated at compile time
...
Objects may be
declared const, which prevents them from being changed
...


2
...
Names are made up of letters and digits; the first character must be a letter
...
Don't begin
variable names with underscore, however, since library routines often use such names
...
Traditional C practice is to use lower case for variable
names, and all upper case for symbolic constants
...
For function names and external variables,
the number may be less than 31, because external names may be used by assemblers and loaders over which
the language has no control
...
Keywords like if, else, int, float, etc
...
They must be in lower case
...
We tend to use short names for local variables, especially loop indices, and longer
names for external variables
...
2 Data Types and Sizes
There are only a few basic data types in C:
char
a single byte, capable of holding one character in the local character set
int
an integer, typically reflecting the natural size of integers on the host machine
float single-precision floating point
double double-precision floating point
In addition, there are a number of qualifiers that can be applied to these basic types
...

The intent is that short and long should provide different lengths of integers where practical; int will
Chapter 2 - Types, Operators and Expressions

1

2

Chapter 2 - Types, Operators and Expressions

normally be the natural size for a particular machine
...
Each compiler is free to choose appropriate sizes for its own hardware, subject only to the the restriction
that shorts and ints are at least 16 bits, longs are at least 32 bits, and short is no longer than int, which
is no longer than long
...
unsigned numbers are
always positive or zero, and obey the laws of arithmetic modulo 2n, where n is the number of bits in the type
...
) Whether plain chars
are signed or unsigned is machine-dependent, but printable characters are always positive
...
As with integers, the sizes of
floating-point objects are implementation-defined; float, double and long double could represent
one, two or three distinct sizes
...
h> and ...
These are discussed in Appendix B
...
Write a program to determine the ranges of char, short, int, and long variables, both
signed and unsigned, by printing appropriate values from standard headers and by direct computation
...


2
...
A long constant is written with a terminal l (ell) or L, as in
123456789L; an integer constant too big to fit into an int will also be taken as a long
...

Floating-point constants contain a decimal point (123
...
The suffixes f or F indicate a float constant; l or L indicate a long double
...
A leading 0 (zero) on an
integer constant means octal; a leading 0x or 0X means hexadecimal
...
Octal and hexadecimal constants may also be followed by L to
make them long and U to make them unsigned: 0XFUL is an unsigned long constant with value 15
decimal
...
The
value of a character constant is the numeric value of the character in the machine's character set
...
If we write '0' instead of a numeric value like 48 that depends on the character set, the program is
independent of the particular value and easier to read
...

Certain characters can be represented in character and string constants by escape sequences like \n (newline);
these sequences look like two characters, but represent only one
...
7) or by
'\xhh'

2

Chapter 2 - Types, Operators and Expressions

Chapter 2 - Types, Operators and Expressions

3

where hh is one or more hexadecimal digits (0
...
f, A
...
So we might write
\a

"center" border=1>
alert (bell) character

\\

\b

backspace \? question mark

\f

formfeed

\n

newline \"

\r

carriage return \ooo

\t

horizontal tab \xhh

\v

vertical tab

backslash

\' single quote
double quote
octal number
hexadecimal number

The character constant '\0' represents the character with value zero, the null character
...

A constant expression is an expression that involves only constants
...
The same escape sequences used in character
constants apply in strings; \" represents the double-quote character
...

Technically, a string constant is an array of characters
...
This representation means that there is no limit to how long a string can be, but programs
must scan a string completely to determine its length
...
Here is our version:
/* strlen: return length of s */
int strlen(char s[])
{
int i;
while (s[i] != '\0')
++i;
return i;
}

strlen and other string functions are declared in the standard header ...

Be careful to distinguish between a character constant and a string that contains a single character: 'x' is not
the same as "x"
...
The latter is an array of characters that contains one character (the letter x) and a '\0'
...
An enumeration is a list of constant integer
values, as in
enum boolean { NO, YES };

The first name in an enum has value 0, the next 1, and so on, unless explicit values are specified
...
*/

Names in different enumerations must be distinct
...

Enumerations provide a convenient way to associate constant values with names, an alternative to #define
with the advantage that the values can be generated for you
...

Nevertheless, enumeration variables offer the chance of checking and so are often better than #defines
...


2
...
A
declaration specifies a type, and contains a list of one or more variables of that type, as in
int lower, upper, step;
char c, line[1000];

Variables can be distributed among declarations in any fashion; the lists above could well be written as
int
int
int
char
char

4

lower;
upper;
step;
c;
line[1000];

Chapter 2 - Types, Operators and Expressions

Chapter 2 - Types, Operators and Expressions

5

The latter form takes more space, but is convenient for adding a comment to each declaration for subsequent
modifications
...
If the name is followed by an equals sign and an
expression, the expression serves as an initializer, as in
char
int
int
float

esc = '\\';
i = 0;
limit = MAXLINE+1;
eps = 1
...
An explicitly initialized automatic
variable is initialized each time the function or block it is in is entered; the initializer may be any expression
...
Automatic variables for which is no explicit
initializer have undefined (i
...
, garbage) values
...
For an array, the const qualifier says that the elements will not be altered
...
71828182845905;
const char msg[] = "warning: ";

The const declaration can also be used with array arguments, to indicate that the function does not change
that array:
int strlen(const char[]);

The result is implementation-defined if an attempt is made to change a const
...
5 Arithmetic Operators
The binary arithmetic operators are +, -, *, /, and the modulus operator %
...
The expression
x % y

produces the remainder when x is divided by y, and thus is zero when y divides x exactly
...

Therefore
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
printf("%d is a leap year\n", year);
else
printf("%d is not a leap year\n", year);

The % operator cannot be applied to a float or double
...

The binary + and - operators have the same precedence, which is lower than the precedence of *, / and %,
which is in turn lower than unary + and -
...

Table 2
...


Chapter 2 - Types, Operators and Expressions

5

6

Chapter 2 - Types, Operators and Expressions

2
...
Just below them in precedence are the equality operators:
==

!=

Relational operators have lower precedence than arithmetic operators, so an expression like i < lim-1 is
taken as i < (lim-1), as would be expected
...
Expressions connected by && or || are evaluated left to
right, and evaluation stops as soon as the truth or falsehood of the result is known
...
For example, here is a loop from the input function getline that we wrote in Chapter 1:
for (i=0; i < lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
s[i] = c;

Before reading a new character it is necessary to check that there is room to store it in the array s, so the test
i < lim-1 must be made first
...

Similarly, it would be unfortunate if c were tested against EOF before getchar is called; therefore the call
and assignment must occur before the character in c is tested
...
But since the precedence of != is higher than assignment, parentheses are needed
in
(c=getchar()) != '\n'

to achieve the desired result of assignment to c and then comparison with '\n'
...

The unary negation operator ! converts a non-zero operand into 0, and a zero operand in 1
...
Constructions like !valid read nicely (``if not valid''), but
more complicated ones can be hard to understand
...
Write a loop equivalent to the for loop above without using && or ||
...
7 Type Conversions
When an operator has operands of different types, they are converted to a common type according to a small
number of rules
...
Expressions that don't make sense, like using a float as a subscript, are disallowed
...

A char is just a small integer, so chars may be freely used in arithmetic expressions
...
One is exemplified by this naive
implementation of the function atoi, which converts a string of digits into its numeric equivalent
...
, form a
contiguous increasing sequence
...
If the character is not an upper case letter, lower returns it unchanged
...
This latter
observation is not true of the EBCDIC character set, however, so this code would convert more than just
letters in EBCDIC
...
h>, described in Appendix B, defines a family of functions that provide tests
and conversions that are independent of character set
...
Similarly, the test
c >= '0' && c <= '9'

can be replaced by
isdigit(c)

Chapter 2 - Types, Operators and Expressions

7

8

Chapter 2 - Types, Operators and Expressions

We will use the ...

There is one subtle point about the conversion of characters to integers
...
When a char is converted to an int, can
it ever produce a negative integer? The answer varies from machine to machine, reflecting differences in
architecture
...
On others, a char is promoted to an int by adding zeros at the left end, and thus is always
positive
...
But arbitrary bit patterns stored
in character variables may appear to be negative on some machines, yet positive on others
...

Relational expressions like i > j and logical expressions connected by && and || are defined to have value
1 if true, and 0 if false
...
However, functions like isdigit may return any non-zero value for
true
...
, ``true'' just means ``non-zero'', so this makes no difference
...
In general, if an operator like + or * that takes two
operands (a binary operator) has operands of different types, the ``lower'' type is promoted to the ``higher''
type before the operation proceeds
...
Section 6 of Appendix A states the
conversion rules precisely
...

• Otherwise, if either operand is double, convert the other to double
...

• Otherwise, convert char and short to int
...

Notice that floats in an expression are not automatically converted to double; this is a change from the
original definition
...
h> will use double precision
...

Conversion rules are more complicated when unsigned operands are involved
...
For example, suppose that int is 16 bits and long is 32 bits
...
But -1L > 1UL because -1L
is promoted to unsigned long and thus appears to be a large positive number
...

A character is converted to an integer, either by sign extension or not, as described above
...
Thus in
int i;
char c;

8

Chapter 2 - Types, Operators and Expressions

Chapter 2 - Types, Operators and Expressions

9

i = c;
c = i;

the value of c is unchanged
...
Reversing the order of
assignments might lose information, however
...
When a double is converted to float, whether the value is rounded or
truncated is implementation dependent
...
In the absence of a function prototype, char and short become int, and float
becomes double
...

Finally, explicit type conversions can be forced (``coerced'') in any expression, with a unary operator called a
cast
...
The precise meaning of a cast is
as if the expression were assigned to a variable of the specified type, which is then used in place of the whole
construction
...
(sqrt is declared in ...
) So if n is an integer, we can use
sqrt((double) n)

to convert the value of n to double before passing it to sqrt
...
The cast operator has the same high precedence as other unary
operators, as summarized in the table at the end of this chapter
...
Thus, given a function prototype for sqrt:
double sqrt(double)

the call
root2 = sqrt(2)

coerces the integer 2 into the double value 2
...

The standard library includes a portable implementation of a pseudo-random number generator and a function
for initializing the seed; the former illustrates a cast:
unsigned long int next = 1;
/* rand: return pseudo-random integer on 0
...
Write a function htoi(s), which converts a string of hexadecimal digits (including an
optional 0x or 0X) into its equivalent integer value
...


2
...
The increment operator ++
adds 1 to its operand, while the decrement operator -- subtracts 1
...
In both cases, the effect is to increment n
...
This means
that in a context where the value is being used, not just the effect, ++n and n++ are different
...
In both cases, n becomes 6
...

In a context where no value is wanted, just the incrementing effect, as in
if (c == '\n')
nl++;

prefix and postfix are the same
...
For
instance, consider the function squeeze(s,c), which removes all occurrences of the character c from the
string s
...
This is exactly equivalent to
if (s[i] != c) {
s[j] = s[i];
j++;
}

10

Chapter 2 - Types, Operators and Expressions

Chapter 2 - Types, Operators and Expressions

11

Another example of a similar construction comes from the getline function that we wrote in Chapter 1,
where we can replace
if (c == '\n') {
s[i] = c;
++i;
}

by the more compact
if (c == '\n')
s[i++] = c;

As a third example, consider the standard function strcat(s,t), which concatenates the string t to the
end of string s
...
As we have written
it, strcat returns no value; the standard library version returns a pointer to the resulting string
...

Exercise 2-4
...

Exercise 2-5
...
(The standard library
function strpbrk does the same job but returns a pointer to the location
...
9 Bitwise Operators
C provides six operators for bit manipulation; these may only be applied to integral operands, that is, char,
short, int, and long, whether signed or unsigned
...


Chapter 2 - Types, Operators and Expressions

11

12

Chapter 2 - Types, Operators and Expressions

The bitwise OR operator | is used to turn bits on:
x = x | SET_ON;

sets to one in x the bits that are set to one in SET_ON
...

One must distinguish the bitwise operators & and | from the logical operators && and ||, which imply
left-to-right evaluation of a truth value
...

The shift operators << and >> perform left and right shifts of their left operand by the number of bit positions
given by the right operand, which must be non-negative
...
Right shifting an unsigned quantity
always fits the vacated bits with zero
...

The unary operator ~ yields the one's complement of an integer; that is, it converts each 1-bit into a 0-bit and
vice versa
...
Note that x & ~077 is independent of word length, and is thus preferable
to, for example, x & 0177700, which assumes that x is a 16-bit quantity
...

As an illustration of some of the bit operators, consider the function getbits(x,p,n) that returns the
(right adjusted) n-bit field of x that begins at position p
...
For example, getbits(x,4,3) returns the three bits in positions
4, 3 and 2, right-adjusted
...
~0 is all 1-bits; shifting
it left n positions with ~0<with ones in the rightmost n bits
...
Write a function setbits(x,p,n,y) that returns x with the n bits that begin at position p
set to the rightmost n bits of y, leaving the other bits unchanged
...
Write a function invert(x,p,n) that returns x with the n bits that begin at position p
inverted (i
...
, 1 changed into 0 and vice versa), leaving the others unchanged
...
Write a function rightrot(x,n) that returns the value of the integer x rotated to the right
by n positions
...
10 Assignment Operators and Expressions
An expression such as
12

Chapter 2 - Types, Operators and Expressions

Chapter 2 - Types, Operators and Expressions

13

i = i + 2

in which the variable on the left side is repeated immediately on the right, can be written in the compressed
form
i += 2

The operator += is called an assignment operator
...
Notice the parentheses around expr2:
x *= y + 1

means
x = x * (y + 1)

rather than
x = x * y + 1

As an example, the function bitcount counts the number of 1-bits in its integer argument
...

Quite apart from conciseness, assignment operators have the advantage that they correspond better to the way
people think
...

Thus the expression i += 2 is preferable to i = i+2
...
And an assignment
Chapter 2 - Types, Operators and Expressions

13

14

Chapter 2 - Types, Operators and Expressions

operator may even help a compiler to produce efficient code
...


The other assignment operators (+=, -=, etc
...

In all such expressions, the type of an assignment expression is the type of its left operand, and the value is the
value after the assignment
...
In a two's complement number system, x &= (x-1) deletes the rightmost 1-bit in x
...
Use this observation to write a faster version of bitcount
...
11 Conditional Expressions
The statements
if (a > b)
z = a;
else
z = b;

compute in z the maximum of a and b
...
In the expression
expr1 ? expr2 : expr3

the expression expr1 is evaluated first
...
Otherwise expr3 is evaluated, and that is the value
...
Thus to set z to the maximum of a and b,
z = (a > b) ? a : b;

/* z = max(a, b) */

It should be noted that the conditional expression is indeed an expression, and it can be used wherever any
other expression can be
...
For example, if f is a float and n an int, then the
expression
(n > 0) ? f : n

is of type float regardless of whether n is positive
...
They are advisable anyway, however, since they make the condition
part of the expression easier to see
...
For example, this loop prints n elements of an array,
10 per line, with each column separated by one blank, and with each line (including the last) terminated by a
newline
...
All other elements are followed by one
blank
...
Another good example is
printf("You have %d items%s
...
Rewrite the function lower, which converts upper case letters to lower case, with a
conditional expression instead of if-else
...
12 Precedence and Order of Evaluation
Table 2
...
Operators on the same line have the same precedence; rows are in order of decreasing
precedence, so, for example, *, /, and % all have the same precedence, which is higher than that of binary +
and -
...
The operators -> and
...
Chapter 5 discusses *
(indirection through a pointer) and & (address of an object), and Chapter 3 discusses the comma operator
...

left to right
! ~ ++ -- + - * (type)
right to left
sizeof
* / %
left to right
+ left to right
<< >>
left to right
< <= > >=
left to right
== !=
left to right
&
left to right
^
left to right
|
left to right
&&
left to right
||
left to right
?:
right to left
= += -= *= /= %= &=
right to left
^= |= <<= >>=
,
left to right
Unary & +, -, and * have higher precedence than the binary forms
...
1: Precedence and Associativity of Operators
Note that the precedence of the bitwise operators &, ^, and | falls below == and !=
...


must be fully parenthesized to give proper results
...
(The
exceptions are &&, ||, ?:, and `,'
...
Intermediate results can be stored in temporary variables to ensure a
particular sequence
...
The solution, of course, is to write
++n;
printf("%d %d\n", n, power(2, n));

Function calls, nested assignment statements, and increment and decrement operators cause ``side effects'' some variable is changed as a by-product of the evaluation of an expression
...
One unhappy situation is typified by the statement
a[i] = i++;

The question is whether the subscript is the old value of i or the new
...
The standard intentionally leaves most
such matters unspecified
...
(The standard
does specify that all side effects on arguments take effect before a function is called, but that would not help
in the call to printf above
...
Naturally, it is necessary to know what things to avoid, but if you don't know how they are done on
various machines, you won't be tempted to take advantage of a particular implementation
...
com)

Back to Chapter 2 -- Index -- Chapter 4

16

Chapter 2 - Types, Operators and Expressions

Chapter 3 - Control Flow
The control-flow of a language specify the order in which computations are performed
...


3
...
) becomes a statement when it is followed by a
semicolon, as in
x = 0;
i++;
printf(
...

Braces { and } are used to group declarations and statements together into a compound statement, or block, so
that they are syntactically equivalent to a single statement
...
(Variables can be declared inside any block; we will talk about this in Chapter 4
...


3
...
Formally the syntax is
if (expression)
statement1
else
statement2

where the else part is optional
...
If it is false (expression is zero) and if there is an else part, statement2 is
executed instead
...
The most obvious
is writing
if (expression)

instead of
if (expression != 0)

Sometimes this is natural and clear; at other times it can be cryptic
...
This is resolved by associating the else with the closest previous else-less if
...
If that isn't what you want, braces must be
used to force the proper association:
if (n > 0) {
if (a > b)
z = a;
}
else
z = b;

The ambiguity is especially pernicious in situations like this:
if (n > 0)
for (i = 0; i < n; i++)
if (s[i] > 0) {
printf("
...
This kind of bug can be hard to find; it's a good idea to use braces when there are
nested ifs
...


3
...
This sequence of if statements is the most general
way of writing a multi-way decision
...
As always, the code for each
statement is either a single statement, or a group of them in braces
...
Sometimes there is no explicit action for the default; in that case the trailing
2

Chapter 3 - Control Flow

Chapter 3 - Control Flow

3

else
statement

can be omitted, or it may be used for error checking to catch an ``impossible'' condition
...
The elements of v must be in increasing order
...

Binary search first compares the input value x to the middle element of the array v
...
In either case, the
next step is to compare x to the middle element of the selected half
...

/* binsearch: find x in v[0] <= v[1] <=
...

Exercise 3-1
...
) Write a version with only one test inside the loop and measure the difference in run-time
...
4 Switch
The switch statement is a multi-way decision that tests whether an expression matches one of a number of
constant integer values, and branches accordingly
...
If a case matches the
expression value, execution starts at that case
...
The case labeled
default is executed if none of the other cases are satisfied
...
Cases and the default clause can occur in any order
...
else if
...
Here is the same program with a switch:
"digits =");

Chapter 3 - Control Flow

3

4

Chapter 3 - Control Flow
for (i = 0; i < 10; i++)
printf(" %d", ndigit[i]);
printf(", white space = %d, other = %d\n",
nwhite, nother);
return 0;
}

The break statement causes an immediate exit from the switch
...

break and return are the most common ways to leave a switch
...

Falling through cases is a mixed blessing
...
But it also implies that normally each case must end with a
break to prevent falling through to the next
...
With the exception of multiple labels for a single
computation, fall-throughs should be used sparingly, and commented
...
Some day when another case gets added at the end, this bit of defensive programming will save
you
...
Write a function escape(s,t) that converts characters like newline and tab into visible
escape sequences like \n and \t as it copies the string t to s
...
Write a function for the other
direction as well, converting escape sequences into the real characters
...
5 Loops - While and For
We have already encountered the while and for loops
...
If it is non-zero, statement is executed and expression is re-evaluated
...

The for statement
for (expr1; expr2; expr3)
statement

is equivalent to
expr1;
while (expr2) {
statement
expr3;
}

except for the behaviour of continue, which is described in Section 3
...

Grammatically, the three components of a for loop are expressions
...
Any of the three parts can be omitted,
although the semicolons must remain
...
If
the test, expr2, is not present, it is taken as permanently true, so
for (;;) {

4

Chapter 3 - Control Flow

Chapter 3 - Control Flow

5


...

Whether to use while or for is largely a matter of personal preference
...

The for is preferable when there is a simple initialization and increment since it keeps the loop control
statements close together and visible at the top of the loop
...


which is the C idiom for processing the first n elements of an array, the analog of the Fortran DO loop or the
Pascal for
...
Because the components of the for are arbitrary expressions, for loops are not
restricted to arithmetic progressions
...

As a larger example, here is another version of atoi for converting a string to its numeric equivalent
...
(Chapter 4 shows atof, which does the same conversion for floating-point numbers
...
The whole process terminates on the
first character that could not be part of a number
...
html#sb
...


The advantages of keeping loop control centralized are even more obvious when there are several nested
loops
...
The basic idea of this sorting
algorithm, which was invented in 1959 by D
...
Shell, is that in early stages, far-apart elements are compared,
rather than adjacent ones as in simpler interchange sorts
...
The interval between compared elements is gradually decreased
to one, at which point the sort effectively becomes an adjacent interchange method
...
v[n-1] into increasing order */
void shellsort(int v[], int n)
{
int gap, i, j, temp;
for (gap = n/2; gap > 0; gap /= 2)
for (i = gap; i < n; i++)
for (j=i-gap; j>=0 && v[j]>v[j+gap]; j-=gap) {
temp = v[j];
v[j] = v[j+gap];
v[j+gap] = temp;
}

Chapter 3 - Control Flow

5

6

Chapter 3 - Control Flow
}

There are three nested loops
...
The middle loop steps along the elements
...

Since gap is eventually reduced to one, all elements are eventually ordered correctly
...

One final C operator is the comma ``,'', which most often finds use in the for statement
...
Thus in a for statement, it is possible to place multiple expressions in the
various parts, for example to process two indices in parallel
...

#include ...
, are not comma operators, and do
not guarantee left to right evaluation
...
The most suitable uses are for constructs strongly related to each
other, as in the for loop in reverse, and in macros where a multistep computation has to be a single
expression
...
Write a function expand(s1,s2) that expands shorthand notations like a-z in the string s1
into the equivalent complete list abc
...
Allow for letters of either case and digits, and be
prepared to handle cases like a-b-c and a-z0-9 and -a-z
...


3
...
By contrast,
the third loop in C, the do-while, tests at the bottom after making each pass through the loop body; the
body is always executed at least once
...
If it is true, statement is evaluated again, and so on
...
Except for the sense of the test, do-while is
equivalent to the Pascal repeat-until statement
...
Nonetheless, from time to time it
is valuable, as in the following function itoa, which converts a number to a character string (the inverse of
atoi)
...
We have chosen to generate the string backwards, then
reverse it
...
We also used braces around the single statement that makes up the body of the
do-while, even though they are unnecessary, so the hasty reader will not mistake the while part for the
beginning of a while loop
...
In a two's complement number representation, our version of itoa does not handle the largest
negative number, that is, the value of n equal to -(2wordsize-1)
...
Modify it to print that value
correctly, regardless of the machine on which it runs
...
Write the function itob(n,s,b) that converts the integer n into a base b character
representation in the string s
...

Exercise 3-6
...
The third argument is a
minimum field width; the converted number must be padded with blanks on the left if necessary to make it
wide enough
...
7 Break and Continue
It is sometimes convenient to be able to exit from a loop other than by testing at the top or bottom
...
A break causes
the innermost enclosing loop or switch to be exited immediately
...

/* trim: remove trailing blanks, tabs, newlines */
int trim(char s[])
{
int n;
for (n = strlen(s)-1; n >= 0; n--)

Chapter 3 - Control Flow

7

8

Chapter 3 - Control Flow
if (s[n] != ' ' && s[n] != '\t' && s[n] != '\n')
break;
s[n+1] = '\0';
return n;
}

strlen returns the length of the string
...
The loop is broken when one is found, or when n becomes
negative (that is, when the entire string has been scanned)
...

The continue statement is related to break, but less often used; it causes the next iteration of the
enclosing for, while, or do loop to begin
...
The continue statement applies only to
loops, not to switch
...

As an example, this fragment processes only the non-negative elements in the array a; negative values are
skipped
...
/* do positive elements */

The continue statement is often used when the part of the loop that follows is complicated, so that
reversing a test and indenting another level would nest the program too deeply
...
8 Goto and labels
C provides the infinitely-abusable goto statement, and labels to branch to
...
We have not used goto in
this book
...
The most common is to abandon
processing in some deeply nested structure, such as breaking out of two or more loops at once
...
Thus:
for (
...
) {

...

error:
/* clean up the mess */

This organization is handy if the error-handling code is non-trivial, and if errors can occur in several places
...
It can be attached to any statement
in the same function as the goto
...

As another example, consider the problem of determining whether two arrays a and b have an element in
common
...

found:
/* got one: a[i] == b[j] */

...
For example, the array search becomes
found = 0;
for (i = 0; i < n && !found; i++)
for (j = 0; j < m && !found; j++)
if (a[i] == b[j])
found = 1;
if (found)
/* got one: a[i-1] == b[j-1] */

...


With a few exceptions like those cited here, code that relies on goto statements is generally harder to
understand and to maintain than code without gotos
...

Back to Chapter 2 -- Index -- Chapter 4
Compiled by tmdcjsl(skidrow8123@hotmail
...
Appropriate functions hide details of operation from parts of the
program that don't need to know about them, thus clarifying the whole, and easing the pain of making
changes
...
A program may reside in one or more source files
...
We will not
go into that process here, however, since the details vary from system to system
...
As
we saw first in Chapter 1, it is now possible to declare the type of arguments when a function is declared
...
This makes it possible
for a compiler to detect many more errors than it could before
...

The standard clarifies the rules on the scope of names; in particular, it requires that there be only one
definition of each external object
...

The C preprocessor has also been enhanced
...


4
...
(This is a special case of the UNIX program grep
...
Three small pieces are better to deal with than one big
one, because irrelevant details can be buried in the functions, and the chance of unwanted interactions is
minimized
...


Chapter 4 - Functions and Program Structure

1

2

Chapter 4 - Functions and Program Structure

``While there's another line'' is getline, a function that we wrote in Chapter 1, and ``print it'' is printf,
which someone has already provided for us
...

We can solve that problem by writing a function strindex(s,t) that returns the position or index in the
string s where the string t begins, or -1 if s does not contain t
...
When we
later need more sophisticated pattern matching, we only have to replace strindex; the rest of the code can
remain the same
...
)
Given this much design, filling in the details of the program is straightforward
...
For now, the pattern to be searched for is a literal string, which is not
the most general of mechanisms
...
There is
also a slightly different version of getline; you might find it instructive to compare it to the one in Chapter
1
...
A do-nothing function like this is sometimes useful as a place holder
during program development
...

A program is just a set of definitions of variables and functions
...
The functions can occur in
any order in the source file, and the source program can be split into multiple files, so long as no function is
split
...
Any
expression can follow return:
return expression;

The expression will be converted to the return type of the function if necessary
...

The calling function is free to ignore the returned value
...
Control also returns to the caller with no value when
execution ``falls off the end'' of the function by reaching the closing right brace
...
In any case, if a
function fails to return a value, its ``value'' is certain to be garbage
...
This value is
available for use by the environment that called the program
The mechanics of how to compile and load a C program that resides on multiple source files vary from one
system to the next
...

Suppose that the three functions are stored in three files called main
...
c, and strindex
...

Then the command
cc main
...
c strindex
...
o, getline
...
o,
then loads them all into an executable file called a
...
If there is an error, say in main
...
c getline
...
o

The cc command uses the ``
...
o'' naming convention to distinguish source files from object files
...
Write the function strindex(s,t) which returns the position of the rightmost occurrence of
t in s, or -1 if there is none
...
2 Functions Returning Non-integers
So far our examples of functions have returned either no value (void) or an int
...
To illustrate how to deal with this, let us write and use the function
atof(s), which converts the string s to its double-precision floating-point equivalent
...
It handles an optional sign and decimal point, and
the presence or absence of either part or fractional part
...
The standard library includes an atof; the header
...

First, atof itself must declare the type of value it returns, since it is not int
...

The function atof must be declared and defined consistently
...
But if (as is more likely)
atof were compiled separately, the mismatch would not be detected, atof would return a double that
main would treat as an int, and meaningless answers would result
...

The reason a mismatch can happen is that if there is no function prototype, a function is implicitly declared by
its first appearance in an expression, such as
sum += atof(line)

If a name that has not been previously declared occurs in an expression and is followed by a left parentheses,
it is declared by context to be a function name, the function is assumed to return an int, and nothing is
assumed about its arguments
...
This special meaning of the empty argument list is intended to permit older C programs to
compile with new compilers
...
If the function takes
arguments, declare them; if it takes no arguments, use void
...
The value of the expression in
return expression;

is converted to the type of the function before the return is taken
...

This operation does potentionally discard information, however, so some compilers warn of it
...

Exercise 4-2
...
45e-6

where a floating-point number may be followed by e or E and an optionally signed exponent
...
3 External Variables
A C program consists of a set of external objects, which are either variables or functions
...
External variables are defined outside of any function, and are thus potentionally available to many
functions
...
By default, external variables and functions have the property that all references to them by
the same name, even from functions compiled separately, are references to the same thing
...
) In this sense, external variables are analogous to Fortran COMMON blocks or
variables in the outermost block in Pascal
...
Because external variables are globally accessible, they
provide an alternative to function arguments and return values for communicating data between functions
...

If a large number of variables must be shared among functions, external variables are more convenient and
efficient than long argument lists
...

External variables are also useful because of their greater scope and lifetime
...
External
variables, on the other hand, are permanent, so they can retain values from one function invocation to the next
...

Let us examine this issue with a larger example
...
Because it is easier to implement, the calculator will use reverse Polish notation
instead of infix
...
)
In reverse Polish notation, each operator follows its operands; an infix expression like
(1 - 2) * (4 + 5)

is entered as
1 2 - 4 5 + *

Chapter 4 - Functions and Program Structure

5

6

Chapter 4 - Functions and Program Structure

Parentheses are not needed; the notation is unambiguous as long as we know how many operands each
operator expects
...
Each operand is pushed onto a stack; when an operator arrives, the proper
number of operands (two for binary operators) is popped, the operator is applied to them, and the result is
pushed back onto the stack
...
Next, 4 and 5 are pushed and then replaced by their sum, 9
...
The value on the top of the stack is popped and printed when the end of the
input line is encountered
...
And there should be a separate function for fetching the next input operator or
operand
...
On possibility is to keep it in main, and pass the stack and the current stack position to the routines
that push and pop it
...
So we have decided to store the stack and its associated information in external
variables accessible to the push and pop functions but not to main
...
If for now we think of the program as existing in one source
file, it will look like this:
"temp0032
...
4">Section 3
...

#include ...
h>

/* for

#define MAXOP
#define NUMBER

/* max size of operand or operator */
/* signal that a number was found */

100
'0'

atof() */

int getop(char []);
void push(double);
double pop(void);
/* reverse Polish calculator */
main()
{
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {

6

Chapter 4 - Functions and Program Structure

Chapter 4 - Functions and Program Structure

7

case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0
...
8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}

Because + and * are commutative operators, the order in which the popped operands are combined is
irrelevant, but for - and / the left and right operand must be distinguished
...
To guarantee the right order, it is
necessary to pop the first value into a temporary variable as we did in main
...
0;
}
}

Chapter 4 - Functions and Program Structure

7

8

Chapter 4 - Functions and Program Structure

A variable is external if it is defined outside of any function
...
But main itself does not refer to the stack or
stack position - the representation can be hidden
...
The
task is easy
...
If the next character is not a digit or a hexadecimal point, return it
...

#include ...
')
return c;
/* not a number */
i = 0;
if (isdigit(c))
/* collect integer part */
while (isdigit(s[++i] = c = getch()))
;
if (c == '
...
One instance is collecting characters that make up a number: until the first
non-digit is seen, the number is not complete
...

The problem would be solved if it were possible to ``un-read'' the unwanted character
...
Fortunately, it's easy to simulate un-getting a character, by writing a pair of
cooperating functions
...

How they work together is simple
...
getch reads from the buffer if there is anything else, and calls getchar if the buffer is
empty
...

Since the buffer and the index are shared by getch and ungetch and must retain their values between calls,
they must be external to both routines
...
We have used an array for the pushback, rather than a single character, to illustrate a more
general approach
...
Given the basic framework, it's straightforward to extend the calculator
...

Exercise 4-4
...
Add a command to clear the stack
...
Add access to library functions like sin, exp, and pow
...
h> in Appendix B, Section
4
...
Add commands for handling variables
...
) Add a variable for the most recently printed value
...
Write a routine ungets(s) that will push back an entire string onto the input
...
Suppose that there will never be more than one character of pushback
...

Exercise 4-9
...
Decide what their
properties ought to be if an EOF is pushed back, then implement your design
...
An alternate organization uses getline to read an entire input line; this makes getch and
ungetch unnecessary
...


4
...
Among the questions of interest are
• How are declarations written so that variables are properly declared during compilation?
• How are declarations arranged so that all the pieces will be properly connected when the program is
loaded?
• How are declarations organized so there is only one copy?
• How are external variables initialized?
Let us discuss these topics by reorganizing the calculator program into several files
...

Chapter 4 - Functions and Program Structure

9

10

Chapter 4 - Functions and Program Structure

The scope of a name is the part of the program within which the name can be used
...
Local
variables of the same name in different functions are unrelated
...

The scope of an external variable or a function lasts from the point at which it is declared to the end of the file
being compiled
...
}
int sp = 0;
double val[MAXVAL];
void push(double f) {
...
}

then the variables sp and val may be used in push and pop simply by naming them; no further declarations
are needed
...

On the other hand, if an external variable is to be referred to before it is defined, or if it is defined in a
different source file from the one where it is being used, then an extern declaration is mandatory
...
A declaration
announces the properties of a variable (primarily its type); a definition also causes storage to be set aside
...
On the other hand, the lines
extern int sp;
extern double val[];

declare for the rest of the source file that sp is an int and that val is a double array (whose size is
determined elsewhere), but they do not create the variables or reserve storage for them
...
(There may also be extern declarations in the file
containing the definition
...

Initialization of an external variable goes only with the definition
...
Then these definitions and declarations
would be necessary to tie them together:
in file1:
extern int sp;
extern double val[];
void push(double f) {
...
}

in file2:
int sp = 0;
double val[MAXVAL];

Because the extern declarations in file1 lie ahead of and outside the function definitions, they apply to all
functions; one set of declarations suffices for all of file1
...


4
...
The main function would go in one file, which we will call main
...
c; getop goes into a third, getop
...
Finally,
getch and ungetch go into a fourth file, getch
...

There is one more thing to worry about - the definitions and declarations shared among files
...
Accordingly, we will place this common material in a header file, calc
...
(The #include line is described in Section 4
...
) The resulting program then looks like this:

Chapter 4 - Functions and Program Structure

11

12

Chapter 4 - Functions and Program Structure

There is a tradeoff between the desire that each file have access only to the information it needs for its job and
the practical reality that it is harder to maintain more header files
...
For a much larger program, more organization and more headers
would be needed
...
6 Static Variables
The variables sp and val in stack
...
c, are for the private use of the
functions in their respective source files, and are not meant to be accessed by anything else
...
External static thus provides a way to hide names like buf and bufp in the
getch-ungetch combination, which must be external so they can be shared, yet which should not be
visible to users of getch and ungetch
...
If the two routines and
the two variables are compiled in one file, as in
static char buf[BUFSIZE];
static int bufp = 0;

/* buffer for ungetch */
/* next free position in buf */

int getch(void) {
...
}

then no other routine will be able to access buf and bufp, and those names will not conflict with the same
names in other files of the same program
...

The external static declaration is most often used for variables, but it can be applied to functions as well
...
If a function is declared
static, however, its name is invisible outside of the file in which it is declared
...
Internal static variables are local to a
particular function just as automatic variables are, but unlike automatics, they remain in existence rather than
coming and going each time the function is activated
...

Exercise 4-11
...
Hint: use an internal static
variable
...
7 Register Variables
A register declaration advises the compiler that the variable in question will be heavily used
...
But compilers are free to ignore the advice
...
The register declaration can only be applied to automatic variables and to the formal
parameters of a function
...

}

Chapter 4 - Functions and Program Structure

13

14

Chapter 4 - Functions and Program Structure

In practice, there are restrictions on register variables, reflecting the realities of underlying hardware
...
Excess register
declarations are harmless, however, since the word register is ignored for excess or disallowed
declarations
...
The specific restrictions on number and
types of register variables vary from machine to machine
...
8 Block Structure
C is not a block-structured language in the sense of Pascal or similar languages, because functions may not be
defined within other functions
...
Declarations of variables (including initializations) may follow the left brace that introduces
any compound statement, not just the one that begins a function
...
For
example, in
if (n > 0) {
int i; /* declare a new i */
for (i = 0; i < n; i++)

...
An
automatic variable declared and initialized in a block is initialized each time the block is entered
...
Given the declarations
int x;
int y;
f(double x)
{
double y;
}

then within the function f, occurrences of x refer to the parameter, which is a double; outside f, they refer
to the external int
...

As a matter of style, it's best to avoid variable names that conceal names in an outer scope; the potential for
confusion and error is too great
...
9 Initialization
Initialization has been mentioned in passing many times so far, but always peripherally to some other topic
...

In the absence of explicit initialization, external and static variables are guaranteed to be initialized to zero;
automatic and register variables have undefined (i
...
, garbage) initial values
...
For automatic and register variables, the initializer is not
restricted to being a constant: it may be any expression involving previously defined values, even function
calls
...
3 could be written as
int binsearch(int x, int v[], int n)
{
int low = 0;
int high = n - 1;
int mid;

...
Which form to
prefer is largely a matter of taste
...

An array may be initialized by following its declaration with a list of initializers enclosed in braces and
separated by commas
...

If there are fewer initializers for an array than the specified size, the others will be zero for external, static and
automatic variables
...
There is no way to specify repetition of an
initializer, nor to initialize an element in the middle of an array without supplying all the preceding values as
well
...


4
...
Consider
printing a number as a character string
...


Chapter 4 - Functions and Program Structure

15

16

Chapter 4 - Functions and Program Structure

There are two solutions to this problem
...
6
...
Again, this version can
fail on the largest negative number
...
h>
/* printd: print n in decimal */
void printd(int n)
{
if (n < 0) {
putchar('-');
n = -n;
}
if (n / 10)
printd(n / 10);
putchar(n % 10 + '0');
}

When a function calls itself recursively, each invocation gets a fresh set of all the automatic variables,
independent of the previous set
...

It passes 12 to a second printd, which in turn passes 1 to a third
...
That printd prints 2, then returns to the first level
...

Another good example of recursion is quicksort, a sorting algorithm developed by C
...
R
...

Given an array, one element is chosen and the others partitioned in two subsets - those less than the partition
element and those greater than or equal to it
...

When a subset has fewer than two elements, it doesn't need any sorting; this stops the recursion
...
We use the middle element of
each subarray for partitioning
...
v[right] into increasing order */
void qsort(int v[], int left, int right)
{
int i, last;
void swap(int v[], int i, int j);
if (left >= right) /* do nothing if array contains */
return;
/* fewer than two elements */
swap(v, left, (left + right)/2); /* move partition elem */
last = left;
/* to v[0] */
for (i = left + 1; i <= right; i++) /* partition */
if (v[i] < v[left])
swap(v, ++last, i);
swap(v, left, last);
/* restore partition elem */
qsort(v, left, last-1);
qsort(v, last+1, right);
}

We moved the swapping operation into a separate function swap because it occurs three times in qsort
...

Recursion may provide no saving in storage, since somewhere a stack of the values being processed must be
maintained
...
But recursive code is more compact, and often much easier to write and
understand than the non-recursive equivalent
...
6
...
Adapt the ideas of printd to write a recursive version of itoa; that is, convert an integer
into a string by calling a recursive routine
...
Write a recursive version of the function reverse(s), which reverses the string s in place
...
11 The C Preprocessor
C provides certain language facilities by means of a preprocessor, which is conceptionally a separate first step
in compilation
...
Other features
described in this section include conditional compilation and macros with arguments
...
11
...
Any
source line of the form
#include "filename"

or
#include

is replaced by the contents of the file filename
...
An included file may itself contain #include lines
...
h>
...
)
#include is the preferred way to tie the declarations together for a large program
...
Naturally, when an included file is changed, all files that depend on it must be
recompiled
...
11
...
The name in a #define has the same form as a variable name; the
Chapter 4 - Functions and Program Structure

17

18

Chapter 4 - Functions and Program Structure

replacement text is arbitrary
...
The scope of a name
defined with #define is from its point of definition to the end of the source file being compiled
...
Substitutions are made only for tokens, and do not take place within
quoted strings
...

Any name may be defined with any replacement text
...

It is also possible to define macros with arguments, so the replacement text can be different for different calls
of the macro
...
Each occurrence of a formal
parameter (here A or B) will be replaced by the corresponding actual argument
...

If you examine the expansion of max, you will notice some pitfalls
...
For instance
max(i++, j++)

/* WRONG */

will increment the larger twice
...

Nonetheless, macros are valuable
...
h>, in which getchar
and putchar are often defined as macros to avoid the run-time overhead of a function call per character
processed
...
h> are also usually implemented as macros
...
}

Formal parameters are not replaced within quoted strings
...
This can be combined with string concatenation to make, for example, a debugging print
macro:
18

Chapter 4 - Functions and Program Structure

Chapter 4 - Functions and Program Structure
#define

dprint(expr)

19

printf(#expr " = %g\n", expr)

When this is invoked, as in
dprint(x/y)

the macro is expanded into
printf("x/y" " = &g\n", x/y);

and the strings are concatenated, so the effect is
printf("x/y = &g\n", x/y);

Within the actual argument, each " is replaced by \" and each \ by \\, so the result is a legal string constant
...
If a
parameter in the replacement text is adjacent to a ##, the parameter is replaced by the actual argument, the ##
and surrounding white space are removed, and the result is re-scanned
...

The rules for nested uses of ## are arcane; further details may be found in Appendix A
...
Define a macro swap(t,x,y) that interchanges two arguments of type t
...
)

4
...
3 Conditional Inclusion
It is possible to control preprocessing itself with conditional statements that are evaluated during
preprocessing
...

The "sysv
...
h" #elif SYSTEM == MSDOS
#define HDR "msdos
...
h" #endif #include HDR
The #ifdef and #ifndef lines are specialized forms that test whether a
name is defined
...
h go here */
#endif

Back to Chapter 3 -- Index -- Chapter 5
Compiled by tmdcjsl(skidrow8123@hotmail
...
Pointers are much used in C, partly because they
are sometimes the only way to express a computation, and partly because they usually lead to more compact
and efficient code than can be obtained in other ways
...

Pointers have been lumped with the goto statement as a marvelous way to create impossible-to-understand
programs
...
With discipline, however, pointers can also be used to achieve clarity and simplicity
...

The main change in ANSI C is to make explicit the rules about how pointers can be manipulated, in effect
mandating what good programmers already practice and good compilers already enforce
...


5
...
A typical machine has an array of
consecutively numbered or addressed memory cells that may be manipulated individually or in contiguous
groups
...
A pointer is a group of cells (often two or four) that can
hold an address
...
The & operator only applies to objects
in memory: variables and array elements
...

The unary operator * is the indirection or dereferencing operator; when applied to a pointer, it accesses the
object the pointer points to
...
This artificial
sequence shows how to declare a pointer and how to use & and *:
int x = 1, y = 2, z[10];
int *ip;
/* ip is a pointer to int */
ip = x
y = *ip;
*ip = 0;
ip = &z[0];

/* ip now points to x */
/* y is now 1 */
/* x is now 0 */
/* ip now points to z[0] */

The declaration of x, y, and z are what we've seen all along
...
The syntax of the declaration for a
variable mimics the syntax of expressions in which the variable might appear
...
For example,
double *dp, atof(char *);

says that in an expression *dp and atof(s) have values of double, and that the argument of atof is a
pointer to char
...
(There is one exception: a ``pointer to void'' is used to hold any type of
pointer but cannot be dereferenced itself
...
11
...

The unary operators * and & bind more tightly than arithmetic operators, so the assignment
y = *ip + 1

takes whatever ip points at, adds 1, and assigns the result to y, while
*ip += 1

increments what ip points to, as do
++*ip

and
(*ip)++

The parentheses are necessary in this last example; without them, the expression would increment ip instead
of what it points to, because unary operators like * and ++ associate right to left
...
For example, if iq is another
pointer to int,
iq = ip

copies the contents of ip into iq, thus making iq point to whatever ip pointed to
...
2 Pointers and Function Arguments
Since C passes arguments to functions by value, there is no direct way for the called function to alter a
variable in the calling function
...
It is not enough to write
swap(a, b);

where the swap function is defined as
void swap(int x, int y)

2

/* WRONG */

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

3

{
int temp;
temp = x;
x = y;
y = temp;
}

Because of call by value, swap can't affect the arguments a and b in the routine that called it
...

The way to obtain the desired effect is for the calling program to pass pointers to the values to be changed:
swap(&a, &b);

Since the operator & produces the address of a variable, &a is a pointer to a
...

void swap(int *px, int *py)
{
int temp;

/* interchange *px and *py */

temp = *px;
*px = *py;
*py = temp;
}

Pictorially:

Pointer arguments enable a function to access and change objects in the function that called it
...
getint has to return the value it found and also signal end of file
when there is no more input
...

One solution is to have getint return the end of file status as its function value, while using a pointer
argument to store the converted integer back in the calling function
...
4
...
Notice that it is essential to
pass the address of array[n] to getint
...

Our version of getint returns EOF for end of file, zero if the next input is not a number, and a positive
value if the input contains a valid number
...
h>
int getch(void);
void ungetch(int);
/* getint: get next integer from input into *pn */
int getint(int *pn)
{
int c, sign;
while (isspace(c = getch()))
/* skip white space */
;
if (!isdigit(c) && c != EOF && c != '+' && c != '-') {
ungetch(c); /* it is not a number */
return 0;
}
sign = (c == '-') ? -1 : 1;
if (c == '+' || c == '-')
c = getch();
for (*pn = 0; isdigit(c), c = getch())
*pn = 10 * *pn + (c - '0');
*pn *= sign;
if (c != EOF)
ungetch(c);
return c;
}

Throughout getint, *pn is used as an ordinary int variable
...
3) so the one extra character that must be read can be pushed back onto the input
...
As written, getint treats a + or - not followed by a digit as a valid representation of zero
...

Exercise 5-2
...
What type does getfloat return as
its function value?

4

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

5

5
...
Any operation that can be achieved by array subscripting can also be done with
pointers
...

The declaration
int a[10];

defines an array of size 10, that is, a block of 10 consecutive objects named a[0], a[1],
...


The notation a[i] refers to the i-th element of the array
...


Now the assignment
x = *pa;

will copy the contents of a[0] into x
...
Thus, if pa points to a[0],
*(pa+1)

refers to the contents of a[1], pa+i is the address of a[i], and *(pa+i) is the contents of a[i]
...
The meaning of ``adding 1
to a pointer,'' and by extension, all pointer arithmetic, is that pa+1 points to the next object, and pa+i points
to the i-th object beyond pa
...
By definition, the value of a
variable or expression of type array is the address of element zero of the array
...
Since the name of an array is a synonym for the location of the initial element,
the assignment pa=&a[0] can also be written as
pa = a;

Rather more surprising, at first sight, is the fact that a reference to a[i] can also be written as *(a+i)
...
Applying the operator
& to both parts of this equivalence, it follows that &a[i] and a+i are also identical: a+i is the address of the
i-th element beyond a
...
In short, an array-and-index expression is equivalent to one
written as a pointer and offset
...
A pointer is a
variable, so pa=a and pa++ are legal
...

When an array name is passed to a function, what is passed is the location of the initial element
...
We can use this fact to write another version of strlen, which computes the
length of a string
...
That means that calls like
strlen("hello, world");
strlen(array);

6

/* string constant */
/* char array[100]; */

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays
strlen(ptr);

7
/* char *ptr; */

all work
...
When an array
name is passed to a function, the function can at its convenience believe that it has been handed either an array
or a pointer, and manipulate it accordingly
...

It is possible to pass part of an array to a function, by passing a pointer to the beginning of the subarray
...
Within f, the parameter declaration
can read
f(int arr[]) {
...
}

So as far as f is concerned, the fact that the parameter refers to part of a larger array is of no consequence
...
Of course, it is illegal
to refer to objects that are not within the array bounds
...
4 Address Arithmetic
If p is a pointer to some element of an array, then p++ increments p to point to the next element, and p+=i
increments it to point i elements beyond where it currently does
...

C is consistent and regular in its approach to address arithmetic; its integration of pointers, arrays, and address
arithmetic is one of the strengths of the language
...

There are two routines
...
The second, afree(p), releases the storage thus
acquired so it can be re-used later
...
That is, the storage managed by alloc and afree is a
stack, or last-in, first-out
...
7 we will show how they can be implemented
...
This array is private to alloc and afree
...
In practical implementations, the array may well not even
have a name; it might instead be obtained by calling malloc or by asking the operating system for a pointer
to some unnamed block of storage
...
We use a pointer, called allocp,
that points to the next free element
...
If so, alloc returns the current value of allocp (i
...
, the beginning of the free
block), then increments it by n to point to the next free area
...

afree(p) merely sets allocp to p if p is inside allocbuf
...
The
declaration
static char *allocp = allocbuf;

defines allocp to be a character pointer and initializes it to point to the beginning of allocbuf, which is
the next free position when the program starts
...

The test
if (allocbuf + ALLOCSIZE - allocp >= n) {

/* it fits */

checks if there's enough room to satisfy a request for n characters
...
If the request can be satisfied, alloc returns a pointer
to the beginning of a block of characters (notice the declaration of the function itself)
...
C guarantees that zero is never a valid address for data, so a
return value of zero can be used to signal an abnormal event, in this case no space
...
Zero is the sole exception: the constant zero may be assigned to
a pointer, and a pointer may be compared with the constant zero
...
NULL is defined
in ...
We will use NULL henceforth
...
First, pointers may be compared under certain
circumstances
...
, work
properly
...
Any pointer can be meaningfully compared
for equality or inequality with zero
...
(There is one exception: the address of the first element past
the end of an array can be used in pointer arithmetic
...
The construction
p + n

means the address of the n-th object beyond the one p currently points to
...
If an int is four bytes, for example, the int will be scaled by four
...
This fact can be used to write yet another version of strlen:
/* strlen: return length of string s */
int strlen(char *s)
{
char *p = s;
while (*p != '\0')
p++;
return p - s;

Chapter 5 - Pointers and Arrays

9

10

Chapter 5 - Pointers and Arrays
}

In its declaration, p is initialized to s, that is, to point to the first character of the string
...
Because p points to characters, p++
advances p to the next character each time, and p-s gives the number of characters advanced over, that is, the
string length
...
The header
...
If we were being cautious, however, we would use size_t for the return value of strlen, to match
the standard library version
...

Pointer arithmetic is consistent: if we had been dealing with floats, which occupy more storage that chars,
and if p were a pointer to float, p++ would advance to the next float
...
All the pointer manipulations automatically take into account the size of the objects
pointed to
...
All other pointer arithmetic is illegal
...


5
...
In the internal representation, the array is terminated with the null character '\0' so
that programs can find the end
...

Perhaps the most common occurrence of string constants is as arguments to functions, as in
printf("hello, world\n");

When a character string like this appears in a program, access to it is through a character pointer; printf
receives a pointer to the beginning of the character array
...

String constants need not be function arguments
...
This is not a string copy; only pointers are involved
...

There is an important difference between these definitions:
char amessage[] = "now is the time"; /* an array */
char *pmessage = "now is the time"; /* a pointer */

10

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

11

amessage is an array, just big enough to hold the sequence of characters and '\0' that initializes it
...

On the other hand, pmessage is a pointer, initialized to point to a string constant; the pointer may
subsequently be modified to point elsewhere, but the result is undefined if you try to modify the string
contents
...
The first function is strcpy(s,t), which copies the string t to the string s
...
To copy the characters, we need a
loop
...
Here
they are conveniently initialized pointers, which are marched along the arrays a character at a time, until the
'\0' that terminates t has been copied into s
...
Experienced C programmers would prefer
/* strcpy: copy t to s; pointer version 2 */
void strcpy(char *s, char *t)
{
while ((*s++ = *t++) != '\0')
;
}

This moves the increment of s and t into the test part of the loop
...
In the same way, the character is stored into the old s position before s is incremented
...
The net effect is that characters
are copied from t to s, up and including the terminating '\0'
...
So the function would likely be written as
/* strcpy: copy t to s; pointer version 3 */
void strcpy(char *s, char *t)
{
while (*s++ = *t++)
;
}

Although this may seem cryptic at first sight, the notational convenience is considerable, and the idiom should
be mastered, because you will see it frequently in C programs
...
h>) returns the target string as its function value
...
The value
is obtained by subtracting the characters at the first position where s and t disagree
...
For example,
*--p

decrements p before fetching the character that p points to
...
3
...
h> contains declarations for the functions mentioned in this section, plus a variety of
other string-handling functions from the standard library
...
Write a pointer version of the function strcat that we showed in Chapter 2: strcat(s,t)
copies the string t to the end of s
...
Write the function strend(s,t), which returns 1 if the string t occurs at the end of the
string s, and zero otherwise
...
Write versions of the library functions strncpy, strncat, and strncmp, which operate on
at most the first n characters of their argument strings
...
Full descriptions are in Appendix B
...
Rewrite appropriate programs from earlier chapters and exercises with pointers instead of array
indexing
...


5
...
Let us
illustrate by writing a program that will sort a set of text lines into alphabetic order, a stripped-down version
of the UNIX program sort
...
The same algorithms will work, except that now we have to deal with lines of
text, which are of different lengths, and which, unlike integers, can't be compared or moved in a single
operation
...

This is where the array of pointers enters
...
The pointers themselves can bee stored
in an array
...
When two out-of-order lines
have to be exchanged, the pointers in the pointer array are exchanged, not the text lines themselves
...

The sorting process has three steps:
read all the lines of input
sort them
print them in order
As usual, it's best to divide the program into functions that match this natural division, with the main routine
controlling the other functions
...

The input routine has to collect and save the characters of each line, and build an array of pointers to the lines
...

Since the input function can only cope with a finite number of input lines, it can return some illegal count like
Chapter 5 - Pointers and Arrays

13

14

Chapter 5 - Pointers and Arrays

-1 if too much input is presented
...

"error: input too big to sort\n");
return 1;
}
}
#define MAXLEN 1000 /* max length of any input line */
int getline(char *, int);
char *alloc(int);
/* readlines: read input lines */
int readlines(char *lineptr[], int maxlines)
{
int len, nlines;
char *p, line[MAXLEN];
nlines = 0;
while ((len = getline(line, MAXLEN)) > 0)
if (nlines >= maxlines || p = alloc(len) == NULL)
return -1;
else {
line[len-1] = '\0'; /* delete newline */
strcpy(p, line);
lineptr[nlines++] = p;
}
return nlines;
}
/* writelines: write output lines */
void writelines(char *lineptr[], int nlines)
{
int i;
for (i = 0; i < nlines; i++)
printf("%s\n", lineptr[i]);
}

The function getline is from Section 1
...

The main new thing is the declaration for lineptr:
char *lineptr[MAXLINES]

says that lineptr is an array of MAXLINES elements, each element of which is a pointer to a char
...

Since lineptr is itself the name of an array, it can be treated as a pointer in the same manner as in our
earlier examples, and writelines can be written instead as
/* writelines: write output lines */
void writelines(char *lineptr[], int nlines)
{
while (nlines-- > 0)
printf("%s\n", *lineptr++);
}

Initially, *lineptr points to the first line; each element advances it to the next line pointer while nlines
14

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

15

is counted down
...
The quicksort from Chapter 4 needs minor
changes: the declarations have to be modified, and the comparison operation must be done by calling
strcmp
...

/* qsort: sort v[left]
...

Exercise 5-7
...
How much faster is the program?

5
...
In this section, we will show some of their properties
...
For
example, March 1 is the 60th day of a non-leap year, and the 61st day of a leap year
...
Since this latter function computes two
values, the month and day arguments will be pointers:
month_day(1988, 60, &m, &d)

sets m to 2 and d to 29 (February 29th)
...
Since the number of days per month differs for leap years and non-leap years, it's easier
Chapter 5 - Pointers and Arrays

15

16

Chapter 5 - Pointers and Arrays

to separate them into two rows of a two-dimensional array than to keep track of what happens to February
during computation
...

The array daytab has to be external to both day_of_year and month_day, so they can both use it
...

daytab is the first two-dimensional array we have dealt with
...
Hence subscripts are written as
daytab[i][j]

/* [row][col] */

rather than
daytab[i,j]

/* WRONG */

Other than this notational distinction, a two-dimensional array can be treated in much the same way as in
other languages
...

An array is initialized by a list of initializers in braces; each row of a two-dimensional array is initialized by a
corresponding sub-list
...
Since space is not at a premium here, this is clearer than adjusting
the indices
...
In this particular case, it is a pointer to objects that are
arrays of 13 ints
...
}

It could also be
f(int daytab[][13]) {
...
}

which says that the parameter is a pointer to an array of 13 integers
...
Without parentheses, the declaration
int *daytab[13]

is an array of 13 pointers to integers
...

Section 5
...

Exercise 5-8
...
Remedy this defect
...
8 Initialization of Pointer Arrays
Consider the problem of writing a function month_name(n), which returns a pointer to a character string
containing the name of the n-th month
...

month_name contains a private array of character strings, and returns a pointer to the proper one when
called
...

The syntax is similar to previous initializations:
/* month_name: return name of n-th month */
char *month_name(int n)
{
static char *name[] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return (n < 1 || n > 12) ? name[0] : name[n];
}

The declaration of name, which is an array of character pointers, is the same as lineptr in the sorting
example
...
The characters of the i-th string are placed somewhere, and a pointer to them is stored in name[i]
...


5
...
Multi-dimensional Arrays
Newcomers to C are sometimes confused about the difference between a two-dimensional array and an array
of pointers, such as name in the example above
...
But a is a true
two-dimensional array: 200 int-sized locations have been set aside, and the conventional rectangular
subscript calculation 20 * row +col is used to find the element a[row,col]
...
Assuming that each element of b does point to a twenty-element array, then there will be 200
ints set aside, plus ten cells for the pointers
...
That is, each element of b need not point to a twenty-element vector;
some may point to two elements, some to fifty, and some to none at all
...
Compare the
declaration and picture for an array of pointers:
char *name[] = { "Illegal month", "Jan", "Feb", "Mar" };

with those for a two-dimensional array:
char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };

Exercise 5-9
...


5
...
When main is called, it is called with two arguments
...
We customarily use multiple levels of pointers to manipulate these character strings
...
That is, the command
echo hello, world

prints the output
18

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

19

hello, world

By convention, argv[0] is the name by which the program was invoked, so argc is at least 1
...
In the example above, argc is 3, and
argv[0], argv[1], and argv[2] are "echo", "hello,", and "world" respectively
...


The first version of echo treats argv as an array of character pointers:
#include ...

This next variant is based on incrementing argv, which is a pointer to pointer to char, while argc is
counted down:
#include ...
Each successive increment moves it along to
the next argument; *argv is then the pointer to that argument
...

Alternatively, we could write the printf statement as
printf((argc > 1) ? "%s " : "%s", *++argv);

Chapter 5 - Pointers and Arrays

19

20

Chapter 5 - Pointers and Arrays

This shows that the format argument of printf can be an expression too
...
1
...
Following
the lead of the UNIX program grep, let us enhance the program so the pattern to be matched is specified by
the first argument on the command line
...
h>
#include ...
It is declared in ...

The model can now be elaborated to illustrate further pointer constructions
...
One says ``print all the lines except those that match the pattern;'' the second says
``precede each printed line by its line number
...
If we choose -x (for ``except'') to signal the inversion, and -n
(``number'') to request line numbering, then the command
find -x -npattern

will print each line that doesn't match the pattern, preceded by its line number
...
Furthermore, it is convenient for users if option arguments can be
combined, as in
find -nx pattern

Here is the program:
#include ...
h>
#define MAXLINE 1000
int getline(char *line, int max);
/* find: print lines that match pattern from 1st arg */

20

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

21

main(int argc, char *argv[])
{
char line[MAXLINE];
long lineno = 0;
int c, except = 0, number = 0, found = 0;
while (--argc > 0 && (*++argv)[0] == '-')
while (c = *++argv[0])
switch (c) {
case 'x':
except = 1;
break;
case 'n':
number = 1;
break;
default:
printf("find: illegal option %c\n", c);
argc = 0;
found = -1;
break;
}
if (argc != 1)
printf("Usage: find -x -n pattern\n");
else
while (getline(line, MAXLINE) > 0) {
lineno++;
if ((strstr(line, *argv) != NULL) != except) {
if (number)
printf("%ld:", lineno);
printf("%s", line);
found++;
}
}
return found;
}

argc is decremented and argv is incremented before each optional argument
...
Thus
argc should be 1 and *argv should point at the pattern
...
(An alternate valid form would be **++argv
...
In fact, that is what we have used in the inner loop, where the task is to walk along a
specific argument string
...

Exercise 5-10
...
For example,
expr 2 3 4 + *

evaluates 2 * (3+4)
...
Modify the program entab and detab (written as exercises in Chapter 1) to accept a list of
tab stops as arguments
...

Exercise 5-12
...
Choose convenient (for the user) default behavior
...
Write the program tail, which prints the last n lines of its input
...
The program should behave rationally no matter how unreasonable the input or the
value of n
...
6, not in a two-dimensional array of fixed size
...
11 Pointers to Functions
In C, a function itself is not a variable, but it is possible to define pointers to functions, which can be assigned,
placed in arrays, passed to functions, returned by functions, and so on
...

A sort often consists of three parts - a comparison that determines the ordering of any pair of objects, an
exchange that reverses their order, and a sorting algorithm that makes comparisons and exchanges until the
objects are in order
...
This is
the approach taken in our new sort
...
These functions are declared ahead of main and a pointer to the appropriate one is passed to
qsort
...

#include ...
h>
#define MAXLINES 5000
char *lineptr[MAXLINES];

/* max #lines to be sorted */
/* pointers to text lines */

int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
void qsort(void *lineptr[], int left, int right,
int (*comp)(void *, void *));
int numcmp(char *, char *);
/* sort input lines */
main(int argc, char *argv[])
{
int nlines;
/* number of input lines read */
int numeric = 0;
/* 1 if numeric sort */
if (argc > 1 && strcmp(argv[1], "-n") == 0)
numeric = 1;
if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
qsort((void**) lineptr, 0, nlines-1,
(int (*)(void*,void*))(numeric ? numcmp : strcmp));
writelines(lineptr, nlines);
return 0;
} else {
printf("input too big to sort\n");
return 1;
}
}

22

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

23

In the call to qsort, strcmp and numcmp are addresses of functions
...

We have written qsort so it can process any data type, not just character strings
...
The generic pointer type void * is used for the pointer arguments
...
The elaborate cast of the function argument casts the arguments of the comparison function
...

/* qsort: sort v[left]
...
The fourth parameter of qsort is
int (*comp)(void *, void *)

which says that comp is a pointer to a function that has two void * arguments and returns an int
...
The parentheses are needed so the components are correctly associated; without them,
int *comp(void *, void *)

/* WRONG */

says that comp is a function returning a pointer to an int, which is very different
...
Here is numcmp, which compares two strings
on a leading numeric value, computed by calling atof:
#include ...

void swap(void *v[],
{
void *temp;

int i, int j;)

temp = v[i];
v[i] = v[j];
v[j] = temp;
}

A variety of other options can be added to the sorting program; some make challenging exercises
...
Modify the sort program to handle a -r flag, which indicates sorting in reverse (decreasing)
order
...

Exercise 5-15
...

Exercise 5-16
...
Make sure it works in conjunction with -f
...
Add a field-searching capability, so sorting may bee done on fields within lines, each field
sorted according to an independent set of options
...
)

5
...
The syntax is an attempt to make the declaration and the use agree; it works well for simple cases,
but it can be confusing for the harder ones, because declarations cannot be read left to right, and because
parentheses are over-used
...

Although truly complicated declarations rarely arise in practice, it is important to know how to understand
them, and, if necessary, how to create them
...
7
...
The word description reads left to
24

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

25

right
...
It converts a C declaration into a word description, as in these examples:
char **argv
argv: pointer to char
int (*daytab)[13]
daytab: pointer to array[13] of int
int *daytab[13]
daytab: array[13] of pointer to int
void *comp()
comp: function returning pointer to void
void (*comp)()
comp: pointer to function returning void
char (*(*x())[])()
x: function returning pointer to array[] of
pointer to function returning char
char (*(*x[3])())[5]
x: array[3] of pointer to function returning
pointer to array[5] of char

dcl is based on the grammar that specifies a declarator, which is spelled out precisely in Appendix A,
Section 8
...
A direct-dcl is a name, or a parenthesized dcl, or a
direct-dcl followed by parentheses, or a direct-dcl followed by brackets with an optional size
...
For instance, consider this declarator:
(*pfa[])()

pfa will be identified as a name and thus as a direct-dcl
...
Then *pfa[] is
recognized as a dcl, so (*pfa[]) is a direct-dcl
...
We can
also illustrate the parse with a tree like this (where direct-dcl has been abbreviated to dir-dcl):

Chapter 5 - Pointers and Arrays

25

26

Chapter 5 - Pointers and Arrays

The heart of the dcl program is a pair of functions, dcl and dirdcl, that parse a declaration according to
this grammar
...

/* dcl: parse a declarator */
void dcl(void)
{
int ns;
for (ns = 0; gettoken() == '*'; ) /* count *'s */
ns++;
dirdcl();
while (ns-- > 0)
strcat(out, " pointer to");
}
/* dirdcl: parse a direct declarator */
void dirdcl(void)
{
int type;
if (tokentype == '(') {
/* ( dcl ) */
dcl();
if (tokentype != ')')
printf("error: missing )\n");
} else if (tokentype == NAME) /* variable name */
strcpy(name, token);
else
printf("error: expected name or (dcl)\n");

26

Chapter 5 - Pointers and Arrays

Chapter 5 - Pointers and Arrays

27

while ((type=gettoken()) == PARENS || type == BRACKETS)
if (type == PARENS)
strcat(out, " function returning");
else {
strcat(out, " array");
strcat(out, token);
strcat(out, " of");
}
}

Since the programs are intended to be illustrative, not bullet-proof, there are significant restrictions on dcl
...
It does not handle argument types in functions, or
qualifiers like const
...
It doesn't do much error recovery, so invalid declarations
will also confuse it
...

Here are the global variables and the main routine:
#include ...
h>
#include ...
*/

main() /* convert declaration to words */
{
while (gettoken() != EOF) {
/* 1st token on line */
strcpy(datatype, token); /* is the datatype */
out[0] = '\0';
dcl();
/* parse rest of line */
if (tokentype != '\n')
printf("syntax error\n");
printf("%s: %s %s\n", name, out, datatype);
}
return 0;
}

The function gettoken skips blanks and tabs, then finds the next token in the input; a ``token'' is a name, a
pair of parentheses, a pair of brackets perhaps including a number, or any other single character
...

Going in the other direction is easier, especially if we do not worry about generating redundant parentheses
...
undcl also uses the same external
variables as dcl does
...
Make dcl recover from input errors
...
Modify undcl so that it does not add redundant parentheses to declarations
...
Expand dcl to handle declarations with function argument types, qualifiers like const, and
so on
...
com)

Back to Chapter 5 -- Index -- Chapter 7

Chapter 5 - Pointers and Arrays

29

Chapter 6 - Structures
A structure is a collection of one or more variables, possibly of different types, grouped together under a
single name for convenient handling
...
)
Structures help to organize complicated data, particularly in large programs, because they permit a group of
related variables to be treated as a unit instead of as separate entities
...
Some of these in turn could be structures: a name
has several components, as does an address and even a salary
...

The main change made by the ANSI standard is to define structure assignment - structures may be copied and
assigned to, passed to functions, and returned by functions
...
Automatic structures and arrays may now also be
initialized
...
1 Basics of Structures
Let us create a few structures suitable for graphics
...


The two components can be placed in a structure declared like this:
struct point {
int x;
int y;
};

The keyword struct introduces a structure declaration, which is a list of declarations enclosed in braces
...
The tag names this
kind of structure, and can be used subsequently as a shorthand for the part of the declaration in braces
...
A structure member or tag and an ordinary (i
...
,
non-member) variable can have the same name without conflict, since they can always be distinguished by
context
...

A struct declaration defines a type
...
That is,
struct {
...

A structure declaration that is not followed by a list of variables reserves no storage; it merely describes a
template or shape of a structure
...
For example, given the declaration of point above,
struct point pt;

defines a variable pt which is a structure of type struct point
...

A member of a particular structure is referred to in an expression by a construction of the form
structure-name
...
'' connects the structure name and the member name
...
x, pt
...
x * pt
...
y * pt
...
One representation of a rectangle is a pair of points that denote the diagonally
opposite corners:

struct rect {
struct point pt1;
struct point pt2;
};

The rect structure contains two point structures
...
pt1
...


6
...
Copy and assignment include passing arguments to functions and returning values
from functions as well
...
A structure may be initialized by a list of constant
member values; an automatic structure may also be initialized by an assignment
...
There are at least
three possible approaches: pass components separately, pass an entire structure, or pass a pointer to it
...

The first function, makepoint, will take two integers and return a point structure:
/* makepoint: make a point from x and y components */
struct point makepoint(int x, int y)
{
struct point temp;
temp
...
y = y;
return temp;
}

Notice that there is no conflict between the argument name and the member with the same name; indeed the
re-use of the names stresses the relationship
...
pt1 = makepoint(0,0);
screen
...
pt1
...
pt2
...
pt1
...
pt2
...
For instance,
/* addpoints: add two points */
struct addpoint(struct point p1, struct point p2)
{
p1
...
x;
p1
...
y;
return p1;
}

Here both the arguments and the return value are structures
...

As another example, the function ptinrect tests whether a point is inside a rectangle, where we have
adopted the convention that a rectangle includes its left and bottom sides but not its top and right sides:
/* ptinrect: return 1 if
int ptinrect(struct point
{
return p
...
pt1
...
y >= r
...
y
}

p in r, 0 if not */
p, struct rect r)
&& p
...
pt2
...
y < r
...
y;

This assumes that the rectangle is presented in a standard form where the pt1 coordinates are less than the
pt2 coordinates
...
x, (*pp)
...
x because the precedence of the structure member operator
...
The expression *pp
...
x), which is illegal here because x is not a pointer
...
If p is a
pointer to a structure, then
p->member-of-structure

refers to the particular member
...
and -> associate from left to right, so if we have
struct rect r, *rp = r

then these four expressions are equivalent:
r
...
x
rp->pt1
...
pt1)
...
x

The structure operators
...
For example, given the declaration
struct {
int len;
char *str;
} *p;

then
++p->len

increments len, not p, because the implied parenthesization is ++(p->len)
...

(This last set of parentheses is unnecessary
...


6
...
We need an array of character
strings to hold the names, and an array of integers for the counts
...
Each
keyword is a pair:
char *word;
int cout;

and there is an array of pairs
...
Each element of the array is a structure
...
The structure initialization is analogous to earlier ones - the
definition is followed by a list of initializers enclosed in braces:
struct key {
char *word;
int count;
} keytab[] = {
"auto", 0,
"break", 0,
"case", 0,
"char", 0,
"const", 0,
"continue", 0,
"default", 0,
/*
...
It would be more precise to enclose
the initializers for each "row" or structure in braces, as in
{ "auto", 0 },

Chapter 6 - Structures

5

6

Chapter 6 - Structures
{ "break", 0 },
{ "case", 0 },

...
As usual, the number of entries in the array keytab will be computed if the initializers are
present and the [] is left empty
...
The main routine reads the input by
repeatedly calling a function getword that fetches one word at a time
...
The list of keywords must be sorted in
increasing order in the table
...
count, keytab[n]
...
tab[n-1] */
int binsearch(char *word, struct key tab[], int n)
{
int cond;
int low, high, mid;
low = 0;
high = n - 1;
while (low <= high) {
mid = (low+high) / 2;
if ((cond = strcmp(word, tab[mid]
...

The quantity NKEYS is the number of keywords in keytab
...
One possibility would be to
terminate the list of initializers with a null pointer, then loop along keytab until the end is found
...
The size
of the array is the size of one entry times the number of entries, so the number of entries is just
size of keytab / size of struct key
C provides a compile-time unary operator called sizeof that can be used to compute the size of any object
...
(Strictly, sizeof produces an
unsigned integer value whose type, size_t, is defined in the header ...
) An object can be a
variable or array or structure
...

In our case, the number of keywords is the size of the array divided by the size of one element
...

A sizeof can not be used in a #if line, because the preprocessor does not parse type names
...

Now for the function getword
...
getword fetches the next ``word'' from the input, where a word is either a
string of letters and digits beginning with a letter, or a single non-white space character
...

/* getword: get next word or character from input */
int getword(char *word, int lim)
{
int c, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c = getch()))
;
if (c != EOF)
*w++ = c;
if (!isalpha(c)) {
*w = '\0';
return c;
}
for ( ; --lim > 0; w++)
if (!isalnum(*w = getch())) {
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}

getword uses the getch and ungetch that we wrote in Chapter 4
...
The call to ungetch pushes that
character back on the input for the next call
...
h>
...
Our version of getword does not properly handle underscores, string constants, comments, or
preprocessor control lines
...


Chapter 6 - Structures

7

8

Chapter 6 - Structures

6
...

The external declaration of keytab need not change, but main and binsearch do need modification
...
tab[n-1] */
struct key *binsearch(char *word, struck key *tab, int n)
{
int cond;
struct key *low = &tab[0];
struct key *high = &tab[n];
struct key *mid;
while (low < high) {
mid = low + (high-low) / 2;
if ((cond = strcmp(word, mid->word)) < 0)
high = mid;
else if (cond > 0)
low = mid + 1;
else
return mid;
}
return NULL;
}

There are several things worthy of note here
...
If binsearch finds the word, it returns a pointer to it; if it fails, it returns NULL
...
This requires significant changes in
binsearch
...

The computation of the middle element can no longer be simply
mid = (low+high) / 2

/* WRONG */

because the addition of pointers is illegal
...

The most important change is to adjust the algorithm to make sure that it does not generate an illegal pointer
or attempt to access an element outside the array
...
The former is strictly illegal, and it is illegal to dereference the latter
...

In main we wrote
8

Chapter 6 - Structures

Chapter 6 - Structures

9

for (p = keytab; p < keytab + NKEYS; p++)

If p is a pointer to a structure, arithmetic on p takes into account the size of the structure, so p++ increments
p by the correct amount to get the next element of the array of structures, and the test stops the loop at the
right time
...
Because of
alignment requirements for different objects, there may be unnamed ``holes'' in a structure
...
The sizeof operator returns the proper value
...
Accordingly an alternate style is
sometimes used:
struct key *
binsearch(char *word, struct key *tab, int n)

This is a matter of personal taste; pick the form you like and hold to it
...
5 Self-referential Structures
Suppose we want to handle the more general problem of counting the occurrences of all the words in some
input
...
Yet
we can't do a linear search for each word as it arrives, to see if it's already been seen; the program would take
too long
...
)
How can we organize the data to copy efficiently with a list or arbitrary words?
One solution is to keep the set of words seen so far sorted at all times, by placing each word into its proper
position in the order as it arrives
...
Instead we will use a data structure called a binary tree
...

No node may have more than two children; it might have only zero or one
...
This is the tree for
the sentence ``now is the time for all good men to come to the aid of their party'', as built by inserting each
word as it is encountered:

Chapter 6 - Structures

9

10

Chapter 6 - Structures

To find out whether a new word is already in the tree, start at the root and compare the new word to the word
stored at that node
...
If the new record is less than the tree
word, continue searching at the left child, otherwise at the right child
...

This process is recursive, since the search from any node uses a search from one of its children
...

Going back to the description of a node, it is most conveniently represented as a structure with four
components:
struct tnode {
/* the
char *word;
int count;
struct tnode *left;
struct tnode *right;
};

tree node: */
/* points to the text */
/* number of occurrences */
/* left child */
/* right child */

This recursive declaration of a node might look chancy, but it's correct
...

Occasionally, one needs a variation of self-referential structures: two structures that refer to each other
...

struct s *p;
};
struct s {

...
The main routine reads words with getword and installs them in the tree with
addtree
...
h>
#include ...
h>
#define MAXWORD 100

10

Chapter 6 - Structures

Chapter 6 - Structures

11

struct tnode *addtree(struct tnode *, char *);
void treeprint(struct tnode *);
int getword(char *, int);
/* word frequency count */
main()
{
struct tnode *root;
char word[MAXWORD];
root = NULL;
while (getword(word, MAXWORD) != EOF)
if (isalpha(word[0]))
root = addtree(root, word);
treeprint(root);
return 0;
}

The function addtree is recursive
...
At
each stage, that word is compared to the word already stored at the node, and is percolated down to either the
left or right subtree by a recursive call to adtree
...
If a new node is created, addtree returns a pointer to it, which is installed
in the parent node
...
(We will discuss these
routines in a moment
...
This part of the code is
executed only at the leaves of the tree, when a new node is being added
...

treeprint prints the tree in sorted order; at each node, it prints the left subtree (all the words less than this
word), then the word itself, then the right subtree (all the words greater)
...

/* treeprint: in-order print of tree p */
void treeprint(struct tnode *p)
{
if (p != NULL) {
treeprint(p->left);

Chapter 6 - Structures

11

12

Chapter 6 - Structures
printf("%4d %s\n", p->count, p->word);
treeprint(p->right);
}
}

A practical note: if the tree becomes ``unbalanced'' because the words don't arrive in random order, the
running time of the program can grow too much
...
There are generalizations of the binary tree that do not
suffer from this worst-case behavior, but we will not describe them here
...

Clearly it's desirable that there be only one storage allocator in a program, even though it allocates different
kinds of objects
...
First, how does it meet the requirement of most real machines that
objects of certain types must satisfy alignment restrictions (for example, integers often must be located at even
addresses)? Second, what declarations can cope with the fact that an allocator must necessarily return different
kinds of pointers?
Alignment requirements can generally be satisfied easily, at the cost of some wasted space, by ensuring that
the allocator always returns a pointer that meets all alignment restrictions
...
In
Chapter 8 we will show one way to implement malloc
...
In C, the proper method is to declare that malloc returns a pointer to void, then
explicitly coerce the pointer into the desired type with a cast
...
h>
...
html">Chapters 8 and
7
...
Write a program that reads a C program and prints in alphabetical order each group of variable
names that are identical in the first 6 characters, but different somewhere thereafter
...
Make 6 a parameter that can be set from the command line
...
Write a cross-referencer that prints a list of all words in a document, and for each word, a list of
the line numbers on which it occurs
...

Exercise 6-4
...
Precede each word by its count
...
6 Table Lookup
In this section we will write the innards of a table-lookup package, to illustrate more aspects of structures
...
For example, consider the "center">

12

Chapter 6 - Structures

Chapter 6 - Structures

13

A block in the list is a structure containing pointers to the name, the replacement text, and the next block in
the list
...

struct nlist {
/* table entry: */
struct nlist *next;
/* next entry in chain */
char *name;
/* defined name */
char *defn;
/* replacement text */
};

The pointer array is just
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE];

/* pointer table */

The hashing function, which is used by both lookup and install, adds each
character value in the string to a scrambled combination of the previous
ones and returns the remainder modulo the array size
...

/* hash: form hash value for string s */
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}

Unsigned arithmetic ensures that the hash value is non-negative
...
The search is performed by lookup
...

/* lookup: look for s in hashtab */
struct nlist *lookup(char *s)
{
struct nlist *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np;
/* found */
return NULL;
/* not found */
}

Chapter 6 - Structures

13

14

Chapter 6 - Structures

The for loop in lookup is the standard idiom for walking along a linked
list:
for (ptr = head; ptr != NULL; ptr = ptr->next)

...

Otherwise, a new entry is created
...

struct nlist *lookup(char *);
char *strdup(char *);
/* install: put (name, defn) in hashtab */
struct nlist *install(char *name, char *defn)
{
struct nlist *np;
unsigned hashval;
if ((np = lookup(name)) == NULL) { /* not found */
np = (struct nlist *) malloc(sizeof(*np));
if (np == NULL || (np->name = strdup(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];
hashtab[hashval] = np;
} else
/* already there */
free((void *) np->defn);
/*free previous defn */
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}

Exercise 6-5
...

Exercise 6-6
...
e
...
You may also find getch and ungetch helpful
...
7 Typedef
C provides a facility called typedef for creating new data type names
...
The type Length can be used in
declarations, casts, etc
...
Syntactically,
typedef is like the storage classes extern, static, etc
...

As a more complicated example, we could make typedefs for the tree nodes shown earlier in this chapter:
typedef struct tnode *Treeptr;
typedef struct tnode { /*
char *word;
int count;
struct tnode *left;
struct tnode *right;
} Treenode;

the tree node: */
/* points to the text */
/* number of occurrences */
/* left child */
/* right child */

This creates two new type keywords called Treenode (a structure) and
Treeptr (a pointer to the structure)
...
Nor
are there any new semantics: variables declared this way have exactly the
same properties as variables whose declarations are spelled out
explicitly
...
For example,
typedef int (*PFI)(char *, char *);

creates the type PFI, for ``pointer to function (of two char * arguments)
returning int,'' which can be used in contexts like
PFI strcmp, numcmp;

in the sort program of Chapter 5
...
The first is to parameterize a
program against portability problems
...
One common situation is to use typedef
names for various integer quantities, then make an appropriate set of choices of short, int, and long for
each host machine
...

The second purpose of typedefs is to provide better documentation for a program - a type called Treeptr
may be easier to understand than one declared only as a pointer to a complicated structure
...
8 Unions
A union is a variable that may hold (at different times) objects of
different types and sizes, with the compiler keeping track of size and
alignment requirements
...
They are analogous to
variant records in pascal
...
The value of a particular constant must be stored in a variable of the
proper type, yet it is most convenient for table management if the value occupies the same amount of storage
and is stored in the same place regardless of its type
...
The syntax is based on structures:
union u_tag {
int ival;
float fval;
char *sval;
} u;

The variable u will be large enough to hold the largest of the three
types; the specific size is implementation-dependent
...

It is the programmer's responsibility to keep track of which type is
currently stored in a union; the results are implementation-dependent if
something is stored as one type and extracted as another
...
member
or
union-pointer->member
just as for structures
...
ival);
if (utype == FLOAT)
printf("%f\n", u
...
sval);
else
printf("bad type %d in utype\n", utype);

Unions may occur within structures and arrays, and vice versa
...
For example, in the structure
array defined by
struct {
char *name;
int flags;

16

Chapter 6 - Structures

Chapter 6 - Structures

17

int utype;
union {
int ival;
float fval;
char *sval;
} u;
} symtab[NSYM];

the member ival is referred to as
symtab[i]
...
ival

and the first character of the string sval by either of
*symtab[i]
...
sval
symtab[i]
...
sval[0]

In effect, a union is a structure in which all members have offset zero
from the base, the structure is big enough to hold the ``widest'' member,
and the alignment is appropriate for all of the types in the union
...

A union may only be initialized with a value of the type of its first member; thus union u described above can
only be initialized with an integer value
...


6
...
Externally-imposed
interfaces to hardware devices, also often require
pieces of a word
...
Each identifier in a program has certain
information associated with it, for example, whether or not it is a keyword, whether or not it is external and/or
static, and so on
...

The usual way this is done is to define a set of ``masks'' corresponding to the relevant bit positions, as in
"temp0019
...


Certain idioms appear frequently:
flags |= EXTERNAL | STATIC;

turns on the EXTERNAL and STATIC bits in flags, while
flags &= ~(EXTERNAL | STATIC);

turns them off, and
Chapter 6 - Structures

17

18

Chapter 6 - Structures
if ((flags & (EXTERNAL | STATIC)) == 0)
...

Although these idioms are readily mastered, as an alternative C offers the capability of defining and accessing
fields within a word directly rather than by bitwise logical operators
...
'' For example, the
symbol table #defines above could be replaced by the definition of three fields:
struct {
unsigned int is_keyword : 1;
unsigned int is_extern : 1;
unsigned int is_static : 1;
} flags;

This defines a variable table called flags that contains three 1-bit
fields
...
The fields are declared unsigned int to ensure that they are
unsigned quantities
...
is_keyword,
flags
...
Fields behave like small integers, and may participate in arithmetic expressions
just like other integers
...
is_extern = flags
...
is_extern = flags
...
is_extern == 0 && flags
...


to test them
...
Whether a field may overlap a word boundary is
implementation-defined
...
The special width 0 may be used to force alignment at the next word boundary
...
This means that although fields
are useful for maintaining internally-defined data structures, the question of which end comes first has to be
carefully considered when picking apart externally-defined data; programs that depend on such things are not
portable
...

They are not arrays and they do not have addresses, so the & operator cannot be applied on them
...
com)

Back to Chapter 6 -- Index -- Chapter 8

18

Chapter 6 - Structures

Chapter 7 - Input and Output
Input and output are not part of the C language itself, so we have not emphasized them in our presentation
thus far
...
In this chapter we will describe the standard library, a set of functions that provide
input and output, string handling, storage management, mathematical routines, and a variety of other services
for C programs
...
Programs that confine their system interactions to facilities provided by the standard
library can be moved from one system to another without change
...
h>, ...
h>
...
The library is described in detail in Appendix
B
...
1 Standard Input and Output
As we said in Chapter 1, the library implements a simple model of text input and output
...
If the system doesn't operate that way,
the library does whatever necessary to make it appear as if it does
...

The simplest input mechanism is to read one character at a time from the standard input, normally the
keyboard, with getchar:
int getchar(void)

getchar returns the next input character each time it is called, or EOF when it encounters end of file
...
h>
...

In many environments, a file may be substituted for the keyboard by using the < convention for input
redirection: if a program prog uses getchar, then the command line
prog
causes prog to read characters from infile instead
...
Input switching is also invisible if the input comes from another program
via a pipe mechanism: on some systems, the command line
otherprog | prog

runs the two programs otherprog and prog, and pipes the standard output of otherprog into the
standard input for prog
...
putchar returns the character written, or EOF is an error occurs
...
If pipes are supported,
prog | anotherprog

puts the standard output of prog into the standard input of anotherprog
...
Calls to putchar and printf may
be interleaved - output happens in the order in which the calls are made
...
html#s8
...
5
...
h> functions are implemented on a given machine,
programs that use them are shielded from knowledge of the character set
...
Write a program that converts upper case to lower or lower case to upper, depending on the
name it is invoked with, as found in argv[0]
...
2 Formatted Output - printf
The output function printf translates internal values to characters
...
The description here covers most typical uses but is not complete; for the full story, see
Appendix B
...
);

printf converts, formats, and prints its arguments on the standard output under control of the format
...

The format string contains two types of objects: ordinary characters, which are copied to the output stream,
and conversion specifications, each of which causes conversion and printing of the next successive argument
to printf
...
Between
the % and the conversion character there may be, in order:
• A minus sign, which specifies left adjustment of the converted argument
...
The converted argument will be printed in a field at
least this wide
...

• A period, which separates the field width from the precision
...

• An h if the integer is to be printed as a short, or l (letter ell) if as a long
...
1
...

Table 7
...
,15
...

f
double; [-]m
...

double; [-]m
...
ddddddE+/-xx, where the number of d's is given by
e,E
the precision (default 6)
...
Trailing zeros and a trailing decimal point are not printed
...

%
no argument is converted; print a %
A width or precision may be specified as *, in which case the value is computed by converting the next
argument (which must be an int)
...
*s", max, s);

Most of the format conversions have been illustrated in earlier chapters
...
The following table shows the effect of a variety of specifications in printing ``hello, world''
(12 characters)
...

:%s:
:%10s:
:%
...
15s:
:%-15s:
:%15
...
10s:

:hello, world:
:hello, world:
:hello, wor:
:hello, world:
:hello, world:
:hello, world
:
:
hello, wor:
:hello, wor
:

A warning: printf uses its first argument to decide how many arguments follow and what their type is
...
You should also be aware of the difference between these two calls:
printf(s);
printf("%s", s);

/* FAILS if s contains % */
/* SAFE */

The function sprintf does the same conversions as printf does, but stores the output in a string:
int sprintf(char *string, char *format, arg1, arg2,
...
, according to format as before, but places the result
in string instead of the standard output; string must be big enough to receive the result
...
Write a program that will print arbitrary input in a sensible way
...


7
...
Since we are mainly interested in the
argument processing, minprintf will process the format string and arguments but will call the real
Chapter 7 - Input and Output

3

4

Chapter 7 - Input and Output

printf to do the format conversions
...
)

where the declaration
...
The declaration

...
Our minprintf is declared as
void minprintf(char *fmt,
...

The tricky bit is how minprintf walks along the argument list when the list doesn't even have a name
...
h> contains a set of macro definitions that define how to step through an
argument list
...

The type va_list is used to declare a variable that will refer to each argument in turn; in minprintf, this
variable is called ap, for ``argument pointer
...
It must be called once before ap is used
...

Each call of va_arg returns one argument and steps ap to the next; va_arg uses a type name to determine
what type to return and how big a step to take
...
It must
be called before the program returns
...
Revise minprintf to handle more of the other facilities of printf
...
4 Formatted Input - Scanf
The function scanf is the input analog of printf, providing many of the same conversion facilities in the
opposite direction
...
)

scanf reads characters from the standard input, interprets them according to the specification in format,
4

Chapter 7 - Input and Output

Chapter 7 - Input and Output

5

and stores the results through the remaining arguments
...
As with printf, this section is a summary of the most useful features, not an exhaustive list
...
It
returns as its value the number of successfully matched and assigned input items
...
On the end of file, EOF is returned; note that this is different from 0, which
means that the next input character does not match the first specification in the format string
...

There is also a function sscanf that reads from a string instead of the standard input:
int sscanf(char *string, char *format, arg1, arg2,
...
These arguments must be pointers
...

The format string may contain:
• Blanks or tabs, which are not ignored
...

• Conversion specifications, consisting of the character %, an optional assignment suppression character
*, an optional number specifying a maximum field width, an optional h, l or L indicating the width
of the target, and a conversion character
...
Normally the result is places in the
variable pointed to by the corresponding argument
...
An input field is defined as a string of non-white
space characters; it extends either to the next white space character or until the field width, is specified, is
exhausted
...
(White space characters are blank, tab, newline, carriage return, vertical tab, and formfeed
...
The corresponding argument must be a
pointer, as required by the call-by-value semantics of C
...
2
...
2: Basic Scanf Conversions
Character
d
i
o
u
x
c
s
e,f,g
%

Input Data; Argument type
decimal integer; int *
integer; int *
...

octal integer (with or without leading zero); int *
unsigned decimal integer; unsigned int *
hexadecimal integer (with or without leading 0x or 0X); int *
characters; char *
...
The
normal skip-over white space is suppressed; to read the next non-white space character, use %1s
character string (not quoted); char *, pointing to an array of characters long enough for the
string and a terminating '\0' that will be added
...


Chapter 7 - Input and Output

5

6

Chapter 7 - Input and Output

The conversion characters d, i, o, u, and x may be preceded by h to indicate that a pointer to short rather
than int appears in the argument list, or by l (letter ell) to indicate that a pointer to long appears in the
argument list
...
2f\n", sum += v);
return 0;
}

Suppose we want to read input lines that contain dates of the form
25 Dec 1988

The scanf statement is
int day, year;
char monthname[20];
scanf("%d %s %d", &day, monthname, &year);

No & is used with monthname, since an array name is a pointer
...

So we could read dates of the form mm/dd/yy with the scanf statement:
int day, month, year;
scanf("%d/%d/%d", &month, &day, &year);

scanf ignores blanks and tabs in its format string
...
) as it looks for input values
...
For example, suppose we want to read lines that might contain a
date in either of the forms above
...
The next call to any input function will begin
by reading the first character not read by scanf
...
By far the most common error is
writing
scanf("%d", n);

instead of
scanf("%d", &n);

6

Chapter 7 - Input and Output

Chapter 7 - Input and Output

7

This error is not generally detected at compile time
...
Write a private version of scanf analogous to minprintf from the previous section
...
Rewrite the postfix calculator of Chapter 4 to use scanf and/or sscanf to do the input and
number conversion
...
5 File Access
The examples so far have all read the standard input and written the standard output, which are automatically
defined for a program by the local operating system
...
One
program that illustrates the need for such operations is cat, which concatenates a set of named files into the
standard output
...
For example, the command
cat x
...
c

prints the contents of the files x
...
c (and nothing else) on the standard output
...

The rules are simple
...

fopen takes an external name like x
...
c, does some housekeeping and negotiation with the operating
system (details of which needn't concern us), and returns a pointer to be used in subsequent reads or writes of
the file
...
Users don't need to know the details, because the definitions
obtained from ...
The only declaration needed for a
file pointer is exemplified by
FILE *fp;
FILE *fopen(char *name, char *mode);

This says that fp is a pointer to a FILE, and fopen returns a pointer to a FILE
...
(Details of how fopen can be
implemented on the UNIX system are given in Section 8
...
)
The call to fopen in a program is
fp = fopen(name, mode);

The first argument of fopen is a character string containing the name of the file
...
Allowable modes include read
("r"), write ("w"), and append ("a")
...

If a file that does not exist is opened for writing or appending, it is created if possible
...
Trying to
read a file that does not exist is an error, and there may be other causes of error as well, like trying to read a
file when you don't have permission
...
(The error can be
Chapter 7 - Input and Output

7

8

Chapter 7 - Input and Output

identified more precisely; see the discussion of error-handling functions at the end of Section 1 in Appendix
B
...
getc returns the next character from a
file; it needs the file pointer to tell it which file
...

putc is an output function:
int putc(int c, FILE *fp)

putc writes the character c to the file fp and returns the character written, or EOF if an error occurs
...

When a C program is started, the operating system environment is responsible for opening three files and
providing pointers for them
...
h>
...
1
...
These are identical
to scanf and printf, except that the first argument is a file pointer that specifies the file to be read or
written; the format string is the second argument
...
)
int fprintf(FILE *fp, char *format,
...
The design is one that has been found convenient for many programs
...
If there are no arguments, the standard
input is processed
...
h>
/* cat:
main(int
{
FILE
void

concatenate files, version 1 */
argc, char *argv[])
*fp;
filecopy(FILE *, FILE *)

if (argc == 1) /* no args; copy standard input */
filecopy(stdin, stdout);
else
while(--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
printf("cat: can't open %s\n, *argv);
return 1;
} else {
filecopy(fp, stdout);
fclose(fp);
}

8

Chapter 7 - Input and Output

Chapter 7 - Input and Output

9

return 0;
}
/* filecopy: copy file ifp to file ofp */
void filecopy(FILE *ifp, FILE *ofp)
{
int c;
while ((c = getc(ifp)) != EOF)
putc(c, ofp);
}

The file pointers stdin and stdout are objects of type FILE *
...

The function
int fclose(FILE *fp)

is the inverse of fopen, it breaks the connection between the file pointer and the external name that was
established by fopen, freeing the file pointer for another file
...
There is also another reason for fclose on an output file
- it flushes the buffer in which putc is collecting output
...
(You can close stdin and stdout if they are not needed
...
)

7
...
The trouble is that if one of the files can't be accessed for some
reason, the diagnostic is printed at the end of the concatenated output
...

To handle this situation better, a second output stream, called stderr, is assigned to a program in the same
way that stdin and stdout are
...

Let us revise cat to write its error messages on the standard error
...
h>
/* cat:
main(int
{
FILE
void
char

concatenate files, version 2 */
argc, char *argv[])
*fp;
filecopy(FILE *, FILE *);
*prog = argv[0]; /* program name for errors */

if (argc == 1 ) /* no args; copy standard input */
filecopy(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: can't open %s\n",
prog, *argv);
exit(1);
} else {
filecopy(fp, stdout);
fclose(fp);
}

Chapter 7 - Input and Output

9

10

Chapter 7 - Input and Output
if (ferror(stdout)) {
fprintf(stderr, "%s: error writing stdout\n", prog);
exit(2);
}
exit(0);
}

The program signals errors in two ways
...
We included the
program name, from argv[0], in the message, so if this program is used with others, the source of an error
is identified
...
The argument of exit is available to whatever process called this one, so the success or failure of the
program can be tested by another program that uses this one as a sub-process
...
exit calls fclose for each
open output file, to flush out any buffered output
...
exit has the advantage that it can be called from
other functions, and that calls to it can be found with a pattern-searching program like those in Chapter 5
...

int ferror(FILE *fp)

Although output errors are rare, they do occur (for example, if a disk fills up), so a production program should
check this as well
...

int feof(FILE *fp)

We have generally not worried about exit status in our small illustrative programs, but any serious program
should take care to return sensible, useful status values
...
7 Line Input and Output
The standard library provides an input and output routine fgets that is similar to the getline function that
we have used in earlier chapters:
char *fgets(char *line, int maxline, FILE *fp)

fgets reads the next input line (including the newline) from file fp into the character array line; at most
maxline-1 characters will be read
...
Normally fgets returns
line; on end of file or error it returns NULL
...
)
For output, the function fputs writes a string (which need not contain a newline) to a file:
int fputs(char *line, FILE *fp)

It returns EOF if an error occurs, and non-negative otherwise
...
Confusingly, gets deletes the terminating '\n', and puts adds it
...

It is easy to implement our getline from fgets:
/* getline: read a line, return length */
int getline(char *line, int max)
{
if (fgets(line, max, stdin) == NULL)
return 0;
else
return strlen(line);
}

Exercise 7-6
...

Exercise 7-7
...
Should the file name be printed when a matching
line is found?
Exercise 7-8
...


7
...
This section is a brief synopsis of the most useful
...


7
...
1 String Operations
We have already mentioned the string functions strlen, strcpy, strcat, and strcmp, found in
...
In the following, s and t are char *'s, and c and n are ints
...
8
...
h> perform character tests and conversions
...
The function returns int
...
8
...

int ungetc(int c, FILE *fp)

pushes the character c back onto file fp, and returns either c, or EOF for an error
...
ungetc may be used with any of the input functions like scanf, getc, or
getchar
...
8
...
The contents of s depend strongly on the local operating system
...
system returns
a system-dependent integer status from the command executed
...


7
...
5 Storage Management
The functions malloc and calloc obtain blocks of memory dynamically
...

void *calloc(size_t n, size_t size)

returns a pointer to enough free space for an array of n objects of the specified size, or NULL if the request
cannot be satisfied
...

The pointer returned by malloc or calloc has the proper alignment for the object in question, but it must
be cast into the appropriate type, as in
int *ip;
ip = (int *) calloc(n, sizeof(int));

free(p) frees the space pointed to by p, where p was originally obtained by a call to malloc or calloc
...

It is also an error to use something after it has been freed
...
7 shows the implementation of a storage allocator like malloc, in which allocated blocks may be
freed in any order
...
8
...
h>; here are some of the more
frequently used
...

sin(x)
cos(x)
atan2(y,x)
exp(x)
log(x)
log10(x)
pow(x,y)
sqrt(x)
fabs(x)

sine of x, x in radians
cosine of x, x in radians
arctangent of y/x, in radians
exponential function ex
natural (base e) logarithm of x (x>0)
common (base 10) logarithm of x (x>0)
xy
square root of x (x>0)
absolute value of x

7
...
7 Random Number generation
The function rand() computes a sequence of pseudo-random integers in the range zero to RAND_MAX,
which is defined in ...
One way to produce random floating-point numbers greater than or equal
to zero but less than one is

Chapter 7 - Input and Output

13

14

Chapter 7 - Input and Output
#define frand() ((double) rand() / (RAND_MAX+1
...
)
The function srand(unsigned) sets the seed for rand
...
7
...
Functions like isupper can be implemented to save space or to save time
...

Back to Chapter 6 -- Index -- Chapter 8
Compiled by tmdcjsl(skidrow8123@hotmail
...
This chapter describes how to use some of
the most important system calls from C programs
...
Even if you use C on a different operating system, however, you should be able to glean insight
into C programming from studying these examples; although details vary, similar code will be found on any
system
...

This chapter is divided into three major parts: input/output, file system, and storage allocation
...

Chapter 7 was concerned with an input/output interface that is uniform across operating systems
...
In the next few sections we will describe the UNIX system calls for input and output, and
show how parts of the standard library can be implemented with them
...
1 File Descriptors
In the UNIX operating system, all input and output is done by reading or writing files, because all peripheral
devices, even keyboard and screen, are files in the file system
...

In the most general case, before you read and write a file, you must inform the system of your intent to do so,
a process called opening the file
...
The system checks your right to do so (Does the file exist? Do you have
permission to access it?) and if all is well, returns to the program a small non-negative integer called a file
descriptor
...
(A file descriptor is analogous to the file pointer used by the standard library, or to the file
handle of MS-DOS
...

Since input and output involving keyboard and screen is so common, special arrangements exist to make this
convenient
...
If a program
reads 0 and writes 1 and 2, it can do input and output without worrying about opening files
...

Normally file descriptor 2 remains attached to the screen, so error messages can go there
...
In all cases, the file assignments are changed by the shell, not
by the program
...


8
...
For both, the first argument is a file descriptor
...
The third argument is the number
is the number of bytes to be transferred
...
On reading, the number of bytes returned may be
less than the number requested
...
For writing, the return value is the number of bytes written; an error has occurred if this isn't equal
to the number requested
...
The most common values are 1, which means one
character at a time (``unbuffered''), and a number like 1024 or 4096 that corresponds to a physical block size
on a peripheral device
...

Putting these facts together, we can write a simple program to copy its input to its output, the equivalent of the
file copying program written for Chapter 1
...

"syscalls
...
h so we can include
it in the programs of this chapter
...

The parameter BUFSIZ is also defined in syscalls
...
If the
file size is not a multiple of BUFSIZ, some read will return a smaller number of bytes to be written by
write; the next call to read after that will return zero
...
For example, here is a version of getchar that does unbuffered input, by reading the
standard input one character at a time
...
h"
/* getchar: unbuffered single character input */
int getchar(void)
{
char c;
return (read(0, &c, 1) == 1) ? (unsigned char) c : EOF;
}

c must be a char, because read needs a character pointer
...

The second version of getchar does input in big chunks, and hands out the characters one at a time
...
h"

2

Chapter 8 - The UNIX System Interface

Chapter 8 - The UNIX System Interface

3

/* getchar: simple buffered version */
int getchar(void)
{
static char buf[BUFSIZ];
static char *bufp = buf;
static int n = 0;
if (n == 0) { /* buffer is empty */
n = read(0, buf, sizeof buf);
bufp = buf;
}
return (--n >= 0) ? (unsigned char) *bufp++ : EOF;
}

If these versions of getchar were to be compiled with ...


8
...
There are two system calls for this, open and creat [sic]
...
open returns -1 if any error occurs
...
h> on System V UNIX systems, and in ...

To open an existing file for reading,
fd = open(name, O_RDONLY,0);

The perms argument is always zero for the uses of open that we will discuss
...
The system call creat is provided to create new files, or
to re-write old ones
...
If the file already exists, creat will
truncate it to zero length, thereby discarding its previous contents; it is not an error to creat a file that
already exists
...
In
the UNIX file system, there are nine bits of permission information associated with a file that control read,
write and execute access for the owner of the file, for the owner's group, and for all others
...
For example, 0775 specifies read, write and
execute permission for the owner, and read and execute permission for the group and everyone else
...
Our
version copies only one file, it does not permit the second argument to be a directory, and it invents
permissions instead of copying them
...
h>
#include ...
h"
#define PERMS 0666
void error(char *,

/* RW for owner, group, others */


...
With the stat system call, described in
Section 8
...

Notice that the function error is called with variable argument lists much like printf
...
The standard library
function vprintf is like printf except that the variable argument list is replaced by a single argument that
has been initialized by calling the va_start macro
...

#include ...
h>
/* error: print an error message and die */
void error(char *fmt,
...
Accordingly,
any program that intends to process many files must be prepared to re-use file descriptors
...
Termination of a program via exit or return from the main program closes all open files
...
It corresponds to the
standard library function remove
...
Rewrite the program cat from Chapter 7 using read, write, open, and close instead of
their standard library equivalents
...


8
...
When necessary, however, a file can be read or written in any arbitrary order
...
Subsequent reading or writing will begin at that position
...
For example, to append to a file (the redirection >> in the UNIX shell, or "a" for fopen),
seek to the end before writing:
lseek(fd, 0L, 2);

To get back to the beginning (``rewind''),
lseek(fd, 0L, 0);

Notice the 0L argument; it could also be written as (long) 0 or just as 0 if lseek is properly declared
...
For example,
the following function reads any number of bytes from any arbitrary place in a file
...

"syscalls
...
The
standard library function fseek is similar to lseek except that the first argument is a FILE * and the
return is non-zero if an error occurred
...
5 Example - An implementation of Fopen and Getc
Let us illustrate how some of these pieces fit together by showing an implementation of the standard library
routines fopen and getc
...
A file pointer
is a pointer to a structure that contains several pieces of information about the file: a pointer to a buffer, so the
file can be read in large chunks; a count of the number of characters left in the buffer; a pointer to the next
character position in the buffer; the file descriptor; and flags describing read/write mode, error status, etc
...
h>, which must be included (by #include)
in any source file that uses routines from the standard input/output library
...
In the following excerpt from a typical ...
This convention is used by all standard library routines
...
(Recall that
a long #define is continued with a backslash
...
The characters
are returned unsigned, which ensures that all characters will be positive
...
We have also included
macros for accessing the error and end-of-file status and the file descriptor
...
Most of fopen is concerned with getting the file opened and
positioned at the right place, and setting the flag bits to indicate the proper state
...

#include ...
h"

6

Chapter 8 - The UNIX System Interface

Chapter 8 - The UNIX System Interface
#define PERMS 0666

7

/* RW for owner, group, others */

FILE *fopen(char *name, char *mode)
{
int fd;
FILE *fp;
if (*mode != 'r' && *mode != 'w' && *mode != 'a')
return NULL;
for (fp = _iob; fp < _iob + OPEN_MAX; fp++)
if ((fp->flag & (_READ | _WRITE)) == 0)
break;
/* found free slot */
if (fp >= _iob + OPEN_MAX)
/* no free slots */
return NULL;
if (*mode == 'w')
fd = creat(name, PERMS);
else if (*mode == 'a') {
if ((fd = open(name, O_WRONLY, 0)) == -1)
fd = creat(name, PERMS);
lseek(fd, 0L, 2);
} else
fd = open(name, O_RDONLY, 0);
if (fd == -1)
/* couldn't access name */
return NULL;
fp->fd = fd;
fp->cnt = 0;
fp->base = NULL;
fp->flag = (*mode == 'r') ? _READ : _WRITE;
return fp;
}

This version of fopen does not handle all of the access mode possibilities of the standard, though adding
them would not take much code
...

The first call to getc for a particular file finds a count of zero, which forces a call of _fillbuf
...
Otherwise, it tries to
allocate a buffer (if reading is to be buffered)
...
Subsequent calls to _fillbuf will find a buffer allocated
...
h"
/* _fillbuf: allocate and fill input buffer */
int _fillbuf(FILE *fp)
{
int bufsize;
if ((fp->flag&(_READ|_EOF_ERR)) != _READ)
return EOF;
bufsize = (fp->flag & _UNBUF) ? 1 : BUFSIZ;
if (fp->base == NULL)
/* no buffer yet */
if ((fp->base = (char *) malloc(bufsize)) == NULL)
return EOF;
/* can't get buffer */
fp->ptr = fp->base;
fp->cnt = read(fp->fd, fp->ptr, bufsize);
if (--fp->cnt < 0) {
if (fp->cnt == -1)
fp->flag |= _EOF;
else
fp->flag |= _ERR;

Chapter 8 - The UNIX System Interface

7

8

Chapter 8 - The UNIX System Interface
fp->cnt = 0;
return EOF;
}
return (unsigned char) *fp->ptr++;
}

The only remaining loose end is how everything gets started
...

Exercise 8-2
...
Compare code
size and execution speed
...
Design and write _flushbuf, fflush, and fclose
...
The standard library function
int fseek(FILE *fp, long offset, int origin)

is identical to lseek except that fp is a file pointer instead of a file descriptor and return value is an int
status, not a position
...
Make sure that your fseek coordinates properly with the buffering done
for the other functions of the library
...
6 Example - Listing Directories
A different kind of file system interaction is sometimes called for - determining information about a file, not
what it contains
...
The
MS-DOS dir command is analogous
...
But is is necessary to use a
system call to access other information about a file, such as its size
...
What we want is provide access to
the information in a relatively system-independent way, even though the implementation may be highly
system-dependent
...
fsize is a special form of ls that prints
the sizes of all files named in its commandline argument list
...
If there are no arguments at all, it processes the current directory
...
A directory is a file that contains a list of
filenames and some indication of where they are located
...
'' The inode for a file is where all information about the file except its name is kept
...

Regrettably, the format and precise contents of a directory are not the same on all versions of the system
...
The outer level defines a
structure called a Dirent and three routines opendir, readdir, and closedir to provide
8

Chapter 8 - The UNIX System Interface

Chapter 8 - The UNIX System Interface

9

system-independent access to the name and inode number in a directory entry
...
Then we will show how to implement these on systems that use the same directory structure as
Version 7 and System V UNIX; variants are left as exercises
...
The maximum length of a filename
component is NAME_MAX, which is a system-dependent value
...
This information is collected
into a file called dirent
...

#define NAME_MAX

14

/* longest filename component; */
/* system-dependent */

typedef struct {
/* portable directory entry */
long ino;
/* inode number */
char name[NAME_MAX+1];
/* name + '\0' terminator */
} Dirent;
typedef struct {
int fd;
Dirent d;
} DIR;

/* minimal DIR: no buffering, etc
...
That is,
char *name;
struct stat stbuf;
int stat(char *, struct stat *);
stat(name, &stbuf);

fills the structure stbuf with the inode information for the file name
...
h>, and typically looks like this:
struct stat
{
dev_t
ino_t
short
short
short
short
dev_t
off_t
time_t
time_t
time_t
};

/* inode information returned by stat */
st_dev;
st_ino;
st_mode;
st_nlink;
st_uid;
st_gid;
st_rdev;
st_size;
st_atime;
st_mtime;
st_ctime;

/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*

device of inode */
inode number */
mode bits */
number of links to file */
owners user id */
owners group id */
for special files */
file size in characters */
time last accessed */
time last modified */
time originally created */

Most of these values are explained by the comment fields
...
h>, which must be included too
...
The flag definitions are also included in
...
*/

0020000
0060000
0010000

/* character special */
/* block special */
/* regular */

Now we are ready to write the program fsize
...
If the name is a directory, however, then we
have to process that directory one file at a time; it may in turn contain sub-directories, so the process is
recursive
...

#include
#include
#include
#include
#include
#include
#include

...
h>
"syscalls
...
h>
...
h>
"dirent
...
");
else
while (--argc > 0)
fsize(*++argv);
return 0;
}

The function fsize prints the size of the file
...
Note how the flag names S_IFMT and S_IFDIR are used to decide if the file is a
directory
...

int stat(char *, struct stat *);
void dirwalk(char *, void (*fcn)(char *));
/* fsize: print the name of file "name" */
void fsize(char *name)
{
struct stat stbuf;
if (stat(name, &stbuf) == -1) {
fprintf(stderr, "fsize: can't access %s\n", name);
return;
}
if ((stbuf
...
st_size, name);
}

The function dirwalk is a general routine that applies a function to each file in a directory
...
Since
fsize calls dirwalk on each directory, the two functions call each other recursively
...
") == 0
|| strcmp(dp->name, "
...

Each directory always contains entries for itself, called "
...
"; these must be skipped, or
the program will loop forever
...
The next step is to present
minimal versions of opendir, readdir, and closedir for a specific system
...
h>,
which looks like this:
#ifndef DIRSIZ
#define DIRSIZ 14
#endif
struct direct {
/* directory entry */
ino_t d_ino;
/* inode number */
char d_name[DIRSIZ]; /* long name does not have '\0' */
};

Some versions of the system permit much longer names and have a more complicated directory structure
...
It happens to be unsigned
short on the systems we use regularly, but this is not the sort of information to embed in a program; it might
be different on a different system, so the typedef is better
...
h>
...
st_mode & S_IFMT) != S_IFDIR
|| (dp = (DIR *) malloc(sizeof(DIR))) == NULL)
return NULL;
dp->fd = fd;
return dp;
}

closedir closes the directory file and frees the space:
/* closedir: close directory opened by opendir */
void closedir(DIR *dp)
{
if (dp) {
close(dp->fd);
free(dp);
}
}

Finally, readdir uses read to read each directory entry
...
Otherwise, the inode number
and name are placed in a static structure and a pointer to that is returned to the user
...

#include ...
d_ino == 0) /* slot not in use */
continue;
d
...
d_ino;
strncpy(d
...
d_name, DIRSIZ);
d
...
First, many
programs are not ``system programs''; they merely use information that is maintained by the operating system
...
The second
observation is that with care it is possible to create an interface to system-dependent objects that is itself
relatively system-independent
...

Exercise 8-5
...


8
...
The version that we will now write
is unrestricted
...
These routines illustrate some of the considerations involved in writing
machine-dependent code in a relatively machine-independent way, and also show a real-life application of
12

Chapter 8 - The UNIX System Interface

Chapter 8 - The UNIX System Interface

13

structures, unions and typedef
...
Since other activities in the program may also request space without calling this allocator,
the space that malloc manages may not be contiguous
...

Each block contains a size, a pointer to the next block, and the space itself
...


When a request is made, the free list is scanned until a big-enough block is found
...
If the
block is exactly the size requested it is unlinked from the list and returned to the user
...
If no
big-enough block is found, another large chunk is obtained by the operating system and linked into the free
list
...
If the block
being freed is adjacent to a free block on either side, it is coalesced with it into a single bigger block, so
storage does not become too fragmented
...

One problem, which we alluded to in Chapter 5, is to ensure that the storage returned by malloc is aligned
properly for the objects that will be stored in it
...

On some machines, the most restrictive type is a double; on others, int or long suffices
...
'' To simplify alignment, all
blocks are multiples of the header size, and the header is aligned properly
...

In malloc, the requested size in characters is rounded up to the proper number of header-sized units; the
block that will be allocated contains one more unit, for the header itself, and this is the value recorded in the
size field of the header
...

The user can do anything with the space requested, but if anything is written outside of the allocated space the
list is likely to be scrambled
...

The variable base is used to get started
...
In any case, the free list is
then searched
...
If a too-big block is found, the tail end is returned to
the user; in this way the header of the original needs only to have its size adjusted
...

static Header base;
/* empty list to get started */
static Header *freep = NULL;
/* start of free list */
/* malloc: general-purpose storage allocator */
void *malloc(unsigned nbytes)
{
Header *p, *prevp;
Header *moreroce(unsigned);
unsigned nunits;
nunits = (nbytes+sizeof(Header)-1)/sizeof(header) + 1;
if ((prevp = freep) == NULL) {
/* no free list yet */
base
...
ptr = freeptr = prevptr = &base;
base
...
size = 0;
}
for (p = prevp->s
...
ptr) {
if (p->s
...
size == nunits) /* exactly */
prevp->s
...
ptr;
else {
/* allocate tail end */
p->s
...
size;

14

Chapter 8 - The UNIX System Interface

Chapter 8 - The UNIX System Interface

15

p->s
...
The details of how it does this vary from
system to system
...
we don't want
to do that on every call to malloc, so morecore requests al least NALLOC units; this larger block will be
chopped up as needed
...

The UNIX system call sbrk(n) returns a pointer to n more bytes of storage
...
The -1 must be cast to char * so it can be
compared with the return value
...
There is still one assumption, however, that pointers to different blocks
returned by sbrk can be meaningfully compared
...
Thus this version of malloc is portable only among machines for
which general pointer comparison is meaningful
...
com)

Back to Chapter 8 -- Index -- Appendix B

Chapter 8 - The UNIX System Interface

15

Appendix A - Reference Manual
A
...
159-1989
...

For the most part, this document follows the broad outline of the standard, which in turn follows that of the
first edition of this book, although the organization differs in detail
...

Throughout this manual, commentary material is indented and written in smaller type, as this is
...


A
...
It is translated in several phases, which are
described in Par
...
12
...
When the preprocessing
of Par
...
12 is complete, the program has been reduced to a sequence of tokens
...
2
...

Blanks, horizontal and vertical tabs, newlines, formfeeds and comments as described below (collectively,
``white space'') are ignored except as they separate tokens
...

If the input stream has been separated into tokens up to a given character, the next token is the longest string
of characters that could constitute a token
...
2
...
Comments do not nest, and
they do not occur within a string or character literals
...
2
...
The first character must be a letter; the underscore _ counts as
a letter
...
Identifiers may have any length, and for internal identifiers,
at least the first 31 characters are significant; some implementations may take more characters significant
...
A
...
2)
...


A
...
4 Keywords
The following identifiers are reserved for the use as keywords, and may not be used otherwise:

Appendix A - Reference Manual

1

2

Appendix A - Reference Manual
auto
break
case
char
const
continue
default
do

double
else
enum
extern
float
for
goto
if

int
long
register
return
short
signed
sizeof
static

struct
switch
typedef
union
unsigned
void
volatile
while

Some implementations also reserve the words fortran and asm
...


A
...
5 Constants
There are several kinds of constants
...
A
...
2 discusses the basic types:
constant:
integer-constant
character-constant
floating-constant
enumeration-constant
A
...
5
...
Octal constants do not contain the digits 8 or 9
...
The hexadecimal digits include a or A through f or F
with values 10 through 15
...
It may also be suffixed
by the letter l or L to specify that it is long
...
(See Par
...
4 for a discussion of types)
...
If it is unsuffixed, octal or hexadecimal, it has the first possible of these
types: int, unsigned int, long int, unsigned long int
...
If it is suffixed by l or L, then long int, unsigned long
int
...

The elaboration of the types of integer constants goes considerably beyond the first edition, which merely caused large
integer constants to be long
...


A
...
5
...
The value of
a character constant with only one character is the numeric value of the character in the machine's character
set at execution time
...

Character constants do not contain the ' character or newlines; in order to represent them, and certain other
characters, the following escape sequences may be used:

2

newline

NL (LF)

\n

horizontal tab

HT

\t

backslash \
question
?
mark

\\
\?
Appendix A - Reference Manual

Appendix A - Reference Manual

3

vertical tab

VT

\v

backspace

BS

\b

carriage return CR

\r

formfeed

\f

FF

single
quote
double
quote
octal
number
hex
number

'

\'

"

\"

ooo \ooo
hh

\xhh

audible alert
BEL
\a
The escape \ooo consists of the backslash followed by 1, 2, or 3 octal digits, which are taken to specify the
value of the desired character
...
The escape \xhh consists of the backslash, followed by x, followed by
hexadecimal digits, which are taken to specify the value of the desired character
...
For either octal or hexadecimal escape characters, if the implementation treats the char type as
signed, the value is sign-extended as if cast to char type
...

In some implementations, there is an extended set of characters that cannot be represented in the char type
...
Such a constant has type wchar_t, an integral type defined in the standard header ...

As with ordinary character constants, hexadecimal escapes may be used; the effect is undefined if the
specified value exceeds that representable with wchar_t
...
Extended characters are
also new
...


A
...
5
...
The integer and fraction parts both consist
of a sequence of digits
...
The type is determined by the suffix; F or
f makes it float, L or l makes it long double, otherwise it is double
...
5
...
A
...
4) are constants of type int
...
2
...
A string has type ``array of characters'' and storage class static (see Par
...
3 below) and is
initialized with the given characters
...

Adjacent string literals are concatenated into a single string
...
String literals do not contain
newline or double-quote characters; in order to represent them, the same escape sequences as for character
constants are available
...
Wide-character string literals have type ``array of wchar_t
...

The specification that string literals need not be distinct, and the prohibition against modifying them, are new in the ANSI
standard, as is the concatenation of adjacent string literals
...


A
...
Alternative categories are usually listed on separate lines; in a few
cases, a long set of narrow alternatives is presented on one line, marked by the phrase ``one of
...
The syntax is summarized in Par
...
13
...


A
...
An object, sometimes
called a variable, is a location in storage, and its interpretation depends on two main attributes: its storage
class and its type
...
A name also has a scope, which
is the region of the program in which it is known, and a linkage, which determines whether the same name in
another scope refers to the same object or function
...
A
...


A
...
1 Storage Class
There are two storage classes: automatic and static
...
Automatic objects are local to a block (Par
...
3), and are discarded on exit
from the block
...
Objects declared register are automatic, and are (if possible)
stored in fast registers of the machine
...
Within a block, including a block that provides the code for a
function, static objects are declared with the keyword static
...
They may be made local to a particular translation unit by
use of the static keyword; this gives them internal linkage
...


A
...
2 Basic Types
There are several fundamental types
...
h> described in Appendix B defines the
largest and smallest values of each type in the local implementation
...

Objects declared as characters (char) are large enough to store any member of the execution character set
...
Other quantities may be stored into char variables, but the available range of
values, and especially whether the value is signed, is implementation-dependent
...

unsigned char type does not appear in the first edition of this book, but is in common use
...


Besides the char types, up to three sizes of integer, declared short int, int, and long int, are
available
...
Longer integers provide at least as much storage as shorter ones, but the
implementation may make plain integers equivalent to either short integers, or long integers
...

Unsigned integers, declared using the keyword unsigned, obey the laws of arithmetic modulo 2n where n is
the number of bits in the representation, and thus arithmetic on unsigned quantities can never overflow
...

Any of single precision floating point (float), double precision floating point (double), and extra
precision floating point (long double) may be synonymous, but the ones later in the list are at least as
precise as those before
...
The first edition made long float equivalent to double; the locution has been withdrawn
...
A
...
4)
...

Because objects of these types can be interpreted as numbers, they will be referred to as arithmetic types
...
The types float, double, and long double will be called floating types
...
It is used as the type returned by functions that generate no
value
...
4
...

In general these methods of constructing objects can be applied recursively
...
4
...
Declaring an object const announces that its value will not
be changed; declaring it volatile announces that it has special properties relevant to optimization
...
Qualifiers are discussed in
Par
...
8
...

Appendix A - Reference Manual

5

6

Appendix A - Reference Manual

A
...
An obvious example
of an lvalue expression is an identifier with suitable type and storage class
...
The name ``lvalue'' comes from the assignment expression E1 = E2 in which the left operand E1
must be an lvalue expression
...


A
...
This section explains the result to be expected from such conversions
...
6
...


A
...
1 Integral Promotion
A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type,
may be used in an expression wherever an integer may be used
...
This
process is called integral promotion
...
6
...
In a two's
complement representation, this is equivalent to left-truncation if the bit pattern of the unsigned type is
narrower, and to zero-filling unsigned values and sign-extending signed values if the unsigned type is wider
...


A
...
3 Integer and Floating
When a value of floating type is converted to integral type, the fractional part is discarded; if the resulting
value cannot be represented in the integral type, the behavior is undefined
...

When a value of integral type is converted to floating, and the value is in the representable range but is not
exactly representable, then the result may be either the next higher or next lower representable value
...


A
...
4 Floating Types
When a less precise floating value is converted to an equally or more precise floating type, the value is
unchanged
...
If
the result is out of range, the behavior is undefined
...
6
...
The effect is to bring operands into
a common type, which is also the type of the result
...

• First, if either operand is long double, the other is converted to long double
...

• Otherwise, if either operand is float, the other is converted to float
...

• Otherwise, if one operand is long int and the other is unsigned int, the effect depends on
whether a long int can represent all values of an unsigned int; if so, the unsigned int
operand is converted to long int; if not, both are converted to unsigned long int
...

• Otherwise, if either operand is unsigned int, the other is converted to unsigned int
...

There are two changes here
...
Second, shorter unsigned types, when combined
with a larger signed type, do not propagate the unsigned property to the result type; in the first edition, the unsigned
always dominated
...
Unexpected results may still occur when an unsigned expression is compared to a
signed expression of the same size
...
6
...
A
...
7)
...
A
...
7)
...
This produces a null pointer that is equal
to another null pointer of the same type, but unequal to any pointer to a function or object
...
They
must be specified by an explicit type-conversion operator, or cast (Pars
...
7
...
8
...

A pointer may be converted to an integral type large enough to hold it; the required size is
implementation-dependent
...

A pointer to one type may be converted to a pointer to another type
...
It is
guaranteed that a pointer to an object may be converted to a pointer to an object whose type requires less or
equally strict storage alignment and back again without change; the notion of ``alignment'' is
implementation-dependent, but objects of the char types have least strict alignment requirements
...
A
...
8, a pointer may also be converted to type void * and back again without change
...
A
...
4, A
...
2) of the object type to which the pointer refers
...
If qualifiers are removed,
operations on the underlying object remain subject to the qualifiers in its actual declaration
...
Calling the function
specified by the converted pointer is implementation-dependent; however, if the converted pointer is
reconverted to its original type, the result is identical to the original pointer
...
6
...
Because a void expression denotes a nonexistent value, such
an expression may be used only where the value is not required, for example as an expression statement
(Par
...
9
...
A
...
18)
...
For example, a void cast documents the discarding
of the value of a function call used as an expression statement
...


A
...
8 Pointers to Void
Any pointer to an object may be converted to type void * without loss of information
...
Unlike the pointer-to-pointer
conversions discussed in Par
...
6
...

This interpretation of void * pointers is new; previously, char * pointers played the role of generic pointer
...


A
...
Thus, for example, the expressions referred to as the operands of + (Par
...
7
...
A
...
1-A
...
6
...
Left- or right-associativity is specified in each subsection for the operators discussed therein
...
13 incorporates the precedence and associativity of the operators
...
That is, unless the
definition of the operator guarantees that its operands are evaluated in a particular order, the implementation is
free to evaluate operands in any order, or even to interleave their evaluation
...

This rule revokes the previous freedom to reorder expressions with operators that are mathematically commutative and
associative, but can fail to be computationally associative
...


The handling of overflow, divide check, and other exceptions in expression evaluation is not defined by the
language
...
Treatment of division by 0, and all floating-point
exceptions, varies among implementations; sometimes it is adjustable by a non-standard library function
...
7
...
'' This
8

Appendix A - Reference Manual

Appendix A - Reference Manual

9

conversion does not take place if the expression is in the operand of the unary & operator, or of ++, --,
sizeof, or as the left operand of an assignment operator or the
...
Similarly, an expression of type
``function returning T,'' except when used as the operand of the & operator, is converted to ``pointer to
function returning T
...
7
...

primary-expression
identifier
constant
string
(expression)
An identifier is a primary expression, provided it has been suitably declared as discussed below
...
An identifier is an lvalue if it refers to an object (Par
...
5) and if its type is
arithmetic, structure, union, or pointer
...
Its type depends on its form as discussed in Par
...
2
...

A string literal is a primary expression
...
A
...
1, this is usually modified to ``pointer to char''
(wchar_t) and the result is a pointer to the first character in the string
...
A
...
7
...
The precedence of parentheses does not affect whether the expression is an lvalue
...
7
...

postfix-expression:
primary-expression
postfix-expression[expression]
postfix-expression(argument-expression-listopt)
postfix-expression
...
7
...
1 Array References
A postfix expression followed by an expression in square brackets is a postfix expression denoting a
subscripted array reference
...
The expression E1[E2] is
identical (by definition) to *((E1)+(E2))
...
A
...
6
...


Appendix A - Reference Manual

9

10

Appendix A - Reference Manual

A
...
3
...
A7
...
If the postfix expression consists of an identifier for which no declaration exists in the current
scope, the identifier is implicitly declared as if the declaration
extern int identifier();
had been given in the innermost block containing the function call
...
A7
...

In the first edition, the type was restricted to ``function,'' and an explicit * operator was required to call through pointers to
functions
...
The older syntax is still usable
...
The terms
``actual argument (parameter)'' and ``formal argument (parameter)'' respectively are sometimes used for the
same distinction
...
A function may change the values of its parameter objects, which are copies of the argument
expressions, but these changes cannot affect the values of the arguments
...

There are two styles in which functions may be declared
...
In the old style,
parameter types are not specified
...
A
...
6
...
10
...

If the function declaration in scope for a call is old-style, then default argument promotion is applied to each
argument as follows: integral promotion (Par
...
6
...
The effect of the call is undefined if the number of arguments
disagrees with the number of parameters in the definition of the function, or if the type of an argument after
promotion disagrees with that of the corresponding parameter
...
If it is old-style, then the comparison is between the promoted
type of the arguments of the call, and the promoted type of the parameter, if the definition is new-style, the
promoted type of the argument must be that of the parameter itself, without promotion
...
The number of
arguments must be the same as the number of explicitly described parameters, unless the declaration's
parameter list ends with the ellipsis notation (,
...
If the definition of the function is old-style, then
the type of each parameter in the definition, after the definition parameter's type has undergone argument
promotion
...
Mixtures are
to be avoided if possible
...
However, the
arguments and the function designator are completely evaluated, including all side effects, before the function
is entered
...

10

Appendix A - Reference Manual

Appendix A - Reference Manual

11

A
...
3
...
The first operand
expression must be a structure or a union, and the identifier must name a member of the structure or union
...
The
expression is an lvalue if the first expression is an lvalue, and if the type of the second expression is not an
array type
...
The first operand expression must be a pointer to a structure or union, and the identifier must
name a member of the structure or union
...

Thus the expression E1->MOS is the same as (*E1)
...
Structures and unions are discussed in Par
...
8
...

In the first edition of this book, it was already the rule that a member name in such an expression had to belong to the
structure or union mentioned in the postfix expression; however, a note admitted that this rule was not firmly enforced
...


A
...
3
...
The value of the expression is
the value of the operand
...
The
operand must be an lvalue; see the discussion of additive operators (Par
...
7
...
A
...
17)
for further constraints on the operand and details of the operation
...


A
...
4 Unary Operators
Expressions with unary operators group right-to-left
...
7
...
1 Prefix Incrementation Operators
A unary expression followed by a ++ or -- operator is a unary expression
...
The value of the expression is the value after the incrementation (decrementation)
...
A
...
7) and assignment (Par
...
7
...
The result is not an lvalue
...
7
...
2 Address Operator
The unary operator & takes the address of its operand
...
The result is a pointer to the
object or function referred to by the lvalue
...
''
Appendix A - Reference Manual

11

12

Appendix A - Reference Manual

A
...
4
...
It is
an lvalue if the operand is a pointer to an object of arithmetic, structure, union, or pointer type
...

A
...
4
...
An
integral operand undergoes integral promotion
...

The unary + is new with the ANSI standard
...


A
...
4
...

An integral operand undergoes integral promotion
...
The type of the result is the type of the promoted operand
...
7
...
6 One's Complement Operator
The operand of the ~ operator must have integral type, and the result is the one's complement of its operand
...
If the operand is unsigned, the result is computed by subtracting the
value from the largest value of the promoted type
...
The type of the result is the type of the promoted operand
...
7
...
7 Logical Negation Operator
The operand of the ! operator must have arithmetic type or be a pointer, and the result is 1 if the value of its
operand compares equal to 0, and 0 otherwise
...

A
...
4
...
The
operand is either an expression, which is not evaluated, or a parenthesized type name
...

When applied to a structure or union, the result is the number of bytes in the object, including any padding
required to make the object tile an array: the size of an array of n elements is n times the size of one element
...
The
result is an unsigned integral constant; the particular type is implementation-defined
...
h> (See appendix B) defines this type as size_t
...
7
...

cast-expression:
unary expression
(type-name) cast-expression
This construction is called a cast
...
A
...
8
...
A
...
An expression with a cast is not an lvalue
...
7
...

multiplicative-expression:
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression
The operands of * and / must have arithmetic type; the operands of % must have integral type
...

The binary * operator denotes multiplication
...
Otherwise, it is always true that (a/b)*b
+ a%b is equal to a
...


A
...
7 Additive Operators
The additive operators + and - group left-to-right
...
There are some additional type possibilities for each operator
...
A pointer to an object in an array and a value of any
integral type may be added
...
The sum is a pointer of the same type as the original pointer, and points to
another object in the same array, appropriately offset from the original object
...
If the sum pointer points
outside the bounds of the array, except at the first location beyond the high end, the result is undefined
...
It legitimizes a common idiom for looping over the
elements of an array
...
A value of any integral type may be subtracted
from a pointer, and then the same conversions and conditions as for addition apply
...
The type of the result
is defined as ptrdiff_t in the standard header ...
The value is undefined unless the pointers
point to objects within the same array; however, if P points to the last member of an array, then (P+1)-P has
value 1
...
7
...
For both operators, each operand must be integral, and is
subject to integral the promotions
...
The result is
undefined if the right operand is negative, or greater than or equal to the number of bits in the left expression's
type
...
The value of E1>>E2 is E1 right-shifted E2 bit positions
...


A
...
9 Relational Operators
The relational operators group left-to-right, but this fact is not useful; aevaluates to either 0 or 1
...
The type of the result is int
...
Pointers to objects of the same type (ignoring any qualifiers) may be
compared; the result depends on the relative locations in the address space of the pointed-to objects
...
If P points to the last member of an array, then P+1 compares
higher than P, even though P+1 points outside the array
...

These rules slightly liberalize the restrictions stated in the first edition, by permitting comparison of pointers to different
members of a structure or union
...


A
...
10 Equality Operators
equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression
The == (equal to) and the != (not equal to) operators are analogous to the relational operators except for their
lower precedence
...
)
The equality operators follow the same rules as the relational operators, but permit additional possibilities: a
pointer may be compared to a constant integral expression with value 0, or to a pointer to void
...
A
...
6
...
7
...
The
operator applies only to integral operands
...
7
...
The operator applies only to integral operands
...
7
...
The operator applies only to integral operands
...
7
...
It returns 1 if both its operands compare unequal to zero, 0 otherwise
...
Otherwise, the right operand is evaluated, and if it is equal to 0,
the expression's value is 0, otherwise 1
...
The result is
int
...
7
...
It returns 1 if either of its operands compare unequal to zero, and 0
otherwise
...
Otherwise, the right operand is evaluated, and if it
is unequal to 0, the expression's value is 1, otherwise 0
...
The result is
int
...
7
...
Only one of the second and third operands is
evaluated
...
If both are void, or structures or unions
of the same type, or pointers to objects of the same type, the result has the common type
...
If one is a
pointer to void and the other is another pointer, the other pointer is converted to a pointer to void, and that
is the type of the result
...
A
...
2) in the type to which the pointer points are
insignificant, but the result type inherits qualifiers from both arms of the conditional
...
7
...

assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-expression
assignment-operator: one of
= *= /= %= += -= <<= >>= &= ^= |=
All require an lvalue as left operand, and the lvalue must be modifiable: it must not be an array, and must not
have an incomplete type, or be a function
...
The type of an
assignment expression is that of its left operand, and the value is the value stored in the left operand after the
assignment has taken place
...
One of the following must be true: both operands have arithmetic type, in which case the right operand
is converted to the type of the left by the assignment; or both operands are structures or unions of the same
type; or one operand is a pointer and the other is a pointer to void, or the left operand is a pointer and the
right operand is a constant expression with value 0; or both operands are pointers to functions or objects
whose types are the same except for the possible absence of const or volatile in the right operand
...


A
...
18 Comma Operator
expression:
assignment-expression
expression , assignment-expression

16

Appendix A - Reference Manual

Appendix A - Reference Manual

17

A pair of expressions separated by a comma is evaluated left-to-right, and the value of the left expression is
discarded
...
All side effects from
the evaluation of the left-operand are completed before beginning the evaluation of the right operand
...
A
...
3
...
A
...
7), the required syntactic unit is an assignment expression, so the comma
operator appears only in a parenthetical grouping, for example,
f(a, (t=3, t+2), c)

has three arguments, the second of which has the value 5
...
7
...

Constant expressions may not contain assignments, increment or decrement operators, function calls, or
comma operators; except in an operand of sizeof
...
This necessarily rules out arrays, indirection,
address-of, and structure member operations
...
)
More latitude is permitted for the constant expressions of initializers; the operands may be any type of
constant, and the unary & operator may be applied to external or static objects, and to external and static
arrays subscripted with a constant expression
...
Initializers must evaluate either to a constant or to the
address of a previously declared external or static object plus or minus a constant
...
See Par
...
12
...


A
...
Declarations that reserve storage are called definitions
...

declaration-specifiers:
storage-class-specifier declaration-specifiersopt
type-specifier declaration-specifiersopt
type-qualifier declaration-specifiersopt

Appendix A - Reference Manual

17

18

Appendix A - Reference Manual

init-declarator-list:
init-declarator
init-declarator-list , init-declarator
init-declarator:
declarator
declarator = initializer
Declarators will be discussed later (Par
...
8
...
A declaration must
have at least one declarator, or its type specifier must declare a structure tag, a union tag, or the members of an
enumeration; empty declarations are not permitted
...
8
...
A
...
4
...
Such declarations also serve as definitions and cause storage to be reserved
...
Only a few objects are actually placed into registers, and only certain types are eligible; the
restrictions are implementation-dependent
...

The rule that it is illegal to calculate the address of an object declared register, but actually taken to be auto, is new
...
Inside a function, this specifier causes storage to be allocated, and serves as a definition; for its
effect outside a function, see Par
...
11
...

A declaration with extern, used inside a function, specifies that the storage for the declared objects is
defined elsewhere; for its effects outside a function, see Par
...
11
...

The typedef specifier does not reserve storage and is called a storage class specifier only for syntactic
convenience; it is discussed in Par
...
8
...

At most one storage class specifier may be given in a declaration
...

See Pars
...
10-A
...


A
...
2 Type Specifiers
The type-specifiers are

18

Appendix A - Reference Manual

Appendix A - Reference Manual

19

type specifier:
void
char
short
int
long
float
double
signed
unsigned
struct-or-union-specifier
enum-specifier
typedef-name
At most one of the words long or short may be specified together with int; the meaning is the same if
int is not mentioned
...
At most one of signed or
unsigned may be specified together with int or any of its short or long varieties, or with char
...
The signed specifier is useful for forcing char objects
to carry a sign; it is permissible but redundant with other integral types
...
If the type-specifier is missing from a
declaration, it is taken to be int
...

type-qualifier:
const
volatile
Type qualifiers may appear with any type specifier
...
There are no implementation-dependent semantics for volatile objects
...
The purpose of const is to announce objects
that may be placed in read-only memory, and perhaps to increase opportunities for optimization
...
For example, for a machine
with memory-mapped input/output, a pointer to a device register might be declared as a pointer to volatile, in order to
prevent the compiler from removing apparently redundant references through the pointer
...


A
...
3 Structure and Union Declarations
A structure is an object consisting of a sequence of named members of various types
...
Structure and union specifiers have the
same form
...
A structure member may
also consist of a specified number of bits
...

struct-declarator:
declarator
declaratoropt : constant-expression
A type specifier of the form
struct-or-union identifier { struct-declaration-list }
declares the identifier to be the tag of the structure or union specified by the list
...

Objects with an incomplete structure or union type may be mentioned in contexts where their size is not
needed, for example in declarations (not definitions), for specifying a pointer, or for creating a typedef, but
not otherwise
...
Even in specifiers with a list, the structure or union type being declared is
incomplete within the list, and becomes complete only at the } terminating the specifier
...
Therefore, it is impossible to declare a structure or
union containing an instance of itself
...

A very special rule applies to declarations of the form
struct-or-union identifier;
that declare a structure or union, but have no declaration list and no declarators
...
A
...
1), this declaration makes the identifier the
tag of a new, incompletely-typed structure or union in the current scope
...
It is intended to deal with mutually-recursive structures declared in an inner scope, but
whose tags might already be declared in the outer scope
...

20

Appendix A - Reference Manual

Appendix A - Reference Manual

21

The names of members and tags do not conflict with each other or with ordinary variables
...

In the first edition of this book, the names of structure and union members were not associated with their parent
...


A non-field member of a structure or union may have any object type
...
Adjacent field members of structures are packed into implementation-dependent
storage units in an implementation-dependent direction
...
An unnamed field with
width 0 forces this padding, so that the next field will begin at the edge of the next allocation unit
...
It is advisable to read the
language rules for storing bit-fields as ``implementation-dependent'' without qualification
...
In the second case, it is necessary to understand the rules of the local implementation
...
A non-field member
of a structure is aligned at an addressing boundary depending on its type; therefore, there may be unnamed
holes in a structure
...

A union may be thought of as a structure all of whose members begin at offset 0 and whose size is sufficient
to contain any of its members
...
If a pointr to
a union is cast to the type of a pointer to a member, the result refers to that member
...
Once this
declaration has bene given, the declaration
struct tnode s, *sp;

declares s to be a structure of the given sort, and sp to be a pointer to a structure of the given sort
...
left

refers to the left subtree pointer of the structure s, and
s
...

In general, a member of a union may not be inspected unless the value of the union has been assigned using
the same member
...
For example, the following is a
legal fragment:
union {
struct {
int type;
} n;
struct {
int type;
int intnode;
} ni;
struct {
int type;
float floatnode;
} nf;
} u;

...
nf
...
nf
...
14;

...
n
...
sin(u
...
floatnode)
...
8
...
The
form of an enumeration specifier borrows from that of structures and unions
...
If no enumerations with = appear, then the values of the corresponding constants begin
at 0 and increase by 1 as the declaration is read from left to right
...

Enumerator names in the same scope must all be distinct from each other and from ordinary variable names,
but the values need not be distinct
...
The rules for enum-specifiers with and without tags and lists are the same as
those for structure or union specifiers, except that incomplete enumeration types do not exist; the tag of an
enum-specifier without an enumerator list must refer to an in-scope specifier with a list
...


A
...
5 Declarators
Declarators have the syntax:
declarator:
pointeropt direct-declarator
direct-declarator:
identifier
(declarator)
direct-declarator [ constant-expressionopt ]
direct-declarator ( parameter-type-list )
direct-declarator ( identifier-listopt )
pointer:
* type-qualifier-listopt
* type-qualifier-listopt pointer
type-qualifier-list:
type-qualifier
type-qualifier-list type-qualifier
The structure of declarators resembles that of indirection, function, and array expressions; the grouping is the
same
...
8
...
Each declarator declares a
unique main identifier, the one that appears as the first alternative of the production for direct-declarator
...
A
declarator is read as an assertion that when its identifier appears in an expression of the same form as the
declarator, it yields an object of the specified type
...
A
...
2) and a particular declarator, a
declaration has the form ``T D,'' where T is a type and D is a declarator
...

In a declaration T D where D is an unadored identifier, the type of the identifier is T
...
The parentheses do not alter the type, but may
change the binding of complex declarators
...
8
...
1 Pointer Declarators
In a declaration T D where D has the form
* type-qualifier-listopt D1

Appendix A - Reference Manual

23

24

Appendix A - Reference Manual

and the type of the identifier in the declaration T D1 is ``type-modifier T,'' the type of the identifier of D is
``type-modifier type-qualifier-list pointer to T
...

For example, consider the declaration
int *ap[];

Here, ap[] plays the role of D1; a declaration ``int ap[]'' (below) would give ap the type ``array of int,''
the type-qualifier list is empty, and the type-modifier is ``array of
...
''
As other examples, the declarations
int i, *pi, *const cpi = i
const int ci = 3, *pci;

declare an integer i and a pointer to an integer pi
...
The
integer ci is constant, and may not be changed (though it may be initialized, as here
...

A
...
6
...
'' If the constant-expression is present, it must have integral type, and value greater
than 0
...

An array may be constructed from an arithmetic type, from a pointer, from a structure or union, or from
another array (to generate a multi-dimensional array)
...
This implies that for a multi-dimensional
array, only the first dimension may be missing
...
A
...
2), or by initializing it (Par
...
8
...
For example,
float fa[17], *afp[17];

declares an array of float numbers and an array of pointers to float numbers
...
In complete detail, x3d is an array
of three items: each item is an array of five arrays; each of the latter arrays is an array of seven integers
...

The first three have type ``array,'', the last has type int
...

The array subscripting operation is defined so that E1[E2] is identical to *(E1+E2)
...
Because of the conversion rules that apply
to + and to arrays (Pars
...
6, A
...
1, A
...
7), if E1 is an array and E2 an integer, then E1[E2] refers to the
E2-th member of E1
...
The first subexpression
x3d[i][j] is converted by Par
...
7
...
A
...
7, the addition
involves multiplication by the size of an integer
...

A
...
6
...
''
The syntax of the parameters is
parameter-type-list:
parameter-list
parameter-list ,
...
As a special case, the
declarator for a new-style function with no parameters has a parameter list consisting soley of the keyword
void
...
'', then the function may accept more arguments than
the number of parameters explicitly described, see Par
...
7
...
2
...
A
...
1
...

Similarly, if the declarators in the parameter declarations contain identifiers and the function declarator does
not head a function definition, the identifiers go out of scope immediately
...
A
...
8
...
'' The parameters (if present) have the form
identifier-list:
identifier
identifier-list , identifier
In the old-style declarator, the identifier list must be absent unless the declarator is used in the head of a
function definition (Par
...
10
...
No information about the types of the parameters is supplied by the
Appendix A - Reference Manual

25

26

Appendix A - Reference Manual

declaration
...
In none of these are the parameter types specified; they are old-style
...
The parameter names are effectively comments
...

Function declarators with parameter prototypes are, by far, the most important language change introduced by the ANSI
standard
...
Some syntactic ugliness was required for the sake of compatibility, namely void
as an explicit marker of new-style functions without parameters
...
'' for variadic functions is also new, and, together with the macros in the
standard header ...

These notations were adapted from the C++ language
...
8
...

The initializer is preceded by =, and is either an expression, or a list of initializers nested in braces
...

initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
initializer
initializer-list , initializer
All the expressions in the initializer for a static object or array must be constant expressions as described in
Par
...
7
...
The expressions in the initializer for an auto or register object or array must likewise be
constant expressions if the initializer is a brace-enclosed list
...

The first edition did not countenance initialization of automatic structures, unions, or arrays
...


A static object not explicitly initialized is initialized as if it (or its members) were assigned the constant 0
...


26

Appendix A - Reference Manual

Appendix A - Reference Manual

27

The initializer for a pointer or an object of arithmetic type is a single expression, perhaps in braces
...

The initializer for a structure is either an expression of the same type, or a brace-enclosed list of initializers for
its members in order
...
If there are fewer
initializers in the list than members of the structure, the trailing members are initialized with 0
...
Unnamed bit-field members are ignored,and are not initialized
...
If the array has unknown
size, the number of initializers determines the size of the array, and its type becomes complete
...

As a special case, a character array may be initialized by a string literal; successive characters of the string
initialize successive members of the array
...
A
...
6) may initialize an
array of type wchar_t
...

The initializer for a union is either a single expression of the same type, or a brace-enclosed initializer for the
first member of the union
...
The ``first-member'' rule is clumsy, but is hard to generalize without
new syntax
...


An aggregate is a structure or array
...
Braces may be elided in the initialization as follows: if the initializer for an
aggregate's member that itself is an aggregate begins with a left brace, then the succeding comma-separated
list of initializers initializes the members of the subaggregate; it is erroneous for there to be more initializers
than members
...

For example,
int x[] = { 1, 3, 5 };

declares and initializes x as a 1-dimensional array with three members, since no size was specified and there
are three initializers
...
Likewise the next two lines initialize y[1] and y[2]
...
Precisely the same effect could have
been achieved by
float y[4][3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7
};

Appendix A - Reference Manual

27

28

Appendix A - Reference Manual

The initializer for y begins with a left brace, but that for y[0] does not; therefore three elements from the list
are used
...
Also,
float y[4][3] = {
{ 1 }, { 2 }, { 3 }, { 4 }
};

initializes the first column of y (regarded as a two-dimensional array) and leaves the rest 0
...


A
...
8 Type names
In several contexts (to specify type conversions explicitly with a cast, to declare parameter types in function
declarators, and as argument of sizeof) it is necessary to supply the name of a data type
...

type-name:
specifier-qualifier-list abstract-declaratoropt
abstract-declarator:
pointer
pointeropt direct-abstract-declarator
direct-abstract-declarator:
( abstract-declarator )
direct-abstract-declaratoropt [constant-expressionopt]
direct-abstract-declaratoropt (parameter-type-listopt)
It is possible to identify uniquely the location in the abstract-declarator where the identifier would appear if
the construction were a declarator in a declaration
...
For example,
int
int
int
int
int
int

*
*[3]
(*)[]
*()
(*[])(void)

name respectively the types ``integer,'' ``pointer to integer,'' ``array of 3 pointers to integers,'' ``pointer to an
unspecified number of integers,'' ``function of unspecified parameters returning pointer to integer,'' and
``array, of unspecified size, of pointers to functions with no parameters each returning an integer
...
8
...
These identifiers are called typedef names
...
A
...
6)
...

For example, after
typedef long Blockno, *Blockptr;
typedef struct { double r, theta; } Complex;

the constructions
Blockno b;
extern Blockptr bp;
Complex z, *zp;

are legal declarations
...

typedef does not introduce new types, only synonyms for types that could be specified in another way
...

Typedef names may be redeclared in an inner scope, but a non-empty set of type specifiers must be given
...


A
...
10 Type Equivalence
Two type specifier lists are equivalent if they contain the same set of type specifiers, taking into account that
some specifiers can be implied by others (for example, long alone implies long int)
...

Two types are the same if their abstract declarators (Par
...
8
...
Array
sizes and function parameter types are significant
...
9 Statements
Except as described, statements are executed in sequence
...
They fall into several groups
...
9
...

labeled-statement:
identifier : statement
case constant-expression : statement
default : statement
A label consisting of an identifier declares the identifier
...
The scope of the identifier is the current function
...
See Par
...
11
...

Case labels and default labels are used with the switch statement (Par
...
9
...
The constant expression of
case must have integral type
...


A
...
2 Expression Statement
Most statements are expression statements, which have the form
expression-statement:
expressionopt;
Most expression statements are assignments or function calls
...
If the expression is missing, the construction is called a null
statement; it is often used to supply an empty body to an iteration statement to place a label
...
9
...
The body of a function definition is a compound statement
...
A
...
1), after which it resumes its force
...
These rules apply to identifiers in the same name space (Par
...
11); identifiers in different name
spaces are treated as distinct
...
If a jump into the block is executed, these initializations are not performed
...


A
...
4 Selection Statements
Selection statements choose one of several flows of control
...
In the second
form, the second substatement is executed if the expression is 0
...

The switch statement causes control to be transferred to one of several statements depending on the value of
an expression, which must have integral type
...
Any statement within the substatement may be labeled with one or more case labels (Par
...
9
...

The controlling expression undergoes integral promotion (Par
...
6
...
No two of these case constants associated with the same switch may have the same value
after conversion
...
Switches may be
nested; a case or default label is associated with the smallest switch that contains it
...
If one of the case constants is equal to the value of the expression, control passes to
the statement of the matched case label
...
If no case matches, and if there is no default, then
none of the substatements of the swtich is executed
...


A
...
5 Iteration Statements
Iteration statements specify looping
...
With while, the test,
including all side effects from the expression, occurs before each execution of the statement; with do, the test
follows each iteration
...

There is no restriction on its type
...
The third expression is evaluated
after each iteration, and thus specifies a re-initialization for the loop
...

Side-effects from each expression are completed immediately after its evaluation
...
A missing second expression makes the implied test equivalent
to testing a non-zero element
...
9
...

jump-statement:
goto identifier;
continue;
break;
return expressionopt;
In the goto statement, the identifier must be a label (Par
...
9
...
Control
transfers to the labeled statement
...
It causes control to pass to the
loop-continuation portion of the smallest enclosing such statement
...
) {

...

contin: ;
} while (
...
) {

...

A break statement may appear only in an iteration statement or a switch statement, and terminates
execution of the smallest enclosing such statement; control passes to the statement following the terminated
statement
...
When return is followed by an expression, the
value is returned to the caller of the function
...

Flowing off the end of a function is equivalent to a return with no expression
...


A
...


32

Appendix A - Reference Manual

Appendix A - Reference Manual

33

translation-unit:
external-declaration
translation-unit external-declaration
external-declaration:
function-definition
declaration
The scope of external declarations persists to the end of the translation unit in which they are declared, just as
the effect of declarations within the blocks persists to the end of the block
...


A
...
1 Function Definitions
Function definitions have the form
function-definition:
declaration-specifiersopt declarator declaration-listopt compound-statement
The only storage-class specifiers allowed among the declaration specifiers are extern or static; see
Par
...
11
...

A function may return an arithmetic type, a structure, a union, a pointer, or void, but not a function or an
array
...
A
...
6
...

direct-declarator ( parameter-type-list )
direct-declarator ( identifier-listopt )
where the direct-declarator is an identifier or a parenthesized identifier
...

In the first form, the definition is a new-style function, and its parameters, together with their types, are
declared in its parameter type list; the declaration-list following the function's declarator must be absent
...
If the parameter type list ends with ``,
...
h> and described in Appendix B must be used to refer to the extra
arguments
...

In the second form, the definition is old-style: the identifier list names the parameters, while the declaration
list attributes types to them
...
The
declaration list must declare only parameters named in the list, initialization is not permitted, and the only
storage-class specifier possible is register
...
If a parameter is declared to
have type ``array of type,'' the declaration is adjusted to read ``pointer to type;'' similarly, if a parameter is
declared to have type ``function returning type,'' the declaration is adjusted to read ``pointer to function
returning type
...
A
...
3
...

New-style function definitions are new with the ANSI standard
...
The difference

Appendix A - Reference Manual

33

34

Appendix A - Reference Manual
becomes noticable when a pointer to a parameter is generated within a function
...
} is the block giving the code for the function
...
*/
}

where now int max(a, b, c) is the declarator, and int a, b, c; is the declaration list for the
parameters
...
10
...
The term
``external'' refers to their location outside functions, and is not directly connected with the extern keyword;
the storage class for an externally-declared object may be left empty, or it may be specified as extern or
static
...

Two declarations for an object or function are deemed to agree in type under the rule discussed in Par
...
8
...

In addition, if the declarations differ because one type is an incomplete structure, union, or enumeration type
(Par
...
8
...

Moreover, if one type is an incomplete array type (Par
...
8
...
2) and the other is a completed array type, the
types, if otherwise identical, are also taken to agree
...

If the first external declarator for a function or object includes the static specifier, the identifier has
internal linkage; otherwise it has external linkage
...
11
...

An external declaration for an object is a definition if it has an initializer
...
If a definition
for an object appears in a translation unit, any tentative definitions are treated merely as redundant
declarations
...

Each object must have exactly one definition
...
For objects with
external linkage, it applies to the entire program
...
Some implementations relax it by generalizing the notion of tentative definition
...
If a definition occurs somewhere in the program, then the tentative
definitions become merely declarations, but if no definition appears, then all its tentative definitions become a definition
with initializer 0
...
11 Scope and Linkage
A program need not all be compiled at one time: the source text may be kept in several files containing
translation units, and precompiled routines may be loaded from libraries
...

Therefore, there are two kinds of scope to consider: first, the lexical scope of an identifier which is the region
of the program text within which the identifier's characteristics are understood; and second, the scope
associated with objects and functions with external linkage, which determines the connections between
identifiers in separately compiled translation units
...
11
...
These classes are:
objects, functions, typedef names, and enum constants; labels; tags of structures or unions, and enumerations;
and members of each structure or union individually
...
Labels did not previously have
their own name space; tags of structures and unions each had a separate space, and in some implementations enumerations
tags did as well; putting different kinds of tags into the same space is a new restriction
...
This rule has been common practice for several years
...
The scope of a parameter of a
function definition begins at the start of the block defining the function, and persists through the function; the
scope of a parameter in a function declaration ends at the end of the declarator
...
The
scope of a label is the whole of the function in which it appears
...

If an identifier is explicitly declared at the head of a block, including the block constituting a function, any
declaration of the identifier outside the block is suspended until the end of the block
...
11
...
All declarations for the same
object or function identifier with external linkage refer to the same thing, and the object or function is shared
by the entire program
...
A
...
2, the first external declaration for an identifier gives the identifier internal linkage if
the static specifier is used, external linkage otherwise
...
If it does
include extern, and an external declaration for is active in the scope surrounding the block, then the
identifier has the same linkage as the external declaration, and refers to the same object or function; but if no
external declaration is visible, its linkage is external
...
12 Preprocessing
A preprocessor performs macro substitution, conditional compilation, and inclusion of named files
...
The syntax of these
lines is independent of the rest of the language; they may appear anywhere and have effect that lasts
(independent of scope) until the end of the translation unit
...
A
...
2 for how to adjoin lines)
...
A
...
4); in
addition, any character not otherwise defined is taken as a token
...

Preprocessing itself takes place in several logically successive phases that may, in a particular
implementation, be condensed
...
First, trigraph sequences as described in Par
...
12
...
Should the
operating system environment require it, newline characters are introduced between the lines of the
source file
...
Each occurrence of a backslash character \ followed by a newline is deleted, this splicing lines
(Par
...
12
...

3
...
Then preprocessing directives are obeyed, and macros (Pars
...
12
...
12
...

4
...
A
...
5
...
2
...

5
...


A
...
1 Trigraph Sequences
The character set of C source programs is contained within seven-bit ASCII, but is a superset of the ISO
646-1983 Invariant Code Set
...
This replacement
occurs before any other processing
...

Trigraph sequences are new with the ANSI standard
...
12
...
This occurs before division into tokens
...
12
...
A second #define for the same
identifier is erroneous unless the second token sequence is identical to the first, where all white space
separations are taken to be equivalent
...
As with the first form, leading and trailing white space arround the token sequence is
discarded, and the macro may be redefined only with a definition in which the number and spelling of
parameters, and the token sequence, is identical
...
It is not erroneous to apply #undef to an
unknown identifier
...
The arguments of the call are the comma-separated token sequences; commas that are
quoted or protected by nested parentheses do not separate arguments
...
The number of arguments in the call must match the number of parameters in the definition
...
Then the token
sequence resulting from each argument is substituted for each unquoted occurrence of the corresponding
parameter's identifier in the replacement token sequence of the macro
...

Two special operators influence the replacement process
...
A \ character is inserted before each " or \ character that appears surrounding, or inside, a string
literal or character constant in the argument
...
The effect is undefined if invalid tokens are produced,
or if the result depends on the order of processing of the ## operators
...

In both kinds of macro, the replacement token sequence is repeatedly rescanned for more defined identifiers
...

Even if the final value of a macro expansion begins with with #, it is not taken to be a preprocessing directive
...

The most important change is the addition of the # and ## operators, which make quotation and concatenation admissible
...
(See example below
...
Unlike a function to do
the same thing, the arguments and returned value may have any arithmetic type or even be pointers
...

Given the definition
#define tempfile(dir)

#dir "%s"

the macro call tempfile(/usr/tmp) yields
"/usr/tmp" "%s"

which will subsequently be catenated into a single string
...
However, the call cat(cat(1,2),3) is undefined: the
presence of ## prevents the arguments of the outer call from being expanded
...
If a second level of macro definition is introduced,
#define xcat(x, y)

cat(x,y)

things work more smoothly; xcat(xcat(1, 2), 3) does produce 123, because the expansion of xcat
itself does not involve the ## operator
...


A
...
4 File Inclusion
A control line of the form
# include
causes the replacement of that line by the entire contents of the file filename
...
The
named file is searched for in a sequence of implementation-defined places
...
The effect of using ', \, or /* in the filename remains
undefined, but > is permitted
...
> or "
...

#include files may be nested
...
12
...

preprocessor-conditional:
if-line text elif-parts else-partopt #endif
if-line:
# if constant-expression
# ifdef identifier
# ifndef identifier
elif-parts:
elif-line text
elif-partsopt
elif-line:
# elif constant-expression
else-part:
else-line text
else-line:
#else
Each of the directives (if-line, elif-line, else-line, and #endif) appears alone on a line
...
The text following the successful directive
line is treated normally
...
Once a successful #if or #elif line has been found and its text
processed, succeeding #elif and #else lines, together with their text, are discarded
...
Text controlled by inactive
arms of the conditional is ignored except for checking the nesting of conditionals
...
Moreover, any
expressions of the form
defined identifier
or
defined (identifier)
are replaced, before scanning for macros, by 1L if the identifier is defined in the preprocessor, and by 0L if
not
...
Finally, each integer constant is
Appendix A - Reference Manual

39

40

Appendix A - Reference Manual

considered to be suffixed with L, so that all arithmetic is taken to be long or unsigned long
...
A
...
19) is restricted: it must be integral, and may not contain sizeof,
a cast, or an enumeration constant
...

#elif is new since the first edition, although it has been available is some preprocessors
...


A
...
6 Line Control
For the benefit of other preprocessors that generate C programs, a line in one of the forms
# line constant "filename"
# line constant
causes the compiler to believe, for purposes of error diagnostics, that the line number of the next source line is
given by the decimal integer constant and the current input file is named by the identifier
...
Macros in the line are expanded before it is
interpreted
...
12
...


A
...
8 Pragmas
A control line of the form
# pragma token-sequenceopt
causes the preprocessor to perform an implementation-dependent action
...


A
...
9 Null directive
A control line of the form
#

40

Appendix A - Reference Manual

Appendix A - Reference Manual

41

has no effect
...
12
...
They, and also the preprocessor
expansion operator defined, may not be undefined or redefined
...

A string literal containing the name of the file being compiled
...
It is intended that this identifier be defined to be 1 only in standard-conforming
__STDC__
implementations
...


A
...
It has
exactly the same content, but is in different order
...
This grammar can be transformed mechanically into input acceptable for an automatic
parser-generator
...
With one further
change, namely deleting the production typedef-name: identifier and making typedef-name a terminal symbol,
this grammar is acceptable to the YACC parser-generator
...

translation-unit:
external-declaration
translation-unit external-declaration
external-declaration:
function-definition
declaration
function-definition:
declaration-specifiersopt declarator declaration-listopt compound-statement
declaration:
declaration-specifiers init-declarator-listopt;
declaration-list:
declaration
declaration-list declaration
declaration-specifiers:
storage-class-specifier declaration-specifiersopt
type-specifier declaration-specifiersopt
Appendix A - Reference Manual

41

42

Appendix A - Reference Manual
type-qualifier declaration-specifiersopt

storage-class specifier: one of
auto register static extern typedef
type specifier: one of
void char short int long float double signed
unsigned struct-or-union-specifier enum-specifier typedef-name
type-qualifier: one of
const volatile
struct-or-union-specifier:
struct-or-union identifieropt { struct-declaration-list }
struct-or-union identifier
struct-or-union: one of
struct union
struct-declaration-list:
struct declaration
struct-declaration-list struct declaration
init-declarator-list:
init-declarator
init-declarator-list, init-declarator
init-declarator:
declarator
declarator = initializer
struct-declaration:
specifier-qualifier-list struct-declarator-list;
specifier-qualifier-list:
type-specifier specifier-qualifier-listopt
type-qualifier specifier-qualifier-listopt
struct-declarator-list:
struct-declarator
struct-declarator-list , struct-declarator
struct-declarator:
declarator
declaratoropt : constant-expression
enum-specifier:
enum identifieropt { enumerator-list }
enum identifier
enumerator-list:
enumerator
enumerator-list , enumerator

42

Appendix A - Reference Manual

Appendix A - Reference Manual

43

enumerator:
identifier
identifier = constant-expression
declarator:
pointeropt direct-declarator
direct-declarator:
identifier
(declarator)
direct-declarator [ constant-expressionopt ]
direct-declarator ( parameter-type-list )
direct-declarator ( identifier-listopt )
pointer:
* type-qualifier-listopt
* type-qualifier-listopt pointer
type-qualifier-list:
type-qualifier
type-qualifier-list type-qualifier
parameter-type-list:
parameter-list
parameter-list ,
...
identifier
postfix-expression->+identifier
postfix-expression++
postfix-expression-primary-expression:
identifier
constant
string
(expression)
argument-expression-list:
assignment-expression
assignment-expression-list , assignment-expression
constant:
integer-constant
character-constant
floating-constant
enumeration-constant
The following grammar for the preprocessor summarizes the structure of control lines, but is not suitable for
mechanized parsing
...

control-line:
# define identifier token-sequence
# define identifier(identifier,
...
com)

Back to Appendix A -- Index -- Appendix C

Appendix A - Reference Manual

47

Appendix B - Standard Library
This appendix is a summary of the library defined by the ANSI standard
...
We have omitted a few functions that are of limited utility or easily
synthesized from others; we have omitted multi-byte characters; and we have omitted discussion of locale
issues; that is, properties that depend on local language, nationality, or culture
...
h>
...
h>

...
h>
...
h>
...
h>

...
h>
...
h>
...
h>

A header can be accessed by
"sb
...
1 Input and Output: ...
h> represent nearly one third of the
library
...
The library
supports text streams and binary streams, although on some systems, notably UNIX, these are identical
...
An
environment may need to convert a text stream to or from some other representation (such as mapping '\n'
to carriage return and linefeed)
...

A stream is connected to a file or device by opening it; the connection is broken by closing the stream
...
We will use ``file pointer'' and ``stream'' interchangeably when there is no ambiguity
...


B
...
1 File Operations
The following functions deal with operations on files
...

FILE *fopen(const char *filename, const char *mode)
fopen opens the named file, and returns a stream, or NULL if the attempt fails
...
e
...
If the mode includes b after the initial letter, as in
"rb" or "w+b", that indicates a binary file
...

At most FOPEN_MAX files may be open at once
...
It returns
stream, or NULL if an error occurs
...

int fflush(FILE *stream)
On an output stream, fflush causes any buffered but unwritten data to be written; on an input
stream, the effect is undefined
...
fflush(NULL)
flushes all output streams
...
It returns EOF if any errors occurred, and zero
otherwise
...
It returns non-zero
if the attempt fails
...

FILE *tmpfile(void)
tmpfile creates a temporary file of mode "wb+" that will be automatically removed when closed
or when the program terminates normally
...

char *tmpnam(char s[L_tmpnam])
tmpnam(NULL) creates a string that is not the name of an existing file, and returns a pointer to an
internal static array
...
tmpnam generates a different name each time it
is called; at most TMP_MAX different names are guaranteed during execution of the program
...

int setvbuf(FILE *stream, char *buf, int mode, size_t size)
setvbuf controls buffering for the stream; it must be called before reading, writing or any other
operation
...
If buf is not NULL, it will be used as the buffer, otherwise a buffer will be
allocated
...
setvbuf returns non-zero for any error
...
Otherwise, setbuf is equivalent to (void)
setvbuf(stream, buf, _IOFBF, BUFSIZ)
...
1
...

int fprintf(FILE *stream, const char *format,
...
The return value is the
number of characters written, or negative if an error occurred
...
Each conversion specification begins with the character % and ends with a conversion character
...

♦ +, which specifies that the number will always be printed with a sign
...

♦ 0: for numeric conversions, specifies padding to the field width with leading zeros
...
For o, the first digit will become zero
...
For e, E, f, g, and G, the output will always
have a decimal point; for g and G, trailing zeros will not be removed
...
The converted argument will be printed in a field at
least this wide, and wider if necessary
...
The padding character is normally space, but is 0 if the zero padding flag is present
...

• A number, the precision, that specifies the maximum number of characters to be printed from a string,
or the number of digits to be printed after the decimal point for e, E, or f conversions, or the number
of significant digits for g or G conversion, or the number of digits to be printed for an integer (leading
0s will be added to make up the necessary width)
...
``h'' indicates that the corresponding argument is to be printed
as a short or unsigned short; ``l'' indicates that the argument is a long or unsigned
long, ``L'' indicates that the argument is a long double
...

The conversion characters and their meanings are shown in Table B
...
If the character after the % is not a
conversion character, the behavior is undefined
...
1 Printf Conversions
Character
Argument type; Printed As
d,i
int; signed decimal notation
...

unsigned int; unsigned hexadecimal notation (without a leading 0x or 0X), using abcdef
x,X
for 0x or ABCDEF for 0X
...

c
int; single character, after conversion to unsigned char
char *; characters from the string are printed until a '\0' is reached or until the number of
s
characters indicated by the precision have been printed
...
ddd, where the number of d's is given by the
f
precision
...

double; decimal notation of the form [-]m
...
ddddddE+/-xx, where
e,E
the number of d's is specified by the precision
...

double; %e or %E is used if the exponent is less than -4 or greater than or equal to the precision;
g,G
otherwise %f is used
...

p
void *; print as a pointer (implementation-dependent representation)
...
No argument is converted
...
)
printf(
...

int sprintf(char *s, const char *format,
...
s must be big enough to hold the result
...

int vprintf(const char *format, va_list arg)
int vfprintf(FILE *stream, const char *format, va_list arg)
Appendix B - Standard Library

3

4

Appendix B - Standard Library

int vsprintf(char *s, const char *format, va_list arg)
The functions vprintf, vfprintf, and vsprintf are equivalent to the corresponding printf
functions, except that the variable argument list is replaced by arg, which has been initialized by the
va_start macro and perhaps va_arg calls
...
h> in Section B
...


B
...
3 Formatted Input
The scanf function deals with formatted input conversion
...
)

fscanf reads from stream under control of format, and assigns converted values through subsequent
arguments, each of which must be a pointer
...
fscanf returns EOF if
end of file or an error occurs before any conversion; otherwise it returns the number of input items converted
and assigned
...

The format string may contain:
• Blanks or tabs, which are not ignored
...

• Conversion specifications, consisting of a %, an optional assignment suppression character *, an
optional number specifying a maximum field width, an optional h, l, or L indicating the width of the
target, and a conversion character
...
Normally the result is placed in
the variable pointed to by the corresponding argument
...
An input field is defined as a string of
non-white space characters; it extends either to the next white space character or until the field width, if
specified, is exhausted
...
(White space characters are blank, tab, newline, carriage return, vertical tab, and
formfeed
...
The corresponding argument must be a
pointer
...
2
...
The conversion characters e, f, and g
may be preceded by l if a pointer to double rather than float is in the argument list, and by L if a pointer
to a long double
...
2 Scanf Conversions
Character
d
i
o
u
x
c

4

Input Data; Argument type
decimal integer; int*
integer; int*
...

octal integer (with or without leading zero); int *
...

hexadecimal integer (with or without leading 0x or 0X); int*
...
The next input characters are placed in the indicated array, up to the
number given by the width field; the default is 1
...
The normal skip
Appendix B - Standard Library

Appendix B - Standard Library

s
e,f,g
p
n
[
...
]
%

5

over white space characters is suppressed in this case; to read the next non-white space
character, use %1s
...

floating-point number; float *
...

pointer value as printed by printf("%p");, void *
...
No
input is read
...

matches the longest non-empty string of input characters from the set between brackets;
char *
...
] includes ] in the set
...
A '\0' is added
...

literal %; no assignment is made
...
)
scanf(
...

int sscanf(const char *s, const char *format,
...
) is equivalent to scanf(
...


B
...
4 Character Input and Output Functions
int fgetc(FILE *stream)
fgetc returns the next character of stream as an unsigned char (converted to an int), or
EOF if end of file or error occurs
...
fgets returns s, or NULL if end
of file or error occurs
...
It returns the
character written, or EOF for error
...

int getc(FILE *stream)
getc is equivalent to fgetc except that if it is a macro, it may evaluate stream more than once
...

char *gets(char *s)
gets reads the next input line into the array s; it replaces the terminating newline with '\0'
...

int putc(int c, FILE *stream)
putc is equivalent to fputc except that if it is a macro, it may evaluate stream more than once
...

int puts(const char *s)
puts writes the string s and a newline to stdout
...

int ungetc(int c, FILE *stream)

Appendix B - Standard Library

5

6

Appendix B - Standard Library
ungetc pushes c (converted to an unsigned char) back onto stream, where it will be returned
on the next read
...
EOF may not be pushed
back
...


B
...
5 Direct Input and Output Functions
size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream)
fread reads from stream into the array ptr at most nobj objects of size size
...
feof and ferror must be
used to determine status
...
It returns the number
of objects written, which is less than nobj on error
...
1
...
For a binary file, the position is set to offset characters from origin, which may be
SEEK_SET (beginning), SEEK_CUR (current position), or SEEK_END (end of file)
...
fseek returns non-zero on error
...

void rewind(FILE *stream)
rewind(fp) is equivalent to fseek(fp, 0L, SEEK_SET); clearerr(fp)
...
The
type fpos_t is suitable for recording such values
...

int fsetpos(FILE *stream, const fpos_t *ptr)
fsetpos positions stream at the position recorded by fgetpos in *ptr
...


B
...
7 Error Functions
Many of the functions in the library set status indicators when error or end of file occur
...
In addition, the integer expression errno (declared in ...

void clearerr(FILE *stream)
clearerr clears the end of file and error indicators for stream
...

int ferror(FILE *stream)
ferror returns non-zero if the error indicator for stream is set
...
3
...
2 Character Class Tests: ...
h> declares functions for testing characters
...

The functions return non-zero (true) if the argument c satisfies the condition described, and zero if not
...

isalnum(c)
isalpha(c)
iscntrl(c)
isdigit(c)
isgraph(c)
islower(c)
isprint(c)
ispunct(c)

In addition, there are two functions that convert the case of letters:
int tolower(c) convert c to lower case
int toupper(c) convert c to upper case
If c is an upper-case letter, tolower(c) returns the corresponding lower-case letter, toupper(c) returns
the corresponding upper-case letter; otherwise it returns c
...
3 String Functions: ...
h>
...
Except for memmove, the behavior is undefined if
copying takes place between overlapping objects
...

In the following table, variables s and t are of type char *; cs and ct are of type const char *; n is
of type size_t; and c is an int converted to char
...

copy at most n characters of string ct to s; return s
...

concatenate string ct to end of string s; return s
...

compare string cs to string ct, return <0 if cs0 if
cs>ct
...

return pointer to first occurrence of c in cs or NULL if not present
...

size_t
return length of prefix of cs consisting of characters in ct
...

strcspn(cs,ct)
char
return pointer to first occurrence in string cs of any character string ct, or
*strpbrk(cs,ct)
NULL if not present
...

size_t strlen(cs)
return length of cs
...

char *strtok(s,ct) strtok searches s for tokens delimited by characters from ct; see below
...
The first
call in a sequence has a non-NULL s, it finds the first token in s consisting of characters not in ct; it
terminates that by overwriting the next character of s with '\0' and returns a pointer to the token
...
strtok returns NULL when no further token is found
...

The mem
...
In the following table, s and t are of type void *; cs and ct are of type const void
*; n is of type size_t; and c is an int converted to an unsigned char
...

same as memcpy except that it works even if the objects overlap
...

return pointer to first occurrence of character c in cs, or NULL if not
present among the first n characters
...


B
...
h>
The header ...

The macros EDOM and ERANGE (found in ...
A domain error
occurs if an argument is outside the domain over which the function is defined
...
A range error occurs if the result of the function
cannot be represented as a double
...
If the result underflows, the function returns zero; whether errno is set
to ERANGE is implementation-defined
...
Angles
for trigonometric functions are expressed in radians
...

cos-1(x) in range [0,pi], x in [-1,1]
...

tan-1(y/x) in range [-pi,pi]
...

base 10 logarithm log10(x), x>0
...
A domain error occurs if x=0 and y<=0, or if x<0 and y is not an integer
...

smallest integer not less than x, as a double
...

absolute value |x|
x*2n
splits x into a normalized fraction in the interval [1/2,1) which is returned, and a power
of 2, which is stored in *exp
...

splits x into integral and fractional parts, each with the same sign as x
...

floating-point remainder of x/y, with the same sign as x
...


B
...
h>
The header ...

double atof(const char *s)
atof converts s to double; it is equivalent to strtod(s, (char**)NULL)
...

long atol(const char *s)
converts s to long; it is equivalent to strtol(s, (char**)NULL, 10)
...
If the answer would overflow, HUGE_VAL is
returned with the proper sign; if the answer would underflow, zero is returned
...

long strtol(const char *s, char **endp, int base)
strtol converts the prefix of s to long, ignoring leading white space; it stores a pointer to any
unconverted suffix in *endp unless endp is NULL
...
If base is zero, the base is 8, 10, or 16; leading 0
implies octal and leading 0x or 0X hexadecimal
...
If the answer would overflow, LONG_MAX or
LONG_MIN is returned, depending on the sign of the result, and errno is set to ERANGE
...

int rand(void)
rand returns a pseudo-random integer in the range 0 to RAND_MAX, which is at least 32767
...
The initial seed is 1
...
The space is initialized to zero bytes
...
The space is uninitialized
...
The contents will be unchanged up
to the minimum of the old and new sizes
...

realloc returns a pointer to the new space, or NULL if the request cannot be satisfied, in which case
*p is unchanged
...
p must be a pointer to space
previously allocated by calloc, malloc, or realloc
...

void exit(int status)
exit causes normal program termination
...
How status is returned to the environment is implementation-dependent, but zero is
taken as successful termination
...

int atexit(void (*fcn)(void))
atexit registers the function fcn to be called when the program terminates normally; it returns
non-zero if the registration cannot be made
...
If s is NULL, system returns
non-zero if there is a command processor
...

char *getenv(const char *name)
getenv returns the environment string associated with name, or NULL if no string exists
...

void *bsearch(const void *key, const void *base,
size_t n, size_t size,
int (*cmp)(const void *keyval, const void *datum))

bsearch searches base[0]
...
The function cmp
must return negative if its first argument (the search key) is less than its second (a table entry), zero if
equal, and positive if greater
...
bsearch returns
a pointer to a matching item, or NULL if none exists
...
base[n-1] of objects of size size
...

int abs(int n)
abs returns the absolute value of its int argument
...

div_t div(int num, int denom)
div computes the quotient and remainder of num/denom
...

ldiv_t ldiv(long num, long denom)
ldiv computes the quotient and remainder of num/denom
...


B
...
h>
The assert macro is used to add diagnostics to programs:
void assert(int expression)
10

Appendix B - Standard Library

Appendix B - Standard Library

11

If expression is zero when
assert(expression)
is executed, the assert macro will print on stderr a message, such as
Assertion failed: expression, file filename, line nnn
It then calls abort to terminate execution
...

If NDEBUG is defined at the time ...


B
...
h>
The header ...

Suppose lastarg is the last named parameter of a function f with a variable number of arguments
...


B
...
h>
The declarations in ...

int setjmp(jmp_buf env)
The macro setjmp saves state information in env for use by longjmp
...
A call to setjmp can only
occur in certain contexts, basically the test of if, switch, and loops, and only in simple relational
expressions
...
The function containing the setjmp must not have terminated
...


B
...
h>
The header ...

void (*signal(int sig, void (*handler)(int)))(int)

signal determines how subsequent signals will be handled
...
Valid signals include
SIGABRT
SIGFPE
SIGILL
SIGINT
SIGSEGV
SIGTERM

abnormal termination, e
...
, from abort
arithmetic error, e
...
, zero divide or overflow
illegal function image, e
...
, illegal instruction
interactive attention, e
...
, interrupt
illegal storage access, e
...
, access outside memory limits
termination request sent to this program

signal returns the previous value of handler for the specific signal, or SIG_ERR if an error occurs
...
If the handler returns, execution will resume where it was
when the signal occurred
...

int raise(int sig)

raise sends the signal sig to the program; it returns non-zero if unsuccessful
...
10 Date and Time Functions: ...
h> declares types and functions for manipulating date and time
...
clock_t and time_t
are arithmetic types representing times, and struct tm holds the components of a calendar time:
int
int
int
int
int
int
int
int
int

12

tm_sec;
tm_min;
tm_hour;
tm_mday;
tm_mon;
tm_year;
tm_wday;
tm_yday;
tm_isdst;

seconds after the minute (0,61)
minutes after the hour (0,59)
hours since midnight (0,23)
day of the month (1,31)
months since January (0,11)
years since 1900
days since Sunday (0,6)
days since January 1 (0,365)
Daylight Saving Time flag

Appendix B - Standard Library

Appendix B - Standard Library

13

tm_isdst is positive if Daylight Saving Time is in effect, zero if not, and negative if the information is not
available
...
clock()/CLK_PER_SEC is a time in seconds
...
If tp is not NULL, the return
value is also assigned to *tp
...

time_t mktime(struct tm *tp)
mktime converts the local time in the structure *tp into calendar time in the same representation
used by time
...
mktime returns the calendar
time or -1 if it cannot be represented
...

char *asctime(const struct tm *tp)
asctime*tp into a string of the form
Sun Jan

3 15:14:13 1988\n\0

char *ctime(const time_t *tp)
ctime converts the calendar time *tp to local time; it is equivalent to
asctime(localtime(tp))

struct tm *gmtime(const time_t *tp)
gmtime converts the calendar time *tp into Coordinated Universal Time (UTC)
...
The name gmtime has historical significance
...

size_t strftime(char *s, size_t smax, const char *fmt, const struct tm
*tp)
strftime formats date and time information from *tp into s according to fmt, which is
analogous to a printf format
...
Each %c is replaced as described below, using values appropriate for the local environment
...
strftime returns the number of characters, excluding
the '\0', or zero if more than smax characters were produced
...

full weekday name
...

full month name
...

day of the month (01-31)
...

hour (12-hour clock) (01-12)
...

month (01-12)
...

local equivalent of AM or PM
...


Appendix B - Standard Library

13

14

Appendix B - Standard Library
%U
%w
%W
%x
%X
%y
%Y
%Z
%%

week number of the year (Sunday as 1st day of week) (00-53)
...

week number of the year (Monday as 1st day of week) (00-53)
...

local time representation
...

year with century
...

%

B
...
h> and ...
h> defines constants for the sizes of integral types
...

CHAR_BIT
8
bits in a char
CHAR_MAX
UCHAR_MAX or SCHAR_MAX maximum value of char
CHAR_MIN
0 or SCHAR_MIN
maximum value of char
INT_MAX
32767
maximum value of int
INT_MIN
-32767
minimum value of int
LONG_MAX
2147483647
maximum value of long
LONG_MIN
-2147483647
minimum value of long
SCHAR_MAX +127
maximum value of signed char
SCHAR_MIN -127
minimum value of signed char
SHRT_MAX
+32767
maximum value of short
SHRT_MIN
-32767
minimum value of short
UCHAR_MAX 255
maximum value of unsigned char
UINT_MAX
65535
maximum value of unsigned int
ULONG_MAX 4294967295
maximum value of unsigned long
USHRT_MAX 65535
maximum value of unsigned short
The names in the table below, a subset of ...

When a value is given, it represents the minimum magnitude for the corresponding quantity
...

FLT_RADIX
FLT_ROUNDS
FLT_DIG
FLT_EPSILON
FLT_MANT_DIG
FLT_MAX
FLT_MAX_EXP
FLT_MIN
FLT_MIN_EXP
DBL_DIG
DBL_EPSILON
DBL_MANT_DIG
DBL_MAX
14

2
6
1E-5
1E+37
1E-37
10
1E-9
1E+37

radix of exponent, representation, e
...
, 2, 16
floating-point rounding mode for addition
decimal digits of precision
smallest number x such that 1
...
0
number of base FLT_RADIX in mantissa
maximum floating-point number
maximum n such that FLT_RADIXn-1 is representable
minimum normalized floating-point number
minimum n such that 10n is a normalized number
decimal digits of precision
smallest number x such that 1
...
0
number of base FLT_RADIX in mantissa
maximum double floating-point number
Appendix B - Standard Library

Appendix B - Standard Library
DBL_MAX_EXP
DBL_MIN
DBL_MIN_EXP

1E-37

15
maximum n such that FLT_RADIXn-1 is representable
minimum normalized double floating-point number
minimum n such that 10n is a normalized number

Back to Appendix A -- Index -- Appendix C
Compiled by tmdcjsl(skidrow8123@hotmail
...

Almost all were extensions of the original language, and were carefully designed to remain compatible with
existing practice; some repaired ambiguities in the original description; and some represent modifications that
change existing practice
...

More recently, the ANSI committee standardizing the language incorporated most of the changes, and also
introduced other significant modifications
...

This Appendix summarizes the differences between the language defined by the first edition of this book, and
that expected to be defined by the final standard
...

• Preprocessing is more carefully defined in the Standard than in the first edition, and is extended: it is
explicitly token based; there are new operators for concatenation of tokens
("temp0103
...
12">Par
...
12
...
(Many implementations
provide more
...
Escapes for #\^[]{}|~ are defined, see Par
...
12
...
Observe that the introduction of trigraphs
may change the meaning of strings containing the sequence ??
...
The stillborn
entry keyword is withdrawn
...
The effect of
following \ by a character not part of an approved escape sequence is undefined
...
A
...
5
...

• Everyone's favorite trivial change: 8 and 9 are not octal digits
...
It also refines the rules for the type of unsiffixed constants (Par
...
2
...

• Adjacent string literals are concatenated
...
A
...
6
...
The locution long float as a synonym for double is
withdrawn, but long double may be used to declare an extra-precision floating quantity
...
The standard introduces the signed
keyword to make signedness explicit for char and other integral objects
...
The Standard introduces
the use of the void * type as a generic pointer type; previously char * played this role
...

• The Standard places explicit minima on the ranges of the arithmetic types, and mandates headers
( ...
h>) giving the characteristics of each particular implementation
...

• The Standard adopts from C++ the notion of type qualifier, for example const (Par
...
8
...

• Strings are no longer modifiable, and so may be placed in read-only memory
...
''
See Par
...
6
...

• The old assignment operators like =+ are truly gone
...


Appendix C - Summary of Changes

1

2

Appendix C - Summary of Changes
• A compiler's license to treat mathematically associative operators as computationally associative is
revoked
...

• A pointer to a function may be used as a function designator without an explicit * operator
...
A
...
3
...

• Structures may be assigned, passed to functions, and returned by functions
...

• The sizeof operator, in the first edition, yielded type int; subsequently, many implementations
made it unsigned
...
h>)
...
See Par
...
7
...
8 and Par
...
7
...

• The address-of operator & may not be applied to an object declared register, even if the
implementation chooses not to keep the object in a register
...

See Par
...
7
...

• The Standard legalizes the creation of a pointer just beyond the end of an array, and allows arithmetic
and relations on it; see Par
...
7
...

• The Standard introduces (borrowing from C++) the notion of a function prototype declaration that
incorporates the types of the parameters, and includes an explicit recognition of variadic functions
together with an approved way of dealing with them
...
A
...
3
...
8
...
3, B
...
The older style
is still accepted, with restrictions
...
On the other hand, a declaration with just a structure or
union tag redeclares that tag even if it was declared in an outer scope
...

• Some implementations, when presented with an extern declaration in an inner block, would export
the declaration to the rest of the file
...

• The scope of parameters is injected into a function's compound statement, so that variable
declarations at the top level of the function cannot hide the parameters
...
The Standard puts all tags in a single name

2

Appendix C - Summary of Changes


Title: C language
Description: Full knowledge of c language..