Computer Science A Structured Approach Using C by Behrouz A. Forouzan Richard F. Gilberg
Computer Science A Structured Approach Using C by Behrouz A. Forouzan Richard F. Gilberg
Computer Science A Structured Approach Using C by Behrouz A. Forouzan Richard F. Gilberg
— THIRD EDITION
Operator Description Example * Assoc Pr
Identifiers amount
3.14159 N N /A 16
' Constants
Parenthetical Expressions (a + b)
[ ]. Array Index ary[i] N
ninction Call doIt(x, y) Y
Direct Member Selection str.mem N Left - Right 16
-> Indirect Member Selection ptr >mem - N
++ Postfix Increment • Decrement a++ Y
++ Prefix Increment • Decrement ++a Y
sizeof Size in Bytes sizeof(int) N
!
Ones Complement
Not
-a
la
N
N Right - Left 15
+ Plus • Minus +a N
& Address &a N
* Dereference / Indirection *ptr N
( )Type Cast (int)ptr N Right -Left 14
* /% Multiply • Divide • Modulus a * b N Left - Right 13
+ - Addition • Subtraction a + b N Left - Right 12
« » Bit Shift Left • Bit Shift Right a << 3 N Left - Right 1 1
< <= > >= Comparison a < 5 N Left - Right 10
ss ! = Equal • Not Equal N Left - Right 9
a == b
& Bitwise And a & b N Left - Right 8
A
Bitwise Exclusive Or a
A
b N Left - Right 7
Bitwise Or a b N Left - Right 6
&& Logical And a b ScSc N Left - Right 5
Logical Or a b N Left - Right 4
? : Conditional a ? x • Y * N Right - Left 3
= + = -= a = 5
*= / = % =
Assignment a %= b
>> =
A
<< =
a &= c Y Right - Left 2
& = = a |= d
> Comma a, b, c N Left - Right 1 i
* Side Effects ( Yes / No )
Precedence Table
>H
' . '
i -t X .U 6
\/o
*+ - - M i | J, -
' X AH .v A f\ £ w \/ dUe
Computer Science:
A Structured Programming
Approach Using C,
Third Edition
DATE DUE
Nov o 5 rm
LI 2o / H
1ft
March
/tprt I a ao\ ( p
2-6 ^6
AA 3 Z7
^i y
Wt ~ 7 IS7 %
JW 3 0 2( 17 <15
i m 1 o : Qi8 Clb
DEC 17 BtS
fa
IAUG 0 ! 2012
»7
Standard C ( 1999 )
I he following topics, in alphabetical order, are explicitly discussed in the text .
• the Boolean type
• character set extensions ( including Unicode and wide-characters)
• complex arithmetic
• extended math library
• for statement declarations
• integer type extensions
• line comments
IV
Preface to Third Edition v
A C Language Perspective
While C is a complex and professional language, our classroom experience
using the previous editions of this book has shown that beginning students can
easily understand it . We believe that if the language is put into a perspective
that allows the student to understand its design , C is not a difficult language.
There are two aspects of C that separate it from most other languages:
expressions and pointers. The concept of expressions, as used in C , is unique.
The first half of this text builds a firm understanding of expressions. We intro-
duce pointers only to the extent necessary to cover passing them to functions
7
in Chapter 4 and arrays in Chapter 8. Our experiences have shown that with
a firm grasp of the expression concept , much of the mystery of C disappears.
In Chapters 9 , we begin to develop the concept of pointers, which are
further developed in Chapter 10. Chapter 1 1 provides a discussion of C’s
approach to strings. Finally, Chapter 14 provides an extensive discussion of
bitwise operators, a subject that is needed to make the book complete.
The last chapter is a simple introduction to data structures. While not
all courses will have time to cover this chapter, those that do will give the
ce to Third Edition
Visual Approach
A brief scan of the hook will demonstrate that our approach is visual . 1 here
are more than 400 figures, plus more than 70 tables and 180 program exam -
pies. While this amount of material tends to create a large hook, the visual
approach makes it easy for students to follow the material .
Examples
While the programming examples vary in
style. Our experience
complexity, each uses a consistent
working with productional programs that live for 10 to
20 years convinced us that
readable and understandable programs are easier
to work with than programs
written in a terse, cryptic manner. For that reason ,
Preface to Third Edition vii
Coding Techniques
Throughout the text we include coding techniques that make programs more
readable and often more efficient. For example, in the analyTsis of Program 5 - 4
you will find the following discussion:
Where do we check for greater than } The answer is that we
default the greater than condition to the outer else . . . . When cod -
ing a two - way selection statement , try to code the most probable
condition first; with nested selection statements, code the most
probable first and the least probable last.
These techniques are drawn from our extensive industry and classroom
experience.
Software Engineering
A discussion of software engineering principles concludes each chapter. Our
intent is not to replace a separate course in software engineering. Rather, we
firmly believe that by incorporating basic software engineering principles
early in their studies, students will be better prepared for a formal treatment
of the subject . Even more important, by writing well-engineered programs
from the beginning, students will not be forced to unlearn and relearn. I hey
will better understand software discussions in their subsequent classes.
While the software engineering sections are found at the end of each
chapter, they are most successfully taught by introducing them as the chapter
unfolds. Then, a short review at the end of the chapter summarizes the prin-
ciples that have been demonstrated during the lectures.
These sections are visually distinguishable from the rest of the chapter.
They have been set apart for several reasons. First, they are in reality a small
book within a book. While these sections contain important material, the book
stands on its own without them. You may, therefore, decide to cover the soft -
ware engineering sections as formal lecture topics or informally while the
chapter material is being covered. You may decide to assign them to the stu -
dent as additional reading, or, you may decide to exclude them entirely from
the course.
In general, software engineering sections directly or indirectly pertain to
the chapter material. Where they don’t, they discuss general software engi-
neering subjects, such as cohesion, coupling, and quality.
vjjj Preface to Third Edition
Pedagogical Material
first , it helps the stu -
End chapter material meets two pedagogical objectives:
dents to review or summarize what they have learned , and second , it tests the
students’ mastery of the chapter material .
Review Material
• Tips and Common Programming Errors points out helpful hints and
possible problem areas.
• Key Terms provides a list of the important terms introduced in the chapter.
• Summary contains a concise overview of the key points for students to
understand in the chapter.
Practice Sets
• Review Questions contain several multiple choice questions that are
similar to the questions found in the examination database. The answers
to the odd - numbered questions are included in the solutions on the
Course Technology web site .
• Exercises are short questions covering the material in the chapter. The
answers to the odd - numbered exercises are also included in the solutions
on the Course Technology web site.
• Problems are short coding problems, generally intended to be run on a
computer. They can usually be developed in two to three hours. Once
again , odd - numbered solutions are found on the web site .
• Projects are longer, major assignments that may take the average student
six to nine hours to develop.
Professor Materials
The professors materials include the solutions to all review questions, exer-
cises , and problems. Because the projects are more complex assignments lor
which there is no standard answer, we do not provide solutions for them .
In addition to the end - material solutions , there are three other sets ol
support materials: ( 1 ) Copies of all programs in the text. The programs have
been extensively tested on at least two different computers with different
operating systems. All of them conform to the C99 Standard.
( 2 ) PowerPoint materials covering objectives, figures, programs, impor -
tant points , and chapter summaries. The PowerPoint materials can be used as
is or edited to suit individual class needs. This flexibility allows professors to
argument the text materials with their own . It also allows them to rearrange
the materials to suit their individual needs and style .
( 3) New with the third edition is the availability of ExamView®. This
objective- based test generator lets the professor create paper, LAN , or Web-
based tests from testhanks designed specifically for this text . Using the
QuickTest Wizard , they can easily and quickly create true/false and multiple -
choice tests. It is also possible to add questions that cover supplemental
material provided by the professor.
Student Materials
I he student materials include the solutions to the odd - numbered review
questions , exercises , and problems. They also have access to the copies of the
programs used in the text
Acknowledgments
No book of this scope can he developed without the support ol many people.
Reviewers
To anyone who has not been through the process, the value ol peer reviews
cannot he appreciated enough . Writing a text rapidly becomes a myopic pro -
cess. The important guidance of reviewers who can stand hack and review the
text as a whole cannot be measured. We would especially like to acknowledge
the contributions of the reviewers of all three editions.
Stephen Allen , Utah State University
Mary Astone, / roy State University
Ali Behforooz, Towson State University
George Berrv, Wentworth Institute oj technology
Ernest Carey, Utah Valley State College
Ping Chu Chu , Fayetteville State University
Preface tojhird Edition
r
of San Francisco
Constance Conner, City College
Connecticut State University
John S. DaPonte, Southern University
Maurice L. Eggen , Trinity
/
BehrouzA. Forouzan
Richard F. Gilberg
1
Unary Expressions 99
Binary Expressions 101
3.2 Precedence and Associativity 106
Precedence 107
Associativity 108
3.3 Side Effects 110
3.4 Evaluating Expressions 111
Expressions without Side Effects 111
Expressions with Side Effects 112
Warning 113
3.5 Type Conversion 114
Implicit Type Conversion 114
Explicit Type Conversion (Cast) 118
3.6 Statements 120
Statement Type 120
The Role of the Semicolon 124
Statements and Defined Constants 124
3.7 Sample Programs 125
3.8 Software Engineering 135
KISS 135
Parentheses 135
User Communication 136
3.9 Tips and Common Errors 138
3.10 Key Terms 138
3.11 Summary 139
3.12 Practice Sets 140
Review Questions 140
Exercises 142
Problems 144
Projects 146
Glossary 1129
Index 1145
t
I
Introduction to Computers
and
Welcome to computer science! You are about to explore a wonderful
exciting world — a world that offers many challengin g and
In this chapter, we introduce you to the concepts of computer
exciting careers .
science ,
especially as they pertain to computer programming . You will study the con -
cept of a computer system and how it relates to computer hardware
and soft -
ware. We will also present a short history of computer programmi ng
languages so that you understand how they have evolved and how the C lan -
guage fits into the picture.
We will then describe how to write a program , lirst with a review ot the
tools and steps involved , and then with a review of a system development
methodology.
Objectives
To review basic computer systems concepts
To be able to understand the different computing environments and
their
components
To review the history of computer languages
To be able to list and describe the classifications of computer language
s
To understand the steps in the development of a computer program
To review the system development life cycle
1
2 Section 1.1 Computer Systems
Computer
System
Hardware Software
Computer Hardware
The hardware component of the computer system consists of five parts:
input devices, central processing unit (CPU ), primary storage, output
devices, and auxiliary storage devices (Figure 1 - 2 ).
Primary Monitor
Keyboard CPU
Storage
Input Devices
t
Printer
Auxiliary Storage Devices Output Devices
one, tyvo ,
movement of data inside the system . Today’s computers may have
main memory is a place
or more CPUs. Primary storage , also known as
,
where the programs and data are stored temporarily during processing. The
data in primary storage are erased when we turn off a personal computer or
when we log off from a time -sharing computer.
The output device is usually a monitor or a printer to show output . If the
output is shown on the monitor, we say we have a soft copy. If it is printed
on
the printer, we say we have a hard copy .
Auxiliary storage, also known as secondary storage, is used for both
input and output. It is the place where the programs and data are stored per-
manently. When we turn off the computer, our programs and data remain in
the secondary storage, ready for the next time we need them .
Computer Software
Computer software is divided into two broad categories: system software and
application software. This is true regardless of the hardware system architec -
ture. System software manages the computer resources. It provides the inter-
face between the hardware and the users hut does nothing to directly serve
the users’ needs. Application software, on the other hand , is directly responsi -
ble for helping users solve their problems. Figure 1 - 3 shows this breakdown
of computer software .
Software
System Application
Software Software
System Software
System software consists of programs that manage the hardware resources
of a computer and perform required information processing tasks . These pro -
grams are divided into three classes: the operating system , system support
,
and system developme nt .
The operating system provides services such as a user interface, file and
database access, and interfaces to communication systems such as Internet
protocols. The primary purpose of this software is to keep the system operat -
.
ing in an efficient manner while allowing the users access to the system
System support software provides system utilities and other operating
services . Examples of system utilities are sort programs and disk format
4 Section 1.1 Computer Systems
perlormance
consist of programs that provide
programs. Operating services and security monitors to protect the system
statistics for the operational
staff
and data. software ,
, system development
The last system software category lan -
convert programs into machine
includes the language translators that ensure that the programs are error-
guage for execution , debugging tools
to
engineering ( CASE ) systems that
are
free , and computer-assisted software
beyond the scope of this hook .
Application Software
classes: general-purpose software
Application software is broken into two
purpose software is purchased
and application -specific software. General-
he used lor more than one application .
from a software developer and can
word processors , database
Examples of general - purpose software include
management systems, and computer-aided
design systems. I hey are labeled
computing problems.
general purpose because they can solve a variety of user
purpose.
Application -specific software can he used only for its intended
requirements
A general ledger system used by accountants and a material
are examples of
planning system used by a manufacturing organization
only for the task for which
application -specific software. They can he used
they were designed ; they cannot he used for other generaliz ed tasks .
User
Software
W
If users cannot buy software that supports their needs , then a custom -
developed application must be built . In today’s computing environment , one
of the tools used to develop software is the C language that we will he study-
ing in this text .
this situation , we have the whole computer for ourself ; we can do whatever
we want . A typical personal computer is shown in Figure 1 - 5 .
Monitor
Hard Drive
(Internal)
DVD ^
CD
Keyboard Mouse
operating
1 . PC is now generally accepted to mean any hardware using one of Microsoft s Windows
’
Central Computer
Central Storage
Shared Printers
Client/Server Environment
A client/server computing environment splits the computing function
between a central computer and users’ computers. The users are given per-
sonal computers or workstations so that some of the computation responsi -
bility can he moved Irom the central computer and assigned to the
workstations. In the client/server environment , the users’ microcomputers or
workstations are called the client . The central computer, which may be a
powerful microcomputer, minicomputer, or central mainframe system , is
known as the server. Because the work is now shared between the users
computers and the central computer, response time and monitor display are
aster and the users are more productive. Figure 1 - 7 shows a tvpical client /
server environment.
Chapter 1 Introduction to Computers 7
Server
Shared Printers
Central Storage
Clients
Distributed Computing
A distributed computing environment provides a seamless integration of
computing I unctions between different servers and clients. The Internet pro-
vides connectivity to different servers throughout the world . For example ,
eBay uses several computers to provide its auction service. This environment
provides a reliable, scalable, and highly available network. Figure 1 -8 shows a
distributed computing system.
Server
Internet
Server Clients
(browsers)
1940s 1950s
Machine Languages
only programming languages available
In the earliest days of computers , the
computer has its own machine language ,
were machine languages. Each
1 - 1 shows an example of a
which is made of streams of Os and fs. Program
two numbers and prints the
machine language. This program multiplies
results.
Symbolic Languages
It became obvious that few programs would be written if programmers contin-
.
ued to work in machine language In the early 1950s, Admiral Grace I topper, a
mathematician and naval officer, developed the concept of a special computer
program that would convert programs into machine language. I hese early
programming languages simply mirrored the machine languages using
symbols, or mnemonics, to represent the various machine language instructions.
Because they used symbols, these languages were known as symbolic
.
languages Program 1 - 2 shows the multiplication program in a symbolic
language. 2
2. The symbolic language format is label , operators, and operands. There are no labels in this example,
so the left column is empty.
10 Section 1.3 Computer Languages
continued
3. FORTRAN is an acronym for
4. COBOL is an acronym FORmula TRANslation.
for COmmon Business-Ori
rientcd Language.
Chapter 1 Introduction to Computers 11
} // main
Source
00110 100
10101 010
Compiler
01001 011
10110 100
is Object
Loader
Results
Compiling Programs
The code in a source file stored on the disk must he translated into machine
language . This is the job of the compiler. The C compiler is actually tw o sep -
arate programs: the preprocessor and the translator.
The preprocessor reads the source code and prepares it for the transla -
tor. While preparing the code , it scans for special instructions known as
preprocessor commands. These commands tell the preprocessor to look for
special code libraries, make substitutions in the code, and in other ways pre -
pare the code for translation into machine language . The result of prepro-
cessing is called the translation unit .
After the preprocessor has prepared the code for compilation , the trans-
lator does the actual work of converting the program into machine language.
I he translator reads the translation unit and writes the resulting object
module to a file that can then be combined with other precompiled units to
form the final program . An object module is the code in machine language .
Even though the output of the compiler is machine language
code, it is not
yet ready to run ; that is, it is not yet executable
because it does not have the
required C and other functions included .
Linking Programs
fomTofof theseSeVatT
omc th ’
functions
3
uam maJe UPof °f source> Unctions. However
C P 8
and?they
iS man ’ We write
processes^and m.them..icai
, are a part our program
W L , " ,T that " ."elsewhere
libra
°
functions
" T1' f •
^
exist and
The linker assembles be attached
must our program
to .
all of these functio ns, ours and
final executable program . the system s , into our
Chapter 1 Introduction to Computers 13
Executing Programs
Once our program has been linked , it is ready for execution . To execute a pro-
gram , we use an operating system command , such as run , to load the pro-
gram into primary memory and execute it . Getting the program into memory
is the function of an operating system program known as the loader. It
locates the executable program and reads it into memory. When everything is
loaded , the program takes control and it begins execution . In todays inte -
grated development environments, these steps are combined under one
mouse click or pull-down window.
In a typical program execution , the program reads data lor processing,
either from the user or from a file . After the program processes the data , it
prepares the output. Data output can he to the user ’s monitor or to a file .
When the program has finished its job, it tells the operating system , which
then removes the program from memory.
—
5. Many computer scientists believe that all programs contain at least one hug an undetected error —
that is just waiting to cause problems, given the right set of circumstances. Programs have run for
years without problems only to fail when an unusual situation occurs. Perhaps the most famous bug
was the one known as Y 2 K because it caused programs to fail on January 1.20( H ).
6. For a discussion of various models, see Software Engineering : A Practitioner ' s Approach , 5 th. ed . by
Roger S . Pressman , ( New York . NY: McGraw- Hill , 2001 ).
/
Systems
Requirements
Analysis
Design
Maintenance
The waterfall model starts with systems requirements . In this phase, the
systems analyst defines requirements that specif ) what the proposed system
is to accomplish. The requirements are usually stated in terms that the user
understands. The analysis phase looks at different alternatives from a system ’s
point of view, while the design phase determines how the system will be built .
In the design phase, the functions of the individual programs that will make
up the system are determined and the design of the liles and/or the databases
is completed . Finally, in the fourth phase, code , we write the programs. This
is the phase that is explained in this book. After the programs have been writ -
ten and tested to the programmer’s satisfaction , the project proceeds to sys -
tem test . All of the programs arc tested together to make sure the system
works as a whole . The final phase, maintenance , keeps the system working
once it has been put into production .
Although the implication of the waterfall approach is that the phases
( low in a continuous stream from the first
to the last, this is not really the
case. Note the iteration as indicated by the backward - flowing arrows in
Figure 1 - 1 1 . As each phase is developed , errors and omissions will often be
found in the previous work. When this happens, it is necessary to go back to
the previous phase to rework it lor consistency and to analyze the
impact
caused hv the changes. Hopefully, this is a short rework. We are aware of at
least three major projects, however, that were in the code
and test phases
when it was determined that they could not be implemented
and had to be
canceled. When this happens, millions of dollars and years
of development
are lost .
Program Development
Program Development is a multistep process
stand the problem , develop a solution , that requires that we under-
write the program , and then test it.
Chapter 1 Introduction to Computers 15
understand the problem and also due to their excitement about solving new
a
problem . In the first case , what they find is that they did not fully understand
the problem. By taking the time to design the program , they will raise more
questions that must be answered and therefore will gain a better understand -
ing ol the problem .
.
1 The purpose of calculating the square footage is to install new floor cov-
ering.
. .
2 Only the living space will be carpeted The garage and closets will not he
considered.
.
3 The kitchen and bathrooms will he covered with linoleum; the rest ol the
house is to he carpeted.
With this understanding, we decide to write separate modules lor the
kitchen, bathroom( s), bedrooms, family room, and living room. We use sepa -
rate modules because the various rooms may require a dilferent quality ol lino-
leum and carpeting. The structure chart for our design is shown in Figure 1 - 12.
flooring
Cost
get print
calculate Report
Userlnfo
calc calc
Linoleum Carpeting
calc calc
FamilyRoom DineLiving
Pseudocode
are used
a loosely defined syntax and
to
English-like statements that follow
convey the design of an algorithm.
Flowchart
A flowchart is a program design tool in which standard graphical symbols arc
used to represent the logical flow of data through a function. Appendix C
contains complete instructions lor creating flowcharts. If you are not f amiliar
with flowcharts, we suggest you read Appendix C now.
I In flowchart in Figure 1 - 13 shows the design for
*
calculating the area
and cost for the bathrooms . A few points merit
comment here. This flow-
chart is basically the same as the pseudocode. We begin with
prompts for the
Pnce °f the bnoleum and the number of bathrooms and read these two
pieces of data. ( As a general rule ,
flowcharts do not explicitly show standard
1
concepts such as prompts ) The loop reads the dimensions for each bath -
.
.
room Finally, when we know the total area, we calculate the price and return
to calcLinoleum.
O
/ bathProc < \ no
f calcbathRooms ^\ numbath w
7
yes
READ
/
( linoPrice) READ
( bathLngth,
bathWdth )
bathArea
READ
( numbath )
/
-*— zero
bathArea
bathArea +
bathLngth * bathWdth
bathProc
bathProc zero bathProc + 1
o
»
linoCost ^
bathArea ‘ linoPrice
RETURN
When we write a program, we start with the top hox on the structure
chart and work our way to the bottom. This is known as top-down implemen -
tation. You will find that it is a very easy and natural way to write programs,
especially if you have done a solid job on your design.
For our first few programs, there will he only one module, representing
the top hox of the structure chart . The first programs are quite simple and do
20 Section 1.5 System Development
n . When .
we g f 4 however
rlumter
.
, we will begin to
„
subdivisio
.
tjme we wi
not require
write functions an our
struc urt
writing structured
^^
programs . In the
.
point out some more techn | d
good pseudocodede or flowchart s for the
meantime , concentrate on writing
main part of our programs.
Blackbox Jesting
with -
Blackbox testing gets its name from the concept of testing the program
out knowing what is inside it
like a black
—
box
without
that we
knowing
can ’t see
how
into.
it works . In other words ,
the program is
Blackbox test plans are developed by looking only at the requirements
statement ( this is only one reason why it is so important to have a good set ol
requirements ) . The test engineer uses these requirements and his or her
knowledge of systems development and the user working environment to cre-
ate a test plan that will then he used when the system is tested as a whole. We
should ask to see this test plan before we write our program . The test engi -
neer 's plan will help us make sure we fully understand the requirements and
also help us create our own test plan .
Whitebox Jesting
Whereas blackbox testing assumes that the tester knows nothing about the
program , whitebox testing assumes that the tester knows everything about
the program . In this case , the program is like a glass house in which every -
thing is visible.
Whitebox testing is our responsibility. As the programmer, we know
exactly what is going on inside the program . We must make sure that every
instruction and even possible situation has been tested . That iis not a
simple task!
fmnf„ iTTi r, Wil' hClP” UuV g00d tCSt data but one thing we can do
f Witi S « ’P ans. We should star the,
st T ‘
n
ask oursetf
S gC
° " '
dt'sign stage. As we build your structure chart ,
““
and make a note of th m
hour later.
^^ WC eed l teSt f r
" °them an
we won ’t remember
°
Chapter 1 Introduction to Computers 21
Except for the most simple program, one set of test data will not completely
validate a program.
When it is time to construct our test cases, we review our notes and orga -
nize them into logical sets. Except for very simple student programs, one set
of test data will never completely validate a program . For large -scale develop -
ment projects, 20, 30, or even more test cases may need to he run to validate
a program .
Finally, while we arc testing, we will think of more test cases. Again , write
them down and incorporate them into our test plan . After our program is fin -
ished and in production , we will need the test plan again when we make mod -
ifications to the program .
I low do we know when our program is completely tested ? In reality, there
is no way to know lor sure. But there are a few things we can do to help the
odds. While some of these concepts will not be clear until you have read
other chapters , we include them here for completeness.
1 . Verify that every line of code has been executed at least once . Fortu -
nately, there are programming tools on the market today that will do this
for us.
2. Verify that every conditional statement in our program has executed both
the true and false branches, even if one of them is null . ( See Chapter 5 . )
3. For every condition that has a range, make sure the tests include the first
and last items in the range, as well as items below the first and above the
—
last the most common mistakes in array range tests occur at the
extremes of the range. (See Chapter 8. )
4 . If error conditions are being checked , make sure all error logic is tested .
I bis may require us to make temporary modifications to our program to
force the errors. ( For instance, an input /output error usually cannot be
—
created it must he simulated . )
22 Section 1.6 Software Engineering
.
Software engineer and that works on
methods and
*
principles
to obtain software that is reliable
, from the first interna
tional conference on
7 !
real machines This definition
was proposed 30 years
after the first computer
software engineering in 969 , art than a science. In
, software was more of
an
was built . During that period ing desenbes it as
treatments of programm
fact, one of the most authoritative . This three -volume senes, origi -
an art : The Art of Computer
Programming
1970 s , is con -
nally written by Donald E. Knuth in
the late 1960s and early
of many computer science concepts .
sidered the most complete discussion base for building reliable softwar e
Because the science and engineering 1960 s were a maze of
the 1950 s and
did not yet exist , programs written in
known as “ spaghet ti code .” It was not until Edsger Dijkstra wrote
complexity
a letter to the editor of the Communication
s of the ACM ( Association of Com -
Machinery ) 9 in 1968 that the concept
of structured programming
puting
began to emerge.
Dijkstra was working to develop algorithms
that would mathematically
d that any program could he \ \ i ittcn w ith
prove program accuracy. He propose
es, ( 2 ) the if ...else
only three constructs or types of instructions: 1 sequenc
( )
selection statement , and ( 3 ) the while loop . As we will
see , language develop -
and the switch in C . These
ers have added constructs, such as the for loop
ments to Dijkstra s basic constructs
additional statements are simply enhance
that make programming easier . Today , virtually all program ming languages
offer structure d program ming capabili ties.
Throughout this text we will he emphasizing the concepts of good soft -
ware engineering. Chief among them is the concept of structured program -
ming and a sound programming style. A section in each chapter will include a
discussion of these concepts with specific emphasis on the application of the
material in the chapter.
The tools of programming design have also changed over the years . In the
first generation of programming, one primary tool was a block diagram. This
tool provided boxes, diamonds, and other flowchart symbols to represent dif -
ferent instructions in a program . Each instruction was contained in a sepa -
rate symbol . Ibis concept allowed programmers to write a program on paper
and check its logic flow before they entered it in the computer.
With the advance of symbolic programming, the block diagram gave way
to the flowchart. Although the block diagram and flowchart look similar, the
flowchart does not contain the detail of the block diagram . Many instructions
are implied by the descriptive names put into the boxes ; for example, the read
1 r~
9 . Edsger W . Dijkstra , "Go To Slatemenl
no. 3 ( March 1968 ).
Considered Harmful r
Harmful ," Commun
'
,
2 < Third Edi ion '
.
statements in Figure 1 - 1 3 imply the prompt Flowcharts have largely given
way to other techniques in program design, but they are still used today by
many programmers for working on a difficult logic problem .
Today’s programmers are most likely to use a high-level design tool such
as tight English or pseudocode. We will use pseudocode throughout the text
to describe many of the algorithms we will he developing.
Finally, the last several years have seen the automation of programming
through the use of computer-assisted software engineering ( CASE) tools.
These tools make it possible to determine requirements, design software, and
develop and test software in an automated environment using programming
workstations. The discussion of the CASE environment is beyond the scope
of this text and is left for courses in systems engineering.
I
24 Section 1.8 Key Terms
1.9 Summary
A computer system consists of hardware and software .
Computer hardware consists of a central processing unit ( CPU ) , primary
memory, input devices, output devices , and auxiliary storage .
Software consists of two broad categories: system software and application
software.
The components of system software are the operating system , system sup-
port , and system development.
Application software is divided into general- purpose applications and
application -specific software.
Over the years , programming languages have evolved from machine lan -
guage , to symbolic language , and to high - level languages.
The C language is a high - level language .
The software used to write programs is known as a text editor.
The file created from a text editor is known as a source fde.
J The code in a source file must be translated into machine language using
the C compiler, which is made of two separate programs: the preprocessor
and the translator.
The file created from the compiler is known as an object module.
J An object module is linked to the standard functions necessary for running
the program by the linker.
A linked program is run using a loader.
The system development life cycle is a series of interrelated steps that pro -
vide a rigorous and systematic approach to software development.
To develop a program , a programmer must complete the iollowing steps:
a. Understand the problem .
b . Develop a solution using structure charts and either flowcharts or
pseudocode.
c. Write the program .
d . Test the program .
The development of a test plan starts with the design of the program and
continues through all steps in program development .
Blackbox testing consists primarily of testing based on user requirements.
26 Section 1.10 Practice Sets
.
c Operating system
d. Sort
e. Security monitor
.
13 The is a program design tool that is a visual repre -
sentation of the logic in a function within a program.
a . Flowchart
b. Program map
c . Pseudocode
d. Structure chart
e . Waterfall model
28 Section 1.10 Practice Sets
Exercises
system .
15. Describe the two major components ol a computer
16. Computer hardware is made up of five parts.
List and describe them .
Problems
32. Write pseudocode for calcLivingAreas , Figure
1 - 12 , “Structure Chart for
C alculating Square Footage.
33. Create a flowchart for a routine task ,
such as calling a friend , that you do
on a regular basis.
34. Write pseudocode for the
flowchart you created in Problem 33.
Introduction to the C Language
In Chapter 1 , we traced the evolution of computer languages Irom the
machine languages to high - level languages. As mentioned , C ( the language
used exclusively in this hook ) is a high - level language. Since you are going to
spend considerable time working with the language, you should have some
idea of its origins and evolution .
In this chapter we introduce the basics ol the C language. You will write
your first program , which is traditionally known in C as the ' Hello World , ” or
“ Greeting" program . Along the way we will introduce you to the concepts ol
data types , constants , and variables. Finally, you will see two C library func-
tions that read and write data . Since this chapter is just an introduction to C ,
most of these topics are covered only in sufficient detail to enable you to
write your first program. They will he fully developed in luture chapters.
Objectives
To understand the structure of a C -language program
To write your first C program
To introduce the include preprocessor command
To be able to create good identifiers for objects in a program
To be able to list, describe, and use the C basic data types
To be able to create and use variables and constants in a program
To understand input and output concepts as they apply to C programs
To be able to use simple input and output statements
To understand the software engineering role in documentation, data nam -
ing, and data hiding
29
p
30 Section 2.1 Bockgtound
2.1 Background
It is considered a high -level lan-
C is a structured programming language
to concentrate on the problem at
guage because it allows the programmer
program wdl be using. VVhde
hand and not worry about the machine that the
many languages claim to be machine independent , C is one of the closest to
why it is used by software develop-
achieving that goal. That is another reason
hardware platforms.
ers whose applications have to run on many different
ALGOL, the first lan-
C, like most modern languages, is derived from
gained wide acceptance in the
guage to use a block structure. ALGOL never
United States, but it was widely used in Europe .
ALGOLs introduction in the early 1960s paved the way for the develop -
ment of structured programming concepts. Some ol the first
work was done
by two computer scientists, Corrado Bohm and Guiseppe Jacopini, who pub -
lished a paper in 1966 that defined the concept of structured programming .
Another computer scientist, Edsger Dijkstra, popularized the concept. Mis
letter to the editors of the Communications of the ACM ( Association of Com -
puting Machinery) brought the structured programming concept to the atten -
tion of the computer science community.
Several obscure languages preceded the development ol C. In 1967,
Martin Richards developed a language he called Basic Combined Program-
ming Language, or BCPL. Ken Thompson followed in 1970 with a similar
language he simply called B. B was used to develop the first version of UNIX,
one of the popular network operating systems in use today. Finally, in 1972,
Dennis Ritchie developed C, which took many concepts from ALGOL,
BCPL. This path, along with several others, is shown in Figure 2 - 1 .
ALGOL
B ALGOL - W Modula - 2
Traditional
C Modula-3
ANSI/ISO
C99
C
2.2 ( Programs
It s time to write your first C program ! This section will take you through all
the basic parts of a C program so that you will be able to write it .
Structure of a C Program
Every C program is made of one or more preprocessor commands, a global dec -
laration section , and one or more functions. The global declaration section
comes at the beginning of the program . We will talk more about it later, hut
the basic idea of global declarations is that they are visible to all parts of the
program.
The work of the program is carried out by its functions, blocks of code
that accomplish a task within a program . One, and only one, of the functions
must be named main . The main function is the starting point tor the pro -
gram . .All functions in a program , including main , are divided into two sec -
tions: the declaration section and the statement section. The declaration
section is at the beginning of the function . It describes the data that you will
Preprocessor Directives
Global Declarations
Local Declarations
Statements
} // main
Preprocessor directive to
include <stdio.h> include standard input/output
^ functions in the program.
Preprocessor Commands
The preprocessor commands come at the beginning of the program. All pre-
processor commands start with a pound sign ( # ); this is just one of the rules
.
of C known as its syntax Preprocessor commands can start in any column,
but they traditionally start in column 1 .
The preprocessor command tells the compiler to include the standard
input /output library file in the program. You need this library file to print a
message to the terminal. Printing is one ol the input/output processes identi-
fied in this library. The complete syntax for this command is shown below.
# include <stdio.h>
mam
The executable part of your program begins with the function main, which is
identified hv the function header shown below. We explore the meaning ol
the function syntax in Chapter 4. For now, all you need to understand is that
int says that the function will return an integer value to the operating system,
that the function's name is main, and that it has no parameters (the parame-
ter list is void ). Note that there is no punctuation alter the function
header.
J
34 Section 2.2 C Programs
.. , function one
—
w,,hi„ here ar
S.“EL
, ,
writingttfthTmonitor To invoke or vr ru v h» print
^
,
^
Comments
Although it is reasonable to expect that a good programme
r should he able to
read code, sometimes the meaning of a section of
code is not entirely clear.
who writes the
This is especially true in C . Thus , it is helpful if the person
the reader. Such comments
code places some comments in the code to help
on . The compiler ignores these
are merely internal program documentati
comments when it translates the program into executable code . Io identify a
comment , C uses two different formats : block comments and line comments .
Block Comment
A block comment is used when the comment will span several lines. We call
this comment format block comment . It uses opening and closing comment
tokens. A token is one or more symbols understood by the compiler that help
it interpret code . Each comment token is made of two characters that , taken
together, form the token ; there can be no space between them . The opening
token is /* and the closing token is * /. Everything between the opening and
closing comment tokens is ignored by the compiler. The tokens can start in
any column , and they do not have to be on the same line. The only require -
ment is that the opening token must precede the closing token . Figure 2 - 4
shows two examples of block comments.
/*
* * It is a very common style to put the opening token
** on
Line Comment
The second format, the line comment , uses two slashes ( //) to identify a
comment. This format docs not require an end-of-comment token; the end of
the line automatically ends the comment . Programmers generally use this
format for short comments. The line - comment token can start anywhere on
the line. Figure 2 - 5 contains two examples ol line comments.
Inner ^
^
Comment not Closing
v Allowed / Token
/* /* */ */
Left On
Ignored Its Own
2.3 Identifiers
One feature present in all computer languages is the identifier. Identifiers
allow us to name data and other objects in the program . Each identified
object in the computer is stored at a unique address. If we didn ’t have identi -
fiers that we could use to symbolically represent data locations, we would
have to know and use object’s addresses. Instead , we simply give data identifi -
ers and let the compiler keep track of where they are physically located .
Different programming languages use different syntactical rules to form
identifiers. In C , the rules for identifiers are very simple . The only valid name
symbols are the capital letters A through Z, the lowercase letters a through z ,
the digits 0 through 9 , and the underscore. The first character of the identi -
fier cannot he a digit .
Typically, application programs do not use the underscore for the first
character either, because many of the identifiers in the C system libraries
start with an underscore. In this way, we make sure that
our names do not
duplicate system names , which could become very confusing. The last rule is
that the name we create cannot he keywords. Keywords, also
known as
reserved words , include syntactical words, such as if and
xvhile . For a list of
the reserved words, see Appendix R .
Good identifier names are descriptive but short . To
make them short we
often use abbreviations.2 C allows names to he
up to 63 characters long. II
the names are longer than 63 characters, then only the first 63 are used.
Table 2- 1 summarizes the rules for identifiers.
.
1 First character must be alphabetic character or underscore .
2. Must consist only of alphabetic characters, digits, or underscores.
3. First 63 characters of an identifier are significant.
4. Cannot duplicate a keyword.
An identifier must start with a letter or underscore: it may not have a space
or a hyphen .
Another way to separate the words in a name is to capitalize the first let -
ter in each word. The traditional method of separation in C uses the under -
score. A growing group of programmers, however, prefer to capitalize the first
letter of each word. Table 2 - 2 contains examples of valid and invalid names.
C is a case-sensitive language.
Two more comments about identifiers. Note that some of the identifiers
in Table 2 - 2 are capitalized. Typically, capitalized names are reserved lor pre -
processor - defined names. T he second comment is that C is case sensitive.
This means that even though two identifiers are spelled the same, if the case
of each corresponding letter doesn’t match, C thinks of them as different
names. Under this rule, num, Num, and NUM are three different identifiers.
C Types
Floating-point Derived
Void Integral
In this chapter, we concentrate only on the first three types. The derived
type will be discussed in future chapters.
Void Type
The void type , designated by the keyword void , has no values and no opera -
tions, Although having no values and operations might seem unusual , the void
type is a very uselul data type. For example, it is used to designate that a func-
tion has no parameters as we saw in the main function. It can also he used to
define that a function has no return value as we see in Chapter 4. It can also
be used to define a pointer to generic data as we will see in Chapter 9.
Integral Type
The C language has three integral types: Boolean , character, and integer,
Integral types cannot contain a fraction part; they are whole numbers.
Boolean
With the release of C99, the C language incorporated a
Boolean type.
Named after the French mathematician /philosopher George Boole, a Bool-
ean type can represent only two values: true or false .
Prior to C99 , C used
integers to represent the Boolean values: a o
nonzer number (positive or
negative ) was used to represent true, and
zero was
For backward compatibility, integers can still he used to represent false
,
used to represent Boolean
Chapter 2 Introduction to the C Language 39
values; however, we recommend that new programs use the Boolean type.
The Boolean type, which is referred to by the keyword bool , is stored in mem -
ory as 0 ( false ) or 1 ( true ) .
Character
The third type is character. Although we think of characters as the letters of
the alphabet , a computer has another definition . To a computer, a character
is any value that can be represented in the computer’s alphabet , or as it is bet -
ter known , its character set . The C standard provides two character types:
char and wchar t .
char
wcharj
Most computers use the American Standard Code for Information Inter-
—
change ( ASCII pronounced “ ask-key ) alphabet . You do not need to memo-
rize this alphabet as you did when you learned your natural languages;
however, you will learn many of the special values by using them . The ASCII
code is included in Appendix A.
Most of the personal, mini - , and mainframe computers use 1 byte to
store the char data types. A byte is 8 bits. With 8 bits , there are 256 different
values in the char set . ( Note in Appendix A that ASCII uses only hall of these
possible values.) Although the size of char is machine dependent and varies
from computer to computer, normally it is 1 byte, or 8 bits.
If you examine the ASCII code carefully, you will notice that there is a
pattern to its alphabet that corresponds to the English alphabet . The first
32 ASCII characters and the last ASCII character are control characters.
They are used to control physical devices, such as monitors and printers, and
in telecommunication systems. The rest are characters that we use to com -
pose words and sentences.
All the lowercase letters are grouped together, as are all the uppercase
letters and the digits. Many of the special characters, such as the shift char-
acters on the top row of the keyboard , are grouped together, but some are
found spread throughout the alphabet.
What makes the letter u different from the letter x? In English , it is the
visual formation of the graphic associated with the letter. In the computer, it
is the underlying value of the hit configuration for the letter. The letter a is
binary 0110 0001. The letter x is 0111 1000 . The decimal values of these
two binary numbers are 97 and 120 , respectively.
To support non - English languages and languages that don’t use the
Roman alphabet , the C99 standard created the wide character type
40 Section 2.4 Types
( u’charj )
to »« < >
. Without going in> our for two - byte - chTrac
national standards, one or , YP traditional characters found in
ters. B„« h of those standard character. The otfgi -
ASCII ; that is, all extensions occur as the basic Latin character set . Gen-
nal ASCII characters are now known beyond the scope of an introductory
set is
erally speaking, the wide-character text.
programming text and is not covered in this
Integer
int , int , long int , and long long m . A short
sizes of the integer data type: short
, long int can be referred to as long and
,
int can also be referred to as short
long . C defines these data types so
long long int can be referred to as long , as shown in
be organized from the smallest to the largest
that they can
Figure 2- 9 . The type also defines the size of
the field in which data can be
stored. In C, this is true even though the size is machine
dependent and var -
ies from computer to computer.
short int
int
long int
sizeof (short) < sizeof (int) £ sizeof (long) < sizeof (long long)
S ,gnedEach
’
integer size can be a signed or an unsigned integer. If the integer is
tben one bit must be u ^d for a signed (0 is plus, 1 is minus ) . The
unsigned integer can store a positive number that is twice as large as the signed
integer of the same size.* Table 2 -3 contains typical
values for the integer
08"''6’
hardware aCtUal
^
are dePende on the physical
"‘
3. For a complete discussion , see Appendix D ,
“ Numbering Systems.”
Chapter 2 Introduction to the C Language 41
Real
The real type holds values that consist of an integral and a fractional part,
.
such as 43.32 The C language supports three different sizes of real types:
flout , double, and long double . As was the case for the integer type, real num-
bers are defined so that they can be organized from smallest to largest. I he
relationship among the real types is seen in Figure 2 - 10.
float
double
long double
^ . ^*
_‘ number is a real
mathematics and engineering.
a
!!j maginary tvpe, like the real type,
P
'
can ,
double imaginary.
r
beyonhS «e” m (h lm m *
the imaginary type yet and the
•"*
Most C implementations do not: support . We mention them here
functions to handle them are not pa rt of the standard
because the imaginary type is one of
-the components of the complex type.
Complex
by most compilers. A com -
C defines a complex type, which is implemented
plex number is a combination of a real and an imaginary ' number. 1 he com -
of three different sizes : float complex
plex type, like the real type, can be
complex . The size needs to be the same in both
double complex , and long long
the real and the imaginary' part . We provide two program examples that use
com plex numbers at the end ot this chapter.
Type Summary
A summary of the four standard data types is shown in I able 2 - 4.
Category
Void Void
a void
C Implementation
2.5 Variables
Variables are named memory locations that
have a type , such as integer or
character, which is inherited from their type.
that a variable may contain the operations
The type determines the values
that may he used with its values.
Chapter 2 Introduction to the C Language 43
Variable Declaration
Each variable in your program must be declared and defined. In C, a declara -
tion is used to name an object, such as a variable. Definitions are used to
.
create the object With one exception, a variable is declared and defined at
the same time. The exception, which we will see later, declares them first and
then defines them at a later time. For variables, definition assumes that the
declaration has been done or is being done at the same time. W hile this dis -
tinction is somewhat of an oversimplification, it works in most situations. We
won’t worry' about the exception at this time.
When we create variables, the declaration gives them a symbolic name
and the definition reserves memory for them. Once defined, variables are
used to hold the data that are required by the program lor its operation. Gen -
erally speaking, where the variable is located in memory is not a program -
mer ’s concern; it is a concern only of the compiler. From our perspective, all
we are concerned with is being able to access the data through their symbolic
names, their identifiers. The concept of variables in memory is illustrated in
Figure 2 - 11 .
Variable' s Variable's
. type „ identifier
char code;
int i ;
long long national debt ;
float payRate ;
double pi;
Program
FIGURE 2 - 11 Variables
A variable’s type can he any of the data types, such as character, integer ,
or real . The one exception to this rule is the type void : a variable cannot be
type void .
To create a variable, we first specify the type, which automatically speci -
fies it size ( precision ), and then its identifier, as shown below in the definition
of a real variable named price ol type float.
float price ;
bool
short
long
fact ;
maxltems ;
long national debt ;
_ / / Word separator : Capital
/ / word separator : underscore
/ / Word separator : Capital
float payRate ;
double tax;
;
float complex voltage
—
/ / Poor style see text
char
int
code , kind ;
a, b; —
/ / Poor style see text
Variable Initialization
We can initialize a variable at the same time that we declare it hv including an
initializer. When present , the initializer establishes the first value that the
variable will contain. To initialize a variable when it is defined , the identifier
is followed by the assignment operator 4 and then the initializer, which is the
value the variable is to have when the function starts. This simple initializa -
tion format is shown below.
int count = 0;
Every time the function containing count is entered, count is set to zero.
Now, what will be the result of the following initialization ? Are both count
and sum initialized or is only sum initialized ?
Again, to avoid confusion and error, we prefer using only one variable
definitionto a line. The preferred code in this case would he
int count = 0;
int sum = 0;
Program Memory
.
When a variable is defined, it is not initialized We must initialize any variable
requiring prescribed data when the function starts.
One final point about initializing variables when they are defined:
Although the practice is convenient and saves you a line of code, it also can
lead to errors. It is better, therefore, to initialize the variable with an assign-
ment statement at the proper place in the body ol the code. This may take
another statement, but the efficiency of the resulting program is exactly the
same, and you will make fewer errors in vour code.
Results:
Welcome. This program adds
three numbers. Enter three numbers
in the form: nnn nnn nnn <return>
11 22 33
Program 2- 2 Analysis Study the style of this program carefully. First, note
how we start with a welcome
message.
Chapter 2 Introduction to the C Language 47
This program contains three different processes. First it reads three numbers. The
code to read the numbers includes the printed instructions and a read ( scanf ] statement.
The second process adds the three numbers. While this process consists of only a com-
ment and one statement, we separate it from the read process. This makes it easier for
the reader to follow the program. Finally, we print the result. Again, the print process is
separated from the calculate process by a blank line.
2.6 Constants
Constants are data values that cannot be changed during the execution of a
program. Like variables, constants have a type. In this section, we discuss
Boolean, character, integer, real, complex, and string constants.
Constant Representation
In this section, we show how we use symbols to represent constants In the .
next section, we show how we can code these constants in our program .
Boolean Constants
A Boolean data type can take only two values. Therefore, we expect that we
have only two symbols to represent a Boolean type. The values are true and
false. As we mentioned before, a Boolean value can have only one of the two
values: 0 ( false ) and 1 ( true ). We use the constant true or false in our program.
To do so, however, requires that we include the Boolean library; stdbool .h .
Character Constants
Character constants are enclosed between two single quotes ( apostrophes).
In addition to the character, we can also use a backslash ( \ ) before the char -
acter. The backslash is known as the escape character. It is used when the
character we need to represent does not have any graphic associated with it —
that is, when it cannot he printed or when it cannot he entered from the key-
hoard. The escape character says that what follows is not the normal charac -
ter hut something else. For example, ' \ n ' represents the newline character
( line feed ). So, even though there may he multiple symbols in the character
constant , they always represent only one character.
L'x
48 Section 2.6 Constants
the character set
constant comes from
The character in .the character . Most computers use the ASCII char-
nufacturer
supplied by the hardware ma calied , the ASCII alphabet. The ASC II charac-
’
Integer Constants
Although integers arc always stored in their binary form , they are simply
coded as we would use them in everyday life. Thus, the value 1 5 is simply
coded as 15.
II we code the number as a series ol digits, its type is signed integer, or
signc ong integer il the number is large. We can override this default by
specifying unsigned ( u or u ) , and long ( l or L ) or long long ( n or LL ) , after
the number I he codes may be combined and may be coded in any order.
. ote that there is no way to specify a short int constant. When we omit the
.
sulhx on a hterai , lt defaults to int While both upper- and lowercase codes
r C0 1men l lal ou always use uppercase to avoid confusion
*
( esnechllv
^" >
able 2- / shows several examples of integer
typical lor a personal computer.
constants The default types are
Chapter 2 Introduction to the C Language 49
0. 0.0 double
.0 0.0 double
2.0 2.0 double
3.1416 3.1416 double
-2 .Of -2.0 float
3.1415926536 L 3.1415926536 long double
Complex Constants
Although we do not discuss the imaginary constants, we need to talk about
complex constants that are widely used in engineering.
Complex constants are coded as two parts, the real part and the imagi -
nary part, separated by a plus sign. The real part is coded using the real
lor -
( ) the
mat rules. The imaginary part is coded as a real number times *
.h ) is
imaginary constant ( _Complex _l). If the complex library ( complex
included, the imaginary constant can he abbreviated as I. Examples are
shown in Table 2 - 9 .
50 Section 2.6 Constants
Value Type
Representation
12.3 + 14.4 * I
,
12.3 + 14.4 * H ) /
2 double complex
1.4736L+ 4.56756L * I
1 2
1.4736 + 4.56756 * H ) / long double
complex
String Constants
A string constant is a sequence of zero or more characters enclosed in double
.
quotes You used a string in your first program without even knowing that it
-
was a string! Look at Program 2 1 to see if you can identify
the string.
Listed in Figure 2- 13 arc several strings, including the one from
Program 2 - 1 .
The first cxample, an empty string, is simply two double
quotes iin succes-
sion. The second example, a string containing only
the letter h, differs from a
character constant in that it is enclosed in double .
quotes When we study
strings, we will see that there is also a big
difference in how h is stored in
memory as a character and as string. I he last
wide characters.
example is a string that uses
it II
"h" // A n u l l string
"Hello World\n"
"HOW ARE YOU"
"Good Morning!"
L"This string
contains wide characters."
FIGURE 2 - 1 3 Some Strings
Chapter 2 Introduction to the C Language 51
At this point, this is all you need to know about strings. We talk more
about them and how they are stored in the computer when we study strings
in Chapter I I .
Use single quotes for character constants. Use double quotes for string
constants .
Coding Constants
In this section we discuss three different ways we code constants in our pro -
grams: literal constants, defined constants, and memory constants.
Literal Constants
A literal is an unnamed constant used to specify data. Ii we know that the
data cannot he changed, then we can simply code the data value itsell in a
statement.
Literals are coded as part of a statement using the constant formats
described in the previous section. For example, the literal 5 is used in the fol -
lowing statement.
a = b + 5;
Defined Constants
Another way to designate a constant is to use the preprocessor command
define. Like all preprocessor commands, it is prefaced with the pound sign ( # .
)
The define commands are usually placed at the beginning ol the program ,
although they are legal anywhere. Placing them at the beginning ol the pro -
gram makes them easy to find and change . A typical define command
might he
Memory Constants
The third wav to use a constant is with memory constants. Memory constants
use a C type qualifier, const , to indicate that the data cannot be changed . Its
format is:
We have seen how to define a variable, which does nothing more than
give a type and size to a named object in memory. Now let us assume that we
want to fix the contents of this memory location so that they cannot he
changed . This is the same concept as a literal , only now we give it a name.
The following code creates a memory constant , cPi . To help us remember
that it is a constant, we preface the identifier name with c .
^define
include <stdio.h>
PI 3.1415926536
continued
Chapter 2 Introduction to the C Language 53
2.7 Input/Output
Although our programs have implicitly shown how to print messages , we have
not formally discussed how we use C facilities to input and output data
. We
devote two chapters, Chapters 7 and 13, to fully explain the C input /output
facilities and how to use them . In this section , we describe simple input and
output formatting.
Streams
In C , data is input to and output from a stream . A stream is a source ol or
destination for data . It is associated with a physical device, such as a termi -
nal , or with a file stored in auxiliary memory.
C uses two forms of streams: text and binary. A text stream consists of a
sequence of characters divided into lines with each line terminated hv a new -
-
line ( \ n ). A binary stream consists of a sequence ol data values such as inte
ger, real , or complex using their memory representati on . In this chapter , we
briefly discuss only text streams. A more detailed discussion ol text streams is
found in Chapter 7 , “Text Input /Output ,“ and a detailed discussion of binary'
streams is found in Chapter 13, Binary Input /Output .
"
we
^
. In C ,
use produce or consume text streams
monitor is known as standard output .
dard input and the
keyboard
Formatting Input/Output
I he previous section discussed the terminal as a text stream source and desti -
nation . We can only receive text streams from a terminal ( keyboard ) and send
.
text streams to a terminal ( monitor ) However, these text streams often repre -
sent different data types, such as integer, real , and Boolean . The C language
provides two formatting functions: printj for output formatting and sccinf for
input formatting. The printf function converts data stored in the program
into a text stream for output to the monitor; the scan
text stream coming from the keyboard to data
/ function converts the
values and stores them in pro-
gram variables. In other words, the printf and
scanf functions are data to text
stream and text stream to data converters.
--
/ The print/ function takes a set of
data values, converts them to a text stream
,-
ncinn f
^T.
* • •
tained in a format control string, and 8 mStmCtl°nS con
sends he
standard output ( monitor ) . For example
m 1 a"
8
" f T
l
°
““* “ • / 352SS ix 5!35
Chapter 2 Introduction to the C Language 55
and then is sent to the monitor. What we see on the monitor is these three
characters, not the integer 234. However, we interpret the three characters
together as an integer value. Figure 2 - 16 shows the concept.
Basic Concept
The printf function uses an interesting design to convert data into text
streams. We describe how the text stream should he formatted using a format
control string containing zero or more conversion specifications. In addi-
tion to the conversion specifications, the control string may contain textual data
and control characters to be displayed.
Each data value to he formatted into the text stream is described as a sep -
arate conversion specification in the control string. The specifications describe
the data values’ type, size, and specific format information, such as how wide
the display width should he. The location of the conversion specification
within the format control string determines its position within the text stream.
The control string and data values are passed to the print function
( print/ ) as parameters, the control string as the first parameter and one
parameter for each value to be printed. In other words, we supply the follow-
ing information to the print function:
.
1 The format control string including any textual data to be inserted into
the text stream .
.
2 A set of zero or more data values to he formatted.
Figure 2 - 17 is a conceptional representation of the format control string
and two conversion specifications .
Figure 2 - 17 ( a ) shows the format string and the data values as parameters
for the print function. Within the control string we have specified quantity
( Qty : ) and total ( Tot : ) as textual data and two conversion specifications ( % d
and % f ). The first specification requires an integer type value; the second
requires a real type value. We discuss the conversion specifications in detail
in the following section.
Figure 2 - 17 ( h) shows the formatting operation and the resulting text
stream. The first data value is a literal integer ; the second data value is
the
contents of a variable named tot . I bis part of Figure 2 - 1 7 shows how
the
print function expands the control stream and inserts the data values and
/
text characters.
56 Section 2.7 Input /Output
~ ~
7r
723 sum ) ; sum 48.53
Iprintf( "Qty r ^rTot7 $ %F
~
/
A i —,
(b) Implementation
Conversion Specification
To insert data into the stream , we use a conversion specification that contains
a start token ( % ), a conversion code , and up to four optional modifiers as
-
shown in Figure 2 18. Only the field -specification token ( % ) and the conver-
sion code arc required.
integer ( d ) , and floating point ( f ) . These codes with some examples are
shown in Table 2 - 10.
char None c %c
short int h d % hd
int None d %d
long int None d % ld
float None f %f
double None f %f
long double L f % Lf
cate a long integer value; the 11 is used to indicate a long long integer value;
and the L is used with floating-point numbers to indicate a long double value.
A width modifier may be used to specify the minimum number of posi -
tions in the output. ( If the data require using more space than we allow, then
print ] overrides the width .) It is very useful to align output in columns, such
as when we need to print a column of numbers. II we don ’t use a width mod -
ifier, each output value will take just enough room lor the data.
If a floating- point number is being printed , then we may specif ) the num -
ber of decimal places to he printed with the precision modifier. The preci -
sion modifier has the format
.m
5. The h code is a carry over from assembler language where it meant hall word .
-
58 Section 2.7 Input/ Output
a maximum value
of 9999.99. Some examples of
of % 7.2 f is designed to print are shown below .
width specifications and precision
2 print positions
% 2 hd // short integer-
positions
% 4d // integer- 4 print
81 ) positions
% 81d // long int-print positions
8 ( not
: nnnn.dd
% 7.2 f // float 7 -
// long double-10 positions
: nnnnnn.ddd
% 10.3Lf
Output Examples
This section contains several output examples. We show the printf statement,
followed by what would be printed. Cover up the solution and try to predict
the results.
1. printf( "%d %c%f " , 23 , 'z', 4.1);
23z4.100000
Note that because there are no spaces between the conversion speci-
fications, the data are formatted without spaces between the values.
2. printf( "%d %c %f " , 23, 'z * , 4.1 );
23 z 4.100000
23 z 4.100000
23 Z 14.2
107 A 53.6
1754 F 122.0
3 P 0.1
Since there are no spaces before and after the format code ( % d), the
number 23 is run together with the text before and alter .
60 Section 2.7 Input /Output
—
is %6d " , 23);
6. printf("The number
23
.
The number is
A A A A
A A A A A
A A A A A
A A A A A A
H you count
co spaces fully . 1
It
J comes from the space
,
after is and before
t tbf "
the format » rinE
conversion specification .
. 11« «1» fou contc from the width in the
This example uses the zero ( lag to print leading zeros. Note that the
width is eight positions. I hree of these positions are taken up by the pre-
cision oi two digits and the decimal point. This leaves live positions tor
the integral portion of the number. Since there are only three digits
( 233 ) , printf inserts two leading zeros.
10. printf( "\"%8c %d \"" , 'h' , 23 );
h 23"
A A A A A A A A A A A A A A A
This example uses multiple Hags. So that we can see the justification,
each value is enclosed in vertical bars. The first value is printed left justi-
fied with the positive flag set. The second example uses zero fill with a
space for the sign. Note that there is a leading space in the output. I bis
represents the plus value. It is then followed hv the leading zeros. The last
example demonstrates that the zero fill is ignored when a numeric value
is printed with left justification.
44 55 0
This example has three conversion specifications hut only two values.
2. printf ( "%d % d \n" , 44 , 55 , 66);
44 55
62 Section 2.7 Input/Output
specifications with three values. In
This example has two conversion .
value
this case , printf ignores the third
.
3 float x = 123.45 ;
d\n" , x );
printf( "The data are: %
Data Source
"
r B 18.23 1
(" Text Stream
^ » scanf (...)
'
Data Values
Ll
18.23 B
price code
L — Discarded —-
(b) Implementation
Conversion Specification
To format data from the input stream , we use a conversion specification
that contains a start token ( % ) , a conversion code , and up to three optional
modifiers as shown in Figure 2 - 21 . Only the field -specification token ( % ) and
the conversion code are required .
Maximum
Flag Size
Width
I here are only three differences between the conversion codes for input
loi matting and output lormatting. First , there is no precision in an input con -
\ ersion specification . It is an error to
include a precision; if scanif finds a pre-
cision it stops processing and the input stream is in the
error state .
There is only one f° r input lormatting, the assignment suppression
Hag ( * ). More commonly associated with text files (
see Chapter 7 ) , the assign -
ment suppression flag tells scan that the next
/
stored . It is discarded . The following scan
input field is to be read but not
acter, and a floating- point number from
/ statement reads an integer, a char-
the input stream . The character is
read and discarded. The other fields are
read ,
here is no matching address parameter for formatted and stored . Note
,
the data to be discarded .
s c a n f ( " %d % * c % f " , sx , Sy);
Chapter 2 Introduction to the C Language 65
Input Parameters
For every' conversion specification there must he a matching variable in the
address list . The address list contains the address of the matching variable.
How do we specify' an address ? It ’s quite simple: Addresses are indicated by
prefixing the variable name with an ampersand ( & ). In C , the ampersand is
known as the address operator. Using the address operator, if the variable
name is price , then the address is & price . Forgetting the ampersand is one
of the most common errors for beginning C programmers, so you will have to
concentrate on it when you use the scanf function .
Remember that the first conversion specification matches the first vari-
able address , the second conversion specification matches the second variable
address, and so on . This correspondence is very' important . It is also very
important that the variable’s type match the conversion specification type.
The C compiler does not verify that they match. If they don ’t , the input data
will not he properly formatted when they are stored in the variable.
.
a End of file is reached .
has been processed .
b. The maximum number of characters
after a digit in a numeric specification.
c. A whitespace character is found
d. An error is detected.
2 . There must be a conversion specification
for each variable to be read .
type for each conversion
3. There must be a variable address of the proper
specification .
4. Any character in the format string other than whitespa
ce or a conver-
sion specification must be exactly matched by the user during input. If
the input stream does not match the character specified , an error is sig-
naled and scanf stops.
5 . It is a fatal error to end the format string with a whitespace character.
Your program will not run correctly if you do.
Input Examples
This section contains several examples. We list the data that will be input
first . T his allows you to cover up the function and try to formulate your own
scan! statement.
1 . 214 156 14 Z
Note that if there were a space between the 14 and the Z . it would
create an error because % c does not skip whitespace! To prevent this prob
lem , put a space before the % c code as shown below. This will cause it to
-
skip leading whitespace.
2 . 2314 15 2.14
3. 14/26 25/66
Note the slashes ( / ) in the format string. Since they are not a part ol
the conversion specification, the user must enter them exactly as shown
or sccitif will stop reading.
4. 11-25-56
- -
scanf ( "%d %d %d " , &a , & b , &c );
Again, we see some required user input, this time dashes between
the month, day, and year. While this is a common date format, it can
.
cause problems A better solution would be to prompt the user separately
for the month, the day, and the year.
234 ( Input )
0 ( Output )
This example has no address token on the variable ( & a ). II the pro -
gram runs at all, the data are read into an unidentified area in memory.
What is printed is the original contents of the variable, in this case 0.
2. float a = 2.1 ;
scanf ( "%5.2f " , & a );
printf ("%5.2f" , a ) ;
74.35 ( Input )
2.10 ( Output )
4. int a = 1
int b 2=
int c 3= &c ) i
scanf ( "%d%d" , &a t & b /
printf ( "%d %d\n", a , b
, c);
5 10 15 ( input)
5 10 3 (output)
continued
Chapter 2 Introduction to the C Language 69
" Nothing!"
Results:
The Boolean values are: 1 0
continued
Chapter 2 Introduction to the C Language 71
Results:
ASCII for character 'A' is: 65
ASCII for character 'a ' is: 97
ASCII for character 'B ' is: 66
ASCII for character ' b' is: 98
ASCII for character 'Z' is: 90
ASCII for character 'z' is: 122
ASCII for character ' 0' is: 48
ASCII for character '8' is: 56
ASCII for character ' \n ' is: 10
ASCII for character '\t' is: 9
ASCII for character '\v' is: 11
ASCII for character ' ' is: 32
ASCII for character ' \a ' is: 7
ASCII for character H i
is: 34
ASCII for character * \' is: 92
ASCII for character i i i
is: 39
25
26
27 return 0 ;
28 } // main
Results:
: 23
Please enter the value of the radius
Radius is : 23.00
Circumference is : 144.51
Area is : 1661.91
The report contains four fields: a part number, which must he printed
with leading zeros; the current quantity on hand; the current quantity on
order; and the price of the item, printed to two decimal points. All data
should he aligned in columns with captions indicating the type of data in
each column. The report should he closed with an “ End of Report message.
'
Program 2 - 8 Analysis There are a few things about Program 2 - 8 that you should note. First, it is fully docu
-
mented. Professional programmers often ignore documentatio n on "one - time - only"
programs, thinking they will throw them away, only to find that they end up using
them over and over. It only takes a few minutes to document a program, and it is
always time well spent. If nothing else, it helps clarify the program in your mind.
Next, look carefully at the formatting for the print statements . Spacing is controlled
by a combination of tabs and format code widths. The double spacing for the end of
/4 Section 2.8 Programming Examples
a newline command \ n
( ) at the beginning of
report message is controlled by placing
.
the message in Statement 29. nrlnrU with a return statement that informs the
-
I ^
»’EEd
good programmer.
^ "
m !mo pr -
°
grams, is the sign of a
Imaginary
^ Argument
Real
y
As the figure shows, the absolute value of the complex a + b * I can be found
as ( a + b ) 1 /2 . The argument can be found as arctan ( b/ ) .
a The conjugate of a
complex number is another complex number defined as a - b * .
I
Program 2 -9 shows how we print the different attributes
of a complex
number using the predefined functions creal , cimag ,
cabs and carg .
,
continued
Chapter 2 Introduction to the C Language 75
Results:
4.000000 4.000000 5.656854 0.785398
-
4.000000 4.000000 5.656854 -0.785398
18
19 // statements
20 sum = x + y ;
21
22
dif = x -
y;
mul = x * y;
23 div = x / y ;
24
25 printf("%f %f %f %f\n" , creal(sum), cimag(sum ),
26 cabs(sum), carg( sum ));
27 printf( ” %f %f %f %f\n", creal(dif ), cimag(dif ),
28 cabs(dif), carg( dif ));
29 printf("%f %f %f %f \n" , creal(mul), cimag( mul ),
30 cabs( mul), carg( mul ));
31 printf("%f %f %f %f\n ” creal(div ),
/
cimag( div ),
32 cabs(div), carg(div ));
33 return 0;
34 > // main
Results:
6.000000 0.000000 6.000000 0.000000
0.000000 8.000000 8.000000 1.570796
25.000000 0.000000 25.000000 0.000000
-0.280000 0.960000 1.000000 1.854590
J
Chapter 2 Introduction to the C Language 77
Program Documentation
There are two levels of program documentation. The first is the general docu -
mentation at the start of the program. The second level is found within each
function.
General Documentation
. Each
Program 2 - 1 1 shows what we recommend lor program documentation
program should start with a general description of the program . Followin g the
of the author and the date the program was
general description is the name
's change history, which documents
written. Following the date is the program
the reason and authority for all changes. For a production program whose
use
Module Documentation
Whenever necessary, we include a brief comment lor blocks of
code. A block
of code is much like a paragraph in report
a . It contains one thought — that is,
one sel of statements that accomp lish a specific task. Blocks ol code in our
ed by blank program lines , just as we skip blank lines
program are separat
between paragraphs in reports.
nt,
If the block of code is difficult, or if the logic is especially significa
line — descripti on ol the blocks
then we give the reader a short —one- or two -
purpose and/or operation. We will provide many example
s of this type ol doc -
umentation through out the text .
be docu -
Sometimes a textbook suggests that each variable in a program
mented. We disagree with this approach. First , the proper location tor vari-
able documentation is in a data dictionar y. A data dictiona ry is a system
78 Section 2.9 Software Engineerii
document
's.iT,“r “ * « " •
need for the comment .
.
tool W
» »
— -- — ,
» p
"ante
,
""8
«1
, , . . -
keeping names short , the ad van
. .
but their real names are length and width. Therefore , your program
should call the sides of the rectangle length and width . These names
are commonly used by anyone describing a rectangle.
2 . When necessary for readability, and to separate similar variables from
each other, combine terms to form a variable name.
Suppose that you are working on a project to compute a payroll.
I here are many different types of taxes. Each of the different taxes
should he clearly distinguished from the others by good data names.
Table 2- 13 shows both good and bad names for this programming situa -
tion . Most of the poor names are either too abbreviated to be meaningful
( such as ftr ) or are generic names ( such as
rate ) that could apply to
many different pieces of data .
Note the two different concepts for separating the words in a vari -
ables name demonstrated in Table 2- 13. In the first example, we capital-
ized the first letter of each word . In the second example, we separated the
words with an underscore . Both are good techniques for making a com -
pound name readable. If you use capitalization , keep in mind that C is
case sensitive , so you must he careful to use the same cases for the name
each time you use it .
.
3 Do not create variable names that are different by only one or two
. Names
letters,
that are
especially if the differences are at the end of the word
too similar create confusion . On the other hand , a naming pattern makes
it easier to recall the names. This is especially true when user terminol -
ogy is being used. Thus , we see that the good names in Table 2 - 13
all
start with fica .
4. Abbreviations, when used , should clearly indicate the word
being
abbreviated .
Table 2- 13 also contains several examples of good abbreviations.
Whenever possible, use abbreviations created by the users. They will
often have a glossary of abbreviations and acronyms that they use.
Short words are usually not abbreviated . II they are short in the first
place, they don ’t need to be made shorter.
5. Avoid the use of generic names.
Generic names are programming or user jargon . For example, count’
and sum are both generic names. They tell you their purpose but don t
give you any clue as to the type ol data they are associated with. Better
names would be emplyCnt and ficaSum. Programmers are especially
fond of using generic names , but they tend to make the program confus -
ing. Several of the poor names in Table 2 - 13 arc generic.
6 . Use memory constants or defined constants rather than literals lor values
that are hard to read or that might change from system to system .
Some constants are nearly impossible to read . We pointed out the
lor it .
space earlier. If you need a space often , create a defined constant
Table 2 - 14 contains several example s of constants that are better when
coded as defined constants.
Data Hiding
of
In “ Structure of a C Program ” in Section 2.2 , we discussed the concept
before main
global and local variables. We pointed out that anything placed
80 Section 2.9 Software Engineering
. . .
requtre
, if a part of your program require data do , ts
to
data . In other words
the data Unt l you learn to use
job it shouldn 't be able to see or modify
, you will not be able to prov de th s data-
functions in Chapter 4, however
hiding capability.
Nevertheless, you should start your programming
with good practices .
structur ed program ming, we now
And since our ultimate objective is good
formulate our first programming standard :
Programming Standard
.
No variables are to be placed in the global area of a program
Any variables placed in the global area of your program — that is, before
main an be used and changed by every part of your program . This is unde-
sirable and is in direct conflict with the structured programming principles of
data hiding and data encapsulation .
Chapter 2 Introduction to the C Language 81
a. If you forget the parentheses after main , you will get a compile error.
.
b If you put a semicolon after the parentheses, you will get a compile error.
c. If you misspell main you will not get a compile error, but you will get
an error when you try to link the program . All programs must have a
function named main .
3. If you forget to close the format string in the scanj or printj statement,
you will get a compile error.
4. Using an incorrect conversion code for the data type being read or writ -
ten is a run - time error. You can ’t read an integer with afloat conversion
code. Your program will compile with this error, but it won 't run correctly.
3. Not separating read and write parameters with commas is a compile error.
6. Forgetting the comma after the format string in a read or write statement
is a compile error.
7. Not terminating a block comment with a close token ( * / ) is a compile error.
8. Not including required libraries, such as stilio . h , at the beginning of your
program is an error. Your program may compile, but the linker cannot
find the required functions in the system library.
9. If you misspell the name of a function , you will get an error when you
link the program . For example, if you misspell scanj or printj , your pro-
gram will compile without errors , but you will get a linker error. Using
the wrong case is a form of spelling error. For example, each of the fol -
lowing function names are different:
10. Forgetting the address operator ( & ) on a scanf parameter is a logic ( run -
time ) error.
I 1 . Do not use commas or other characters in the format string lor a scanf
statement . This will most likely lead to a run - time error when the user
does not enter matching commas or characters. For example, the comma
in the following statement will create a run - time problem if the user
doesn ’t enter it exactly as coded .
2.12 Summary
J In 1972. Dennis Ritchie designed C at Bell Laboratories.
° l '?!?
" the Ameritan National Standards Institute ( ANSI ) approved
ANSI C ;’.in 1990, the ISO standard was approved ,
fhe basic component of a C program is the function .
Every C function iis made of declarations,
statements.
,
definitions and one or more
The floating-point type is further divided into real, imaginary, and complex
.
,
A real number is a number with a fraction. It has three sizes: float
double , and long double.
The imaginary type represents the imaginary part of a complex number
.
It has three sizes, float imaginary , double imaginary , and long double
imaginary .
The complex type contains a real and an imaginary part. C uses three
complex sizes: float complex , double complex , and long double complex
.
A constant is data whose value cannot be changed.
com-
Constants can be coded in three different ways: as literals, as define
mands, and as memory constants .
Variables are named areas of memory used to hold data.
in C.
IJ Variables must be declared and defined before being used
To input data through the keyboard and to output data through
the moni -
tor, use the standard formatted input /output functions.
data through the
scanf is a standard input function for inputting formatted
keyboard.
d data the
IJ printf is a standard output function for outputting formatte
to
monitor .
the reader
As necessary, programs should contain comments that provide
with in-line documentation lor blocks ol code.
and understand.
Programs that use “intelligent names are easier to read
"
2.13 Practice Sets
Review Questions is to store a programs
file, such as stdio.h ,
1. The purpose of a header
source code.
a. True
b. False
can he used in an identifier.
2. Any valid printable ASCII character
a. True
b. False
data from the keyboard is printf .
3. The C standard function that receives
a. True
b. False
the structure of a C program
4. Which of the following statements about
is false?
section.
a. A C program starts with a global declaration
b. Declaration sections contain instructio ns to the .
computer
c. Every program must have at least one function .
d . One and only one function may be named main .
e. Within each function there is a local declaration section .
5. Which of the following statements about block comments is false ?
a . Comments are internal documentation for programmers.
b. Comments are used by the preprocessor to help format the program .
c. Comments begin with a / * token .
.
d Comments cannot he nested .
e. Comments end with a * / token .
.
6 Which of the following identifiers is not valid ?
a . _option
.
b amount
c. sales_amount
d . salesAmount
e. $salesAmount
/ . W Inch ol the following is not a data type?
a. char
b. float
.
c int
d . logical
e. void
Chapter 2 Introduction to the C Language 85
(
8. The code that establishes the original value for a variable is known as a n ) :
a. assignment
b. constant
c. initializer
d . originator
e. value
9. Which of the following statements about a constant is true?
a. Character constants are coded using double quotes ( " ) •
b. It is impossible to tell the computer that a constant should he a float
or a long double .
c. Like variables, constants have a type and may he named .
d . Only integer values can be used in a constant .
e. The value of a constant may be changed during a program s execution .
10. The conversion specification is used to read or write
a short integer.
a. % c
b. %d
.
c %f
d . % hd
e. % lf
1 1 . To print data left justified , you would use a in the
conversion specification .
.
a flag
.
b precision
c. size
d. width
e. width and precision
12. The function reads data from the kevboard .
a. displayf
b. printf
.
c read
d . scanj
e. write
13. One of the most common errors for new programmers is forgetting to use
the address operator for variables in a scanf statement . What is the
address operator ?
a . The address modifier ( @ ) in the conversion specification
b. The ampersand ( & )
c. The caret ( * )
d . The percent ( % )
e. The pound sign ( # )
86 Section 2.13 Practice Sets
Exercises in C ?
a character constant
14. Which of the following is not
a. 'C *
b. 'bb '
c. "C"
d. ' ?
e.
an integer constant in C?
15. Which of the following is not
a. -320
b. +45
c. -31.80
d. 1456
e. 2, 456
-
16. Which of the following is not a floating point
constant in C?
a. 45.6
b. -14.05
c. ' a '
d. pi
e. 40
17. What is the type of each of the following constants?
.
a 15
b. -14.24
.
c ' b'
d. "l"
e. " 16 ”
18. Which of the following is not a valid identifier in C ?
a. A3
I). 4 A
c. if
d. IF
e. tax-rate
19. What is the type of each of the following constants?
a. "7 "
b. 3
.
C " 3.14159 "
d. '2'
e. 5.1
20. What is the type of each of the
following constants?
a. " Hello"
I). 15L
C. 8.5 L
d. 8 . 5 f
e. '\a '
Chapter 2 Introduction to the C Language 87
.
21 Which of the following identifiers are valid and which are invalid ?
Explain your answer.
a. num
b. num2
c. 2dNum
_
d . 2d num
e. num# 2
.
22 Which of the following identifiers are valid and which are invalid ?
Explain your answer.
a. num-2
b. num 2
c. num 2
d. num2
e. num 2
23. What is output from the following program fragment ? To show your out -
.
put draw a grid of at least 8 lines with at least I 3 characters per line.
// Local Declarations
int x = 10;
char w = 'Y';
float z = 5.1234 ;
// Statements
printf( "\nFirst\nExample\ n:" );
printf( " %5d \n , w is %c\n" , x , w );
printf( "\nz is %8.2f \n" , z );
# include ( stdio.h )
int main ( void )
{
print ( "Hello World " );
return 0 ;
{
Section 2.13 Practice Sets
include <stdio>
int main (void )
{ correct ' );
printf('We are to learn');
here
printf('C language
return 0;
} // main
.
.
27 Find any errors in the following
program
// Statements
printf("The end of the program." );
return 0;
} // main
continued
Chapter 2 Introduction to the C Language 89
Problems
.
30 Code the variable declarations for each of the following:
a. a character variable named option
b. an integer variable, sum, initialized to 0
c. a floating-point variable, product , initialized to I
.
3 1 Code the variable declarations for each ol the following:
.
a a short integer variable named code
b. a constant named salesTax initialized to .0825
c. a floating-point named sum of size double initialized to 0
.
32 Write a statement to print the following line. Assume the total value is
contained in a variable named cost.
.
33 Write a program that uses four print statements to print the pattern of
asterisks shown below.
******
******
******
******
.
34 Write a program that uses four print statements to print the pattern of
asterisks shown below.
*
**
***
****
in the alpha-
that uses defined constants for the vowels
( 0 , 2 , 4 , 6, 8 ). It
35. Write a program decimal digits
memory constants for the even inp constants for the odd
bet and lines using literal
then printsthe following three
digits.
a e i o u
2 4 6 8
0
Projects
39. Write a C program using printf statements to print the three f irst letters
of your first name in big blocks. This program does not read anything
from the keyboard. Each letter is formed using seven rows and five col-
umns using the letter itself . For example , the letter B is formed using 17
Bs, as shown below as part of the initials BEF.
l his is just an example. Your program must the first three let -
print
ters ol your first name. Design your print
/ statements carefully to create
Chapter 2 Introduction to the C Language 91
enough blank lines at the beginning and end to make your initials read-
able. Use comments in your program to enhance readability as shown in
this chapter.
40. Write a program that reads a character, an integer, and a floating-point
.
number It then prints the character, first using a character format speci -
fication ( % c ) and then using an integer specification ( % d ) . After printing
the character, it prints the integer and floating-point numbers on separate
lines. Be sure to provide complete instructions (prompts) lor the user.
41. Write a program that prompts the user to enter three numbers and
then
prints them vertically ( each on one line ) , first forward and then reversed
( the last one first), as shown in the following design.
42 Write a program that reads 10 integers and prints the first and the last on
.
one line, the second and the ninth on the next line, the third and the sev-
enth on the next line, and so forth. Sample input and the results are
shown below.
43. Write aprogram that reads nine integers and prints them three in a line
separated by commas as shown below.
Input:
10 31 2 73 24 65 6 87 18
Output
10 , 31 , 2
73 , 24, 65
6 , 87, 18
Structure of a C Program
-
Two features set the C language apart from many other languages: expres
heart oi the lan -
sions and pointers. Both of these concepts lie at the very
guage , giving C its unique look and feel .
This chapter explores the first of these concepts: expressions. Expres-
sions are not new to you ; you have used them in mathematics.
However, C’s
use of expressions is unique to the C language.
Closely tied to the concept of expressions are operators, precedence and
associativity, and statements, all of which are discussed in this chapter
. This
chapter also introduces a concept known as side effects and explains in detail
how it affects statements in C .
Objectives
To be able to list and describe the six expression categories
To understand the rules of precedence and associativity in evaluat
ing
expressions
To understand the result of side effects in expression evaluation
To be able to predict the results when an expression is evaluated
To understand implicit and explicit type conversion
To understand and use the first four statement types: null, expression
,
return, and compound
93
Section 3.1 Expressions
° Lee;:I:whose
PeA:eTpLs
^ ssion contains 'nly one o
.
operator. For example 2 + S is a
, a s mplc express,on . A
simple value is 7; similarly, -a
plex expression is 2 + 5 * 7. To
expressions .
that one operator. An example of a com -
complex expression contains more a complex express,on , we reduce to
evaluate
In the previous example we first evaluate
. the
a series of simple 2 + 3 % gmng a result
then the expression
simple expression 5 * 7 (35) and
of 37. are clearly specified in the
Every language has operators whose actions
language syntax. The order in which
the operators in a complex expression
of known as precedence; the
are evaluated is determined by a set priorities
the expression containing the operator is
higher the precedence, the earlier
the front cover contains the precedence of each
evaluated . The table inside
operator. Looking at that table, we see that in
the expression 2 + 5 * 7, multi-
is eval -
plication has a higher priority than addition the multiply expression
so
detail in the next section .
uated first. We discuss precedence in more
If two operators with the same precedence occur in a complex expression,
another attribute of an operator, its associativity, takes control. Associativity
is the parsing direction used to evaluate an expression . It can be either lelt - to-
right or right- to- left . When two operators with the same precedence occur in
-
an expression and their associativity is left to- right, the left operator is evalu -
ated first . For example, in the expression 3 * 4 / 6, there are two operators,
multiplication and division , with the same precedence and left -to- right asso-
ciativity. Therefore, the multiplication is evaluated before the division . We
also discuss associativity in more detail in the next section .
We can divide simple expressions into six categories based on the number
ol operands, relative positions of the operand and operator, and the prece-
dence of operator. Figure 3- 1 shows the categories.
Expression
Categories
Primary Expressions
The most elementary type of expression is a primary expression . A primary
expression consists of only one operand with no operator. In C , the operand
in the primary expression can he a name, a constant, or a parenthesized
expression . Although a primary expression has no operator, the null operator
in this expression has the precedence of 16 according to the table ol
prece -
dence. In other words , a primary expression is evaluated first in a complex
expression .
Names
the
A name is any identifier for a variable , a function , or any other object in
language. The following are examples of some names used as primary
expressions:
Literal Constants
The second type of primary expression is the literal constant . As discussed in
Chapter 2 , a constant is a piece of data whose value can t change during the
’
Parenthetical Expressions
The third type of primary expression is the parenthetical expression. Any
value enclosed in parentheses must be reducible to a single value and is
therefore a primary expression . I bis includes any of the complex expressions
when they are enclosed in parentheses. Thus, a complex expression can be
enclosed in parentheses to make it a primary expression . The following are
primary expressions:
* 3 + 4) (a = 23 b * 6)
(2
Postfix Expressions
The postfix expression consists of one operand followed by one operator
. Its
category is shown in Figure 3-2 . There are several operators
that create a
table . We discuss only
postfix expression as you can see in the precedence .
three of them here: function call , postfix increment , and postfix decrement
96 Section 3.1 Expressions
Operand Operator
Function Coll
We have already
. In the hello world program , we
the printf function. Function calls are
wrote a message on
postfix expressions. The function name is the
parentheses that follow the name. The
or be empty. When present , the
operand and the operator is the
parentheses
arguments are part of
may conta n arguments
the operator.
.
Postfix Increment/Decrement
The postfix increment and postfix decrement are also postfix operators Virtu -
,
ally all programs require somewhere in their code that the \ ilue 1 be added
«
Q value of expression is a
x — a+ +
0 value of a is incremented by 1
a = a +l
FIGURE 3 - 3 Result of Postfi x a + +
Chapter 3 Structure of aiCProgram 97
—
the postfix decrement ( a ) also has a value and a side effect . As with
the increment , the value of the expression is the value of a before the decre-
ment ; the side effect is the variable is decremented by I .
Prefix Expressions
In prefix expressions , the operator comes before the operand as seen in
Figure 3 - 4 .
^ ^
( OperatorJ ) Operand
Variable
a = a + 1
Q value of a is increment by 1
X = ++ a
The effect of both the postfix and prefix increment is the same: The vari -
able is incremented by 1 . If we don 't need the value of the expression — that
is, if all we need is the effect of incrementing the value of a variable by I
then it makes no difference which one we use. You will find that programmers
—
use the postfix increment and decrement more often , if for no other reason
than that the variable is shown first and is therefore easier to read .
continuei
Chapter 3 Structure of a C Program 99
Results:
value of a 4
value of ++a 5
new value of a: 5
Program 3 - 2 Analysis The only difference in the printouts between Program 3- 1 and Program 3 - 2 is the use
of the increment operators. The first program uses the postfix increment; the second
uses the unary prefix increment. In both cases, we start with the same value for a and
it has the same value at the end. But the value of the expression itself is different. To
help remember the difference, use this rule: If the ++ is before the operand, the incre-
ment takes place before the expression is evaluated; if it is after the operand, the
increment takes place after the expression is evaluated.
If ++ is after the operand, as in a++, the increment takes place after the
expression is evaluated. If ++ is before the operand , as in ++a, the increment
takes place before the expression is evaluated .
Unary Expressions
A unary expression , like a prefix expression , consist ol one operator and one
operand . Also like the prefix expression , the operator comes before the oper-
and . Although prefix expressions and unary expressions look the same, they
belong to different expression categories because the prefix expression needs
a variable as the operand while the unary expression can have an expression
or a variable as the operand . Many of the unary expressions are also familiar
to you from mathematics and will require little explanation . In this chapter
we discuss the sizeof operator, the plus/minus operators, and the cast opera -
tor. The others will be discussed in later chapters. The format of the unary
expressions is demonstrated in Figure 3-6.
100 Section 3.1 Expressions
Operator
Operand
. , ,, of
.. .
typ« o a prtmary expres-
.... - , “ “ *“'“.‘ .I
tells us the sfte, 1« byte
s”I
.
'
m
pdnt On
.
The sixeof operator
“Specifying
po« W « he
some
t
the «» «f
*hardwa
d rin "le' wrll rllus rare<The
" °ki re. A simple examp
of the m eg r type 2 by
personal comparers, rhe sire the very large supercomputers,
, i is 4 bytes. On
On ,,
'
1
some mainframe compoters
it can be as large as 16 bytes
know the exact size (in
. If it is important to with the integer type as
use the sizeof operator
bytes) of an integer, we can
shown below.
sizeof (int)
It is also possible to find the size of a primary expression. I lere are two
examples.
Unary Plus/Minus
The unary plus and unary minus operators are what we think ol as
simply the
plus and minus signs . In C , however , they are actually operators . Because
, they can he used to compute the arithme tic value of an
they are operators
operand.
The plus operator does not change the value of the expression. II the
expression’s value is negative, it remains negative; if the expression’s value is
positive, it remains positive.
The minus operator changes the sign of a value algebraically— that is, to
change it from plus to minus or minus to plus. Note, however, that the value
ol the stored variable is unchanged. The operation of these operators is seen
in Table 3 1. -
Expression Contents of a Before Expression
ond After Expression Value
+a 3 +3
-a 3 -3
continued
TABLE 3 - 1 Examples of Unary Plus And Minus Expressions
Chapter 3 Structure of a C Program J 01
-5 -5
+a
-5 +5
-a
TABLE 3 - 1 Examples of Unary Plus And Minus Expressions (continued
)
Cast Operator
The third unary operator we discuss in this chapter is cast . The cast operator
converts one expression type to another. For example to
, convert an integer to
a real numbe r, we would use the followin g unary express ion.
( float ) x
Binary Expressions
a-
Binary expressions are formed by an operand-operator -operand combin
tion. T hey are perhaps the most common expression category
. Any two num -
bers added, subtracted, multiplied, or divided are usually formed in algebra ic
notation, which is a binary expression. There are many binary express
ions.
We cover the first two in this chapte r. Figure 3 - 7 shows the format of a binary
expression.
Operand I
^ ^
( OperatoT ) Operand
I
FIGURE 3 - 7 Binary Expressions
Multiplicative Expressions
The first binary expression we study, multiplicative expressions
, which take
its name from the first operator, include the multiply
, divide , and modulus
( 1 3 ) among the binary
operators. These operators have the highest priority
operators and are therefore evaluat ed first among them .
ds.
The result of a multiply operator ( * ) is the product of the two operan
The operands can he any arithmetic type ( integral or floating point
- ) . The type
in the chapter.
of the residt depends on the conversion rule that we discuss later
// evaluates t o 30
10 * 3 / / evaluates t o 4
true * 4 // evaluates to 130
A' * 2 // evaluates to 44.6
22.3 * 2
,,
o II P "r
' * 7'; I*
one or j
quotient • H
•'of the"division
w <«examples
«• ,
. ‘"
'
the quotient. The following shows some operator
// evaluates to 3
10 / 3 // evaluates to 0
true / 4 to 32
// evaluates
•A' / 2 to 11.15
// evaluates
22.3 / 2
with the
Multiply and divide are well known , hut you may not be familiar
modulus operator ( % ) , more commonly known as modulo . I his operator
divides the first operand by the second and returns the remainder
rather than
the quotient . Both operands must be integral types and the operator returns
the remainder as an integer type . The following examples demonstrate the
modulo operator .
10 % 3 // evaluates to 1
true % 4 // evaluates to 1
• A ' % 10 // evaluates to 5
22.3 % 2 // -
Error : Modulo cannot be f l o a t i n g point
Because the division and modulus operators are related , they are olten
confused. Remember: The value of an expression with the division operator is
the quotient ; the value of a modulus operator is the remainder. Study the
effect of these two operators in the following expressions:
3 / 5 / / evaluates to 0
3 % 5 / / evaluates to 3
3 / 7
3 % 7 / / evaluates to 0
/ / evaluates to 3
Additive Expressions
In additive expressions, the second operand is added to or subtracted
from
the first operand, depending on the operator used. I lie operands in an addi -
3 + 7 // evaluates to 10
3 7- // evaluates to 4 -
(continuedJ
PROGRAM 3- 3 Binary Expressions
28 return 0;
29 > // main
Results:
integral calculations
17 + 5 = 22
17 5 = 12
17 * 5 = 85
17 / 5 = 3
17 % 5 = 2
Assignment Expressions
The assignment expression evaluates the operand on the right side of the
operator ( = ) and places its value in the variable on the left . The assignment
expression has a value and a side effect .
• The value of the total expression is the value of the expression on the
right of the assignment operator ( = ).
• I he side effect places the expression value in the variable on the left of
the assignment operator.
Simple Assignment
Simple assignment is found iin algebraic expressions
. Three examples of sim -
pie assignments are shown below !
a = 5 b = x + l
i = i + 1
Chapter 3 Structure of a C Program 105
Of course, for the effect to take place, the left variable must be able to
receive it; that is, it must he a variable, not a constant. II the left operand
cannot receive a value and we assign one to it, we get a compile error.
Compound Assignment
A compound assignment is a shorthand notation for a simple assignment It .
requires that the left operand he repeated as a part of the right expression.
Five compound assignment operators are discussed in this chapter: * =, / = , % =,
+=, and
To evaluate a compound assignment expression, first change it to a sim-
ple assignment, as shown in Table 3 - 2. Then perform the operation to deter -
mine the value of the expression.
x *= y + 3
is evaluated as
x x * (y + 3)
Assignments (continued)
PROGRAM 3 - 4 Demonstration of Compound
9 // Local Declarations
10 int x;
11 int y;
12
13 // statements
14 x = 10?
15 y = 5;
16
x, y ) ;
17 printf("x: %2d | y: %2d " ,
printf("|x *= y + 2: %2d " , x *= y + 2 );
18
19 printf( |
" x is now: %2d\n " , x ) ;
20
21 x = 10?
22 printf("x: %2d | y: %2d " , x , y )?
23 printf( " | x /= y + 1: %2d " , x /= y + 1)?
24 printf(" | x is now: %2d\n", x)?
25
26 x = 10?
27 printf( " x: %2d | y: %2d ", x, y ) ;
28 -
printf(" | x %% = y 3: %2d ", x %= y 3);
29 printf( " | x is now: %2d \n", x )?
30
31 return 0;
32 } // main
Results:
x: 10 | y 5 x *= y + 2: 70 x is now: 70
x: 10 I
y: 5 | x /= y + 1: 1 x is now: 1
x: 10 | y 5 | x %= y 3: 0 - I x is now: 0
Program 3 - 4 Analysis Note that we have used an assignment statement in the printf statements to demon-
strate that an assignment expression has a value . As we
said before, this is not good
programming style, but we use it here to match the format we used in
Do not hide calculations in print statements . Also, Program 3-3.
since we are changing the value of
x with each assignment, even though it
is in a printf statement, we need to reset it to
10 for each of the print series.
Precedence
The concept of precedence is well founded in mathematics. For example, in
algebra , multiplication and division are performed before addition and sub-
traction . C extends the concept to 16 levels , as shown in the Precedence
Iahle inside the front cover.
I he following is a simple example of precedence:
2 + 3 * 4
This expression is actually two binary expressions, with one addition and
one multiplication operator. Addition has a precedence ol 12 . Multiplication
has a precedence of 13. This results in the multiplication being done first ,
followed by the addition , as shown below in the same expression with the
default parentheses added. The value of the complete expression is 14 .
(2 + (3 * 4) ) —> 14
-b+ +
Two different operators are in this expression . Ihe first is the unary
minus the second is the postfix increment. The postfix increment has the
higher precedence ( 16 ) , so it is evaluated first . Then the unary minus , with a
precedence of 15 , is evaluated . To reflect the precedence, we have recoded
the expression using parentheses.
( - ( b+ + ) )
6
7 int main ( void)
8 {
9 // Local Declarations
10 int a = 10;
11 int b = 20;
12 int c 30; =
13
14 // Statements
+ c );
15 printf ("a * b + c is: %d\n" , a * b
: %d\n", a * (b + c) );
16 printf ("a * (b + c) is
17 return 0;
18 > // main
Results:
a * b + c is: 230
a * ( b + c) is: 500
Associativity
Associativity can be left - to- right or right - to- left. Left-to- right associativity
evaluates the expression by starting on the left and moving to the right . Con -
versely, right - to -left associativity evaluates the expression by proceeding
Irom the right to the left . Remember, however, that associativity is used only
when the operators all have the same precedence.
in an expression.
Left-to-right Associativity
The following shows an example of left- to
four operators of the same precedence ( -
right associativity. Here we have
* / % *) .
3 * 8 / 4 % 4
* 5
LL?V
Chapter 3 Structure of a C Program 109
(((( 3 * 8) / 4) % 4 ) * 5 )
3 8 / 4 % 4 * 5
Right-to-left Associativity
Several operators have right- to-left associativity as shown in the precedence
table. For example, when more than one assignment operator occurs in an
assignment expression, the assignment operators must he interpreted from
right to left. This means that the rightmost expression will he evaluated first ;
then its value will he assigned to the operand on the left of the assignment
operator and the next expression will he evaluated. Under these rules, the
expression
a + = b *= c -= 5
is evaluated as
(a += ( b *= (c -= 5 )))
which is expanded to
(a = a + (b = b * (c = c - 5)))
(a = 3 + (b = (5 * (c = 8 - 5 )))
L
110 Section 3.3 Side Effects
a = b = c = d = 0;
x = 4;
This simple expression has three parts. First, on the right ol the assign -
ment operator is a primary expression that has the value 4 . Second , the whole
expression ( x = 4 ) also has a value of 4 . And third , as a side effect , x receives
the value 4 .
Let 's modify the expression slightly and see the same three parts.
x = x + 4;
i n t x = 3;
——
p r i n t f ( " Step 1 Value of x: %d
p r i n t f ( " Step 2 Value of x
\n" , x ) ;
—
p r i n t f ( " Step 3 Value of x
a ++
Chapter 3 Structure of a C Program 111
As we saw earlier, the value of this expression is the value of a before the
expression is evaluated . As a side effect , however, the value of a is incre -
mented by 1 .
In C , six operators generate side effects: prefix increment and decrement ,
postfix increment and decrement , assignment , and function call.
a 4 + b / 2 c b
For this example, assume that the values of the variables are
3 4 5
a b c
3 * 4 + 4 / 2 5 * 4
2. Evaluate the highest precedence operators, and replace them with the
resulting value. In the above expression , the operators with the highest
precedence are the multiply and divide ( 13). We therefore evaluate them
first from the left and replace them with the resulting values. The expres-
sion is now
(3 * 4) + (4 / 2) (5 * 4) —> 12 + 2 - 20
— a * 7 / 2 - C ++ * b
— a * 7 / 2 - 5 * b
——
3. Evaluate the prefix expression ( a ) next ( priority 1 3 ) . Remember that as
a prefix expression , the value of a is the value after the side effect ,
which means that we first decrement a and then use its decremented
value. The expression is now
2 * 7 / 2 - 5 * b
4. 1 he multiply and division are now evaluated using their associativity rule
lelt to right , as shown below.
14 / 2 - 5 * b -» 7 - 5 * 4 -> 7 - 20
5 ' The lasl step is to
evaluate the subtraction. The final expression value is
13 as shown in the final example.
7 “ 20 -> 13
-
Alter the side effects, the
variables h ave the values shown below.
2
a b
6
Program 3- 6 evaluates
the tw0 expressions
in this section .
Chapter 3 Structure of a C Program 113
18
19 x = a * 4 + b / 2 - c * b;
20 printf
21 ( " Value of a * 4 + b / 2 c * b: %d \n" , x );
22
23 y = — a * ( 3 + b ) / 2 - C ++ * b ;
24 printf
25 ( " Value of --
a * ( 3 + b) / 2 - C ++ * b: %d \n" , y );
26 printf( "\nValues of the variables are now: \n" );
27 printf( " a = %d \tb = %d\tc = %d \n\n " , a, b , c );
28
29 return 0;
30 } // main
Results:
Initial values of the variables:
a = 3 b = 4 c = 5
Value of a * 4 + b / 2 c * b: -6
Value of
— a * ( 3 + b) / 2 - C ++ * b: -13
Values of the variables are now:
a = 2 b = 4 c = 6
Warning
A warning is in order: In C , if an expression variable is modified more than
once during its evaluation , the result is undefined . C has no specific rule to
114 Section 3.5 Type Conversion
In
/ / C, if a
/ single variable \\
is modified more than \\
once in an expression,
the r e s u l t i s undefined .
WARNING
Conversion Rank
Before we discuss how conversions are handled,
we need to discuss the con-
cept ol conversion rank. In C, we can
assign a rank to the integral and
floating-point arithmetic types. Figure 3- 10
for conversion in this chapter. While
shows the ranks as we use them
the 1 to 9 scale we use is conceptually
correct , the actual implementation
is much more complex
intevt " Fi§Ure 3' , 3 l0 d uble
u integer
integer andI a short 10 US
° »
real »s a higher rank than a long
has a higher rank than a character.
——
Conversions in Assignment Expressions
,Depending
he
* ^- ^ *•-.-K
on^thed fference n the
»
rank" t"'
'
^ tW
°
Chapter 3 Structure of a C Program 115
occurs if the right expression has lower rank; demotion occurs il the right
expression has a higher rank.
Real
9. long double
8. double
Integer 7. float
6. long long
5. long
4. int
Character 3. short
Boolean 2. char
1. bool
Promotion
.
There is normally no problem with promotion The rank of the right expres -
.
sion is elevated to the rank of the left variable The value of the expression is
the value of the right expression after the promotion. The following examples
demonstrate some simple promotions.
bool b = true ;
char c = 'A';
int i = 1234 ;
long double d = 3458.0004 ;
c = b; // value of c is SOH ( ASCII 1 )
I c; // value of i is 65
d = b; // value of d is 1.0
d = i; // value of d is 1234.0
Demotion
Demotion may or may not create problems. If the size of the variable at the
left side can accommodate the value of the expression, there is no problem;
however, some of the results may surprise you . »
Any integral or real value can be assigned to a Boolean type. II the value
of the expression on the right is zeroy false ( 0 ) is stored; if the result is not
zero, either positive or negative, true ( 1 ) is stored.
When an integer or a real is assigned to a variable ol type character, the
least significant byte of the number is converted to a character and stored.
When a real is stored in an integer, the fraction part is dropped. However, if
the integral part is larger than the maximum value that can he stored, the
results are invalid and unpredictable. Similarly, when we try to store a long
double in a variable of type flout , the results are valid if the value fits or
invalid if it is too large.
116 Section 3.5 Type Conversion
.
demonstrate demotion
The following examples
bool b = false;
char c = A' ;
short s = 78;
int j = INT MAX ;
int k = 65;
1 (true)
// value of b is
b = c; // value of s is unpred ictable
s = j; : value of c is B
// demotion
c = k + 1;
bool b = true;
char c = 'A ' ;
int i = 3650;
short s = 78;
long double d = 3458.0004 ;
b + c // b promoted ; result is 'B' ('A ' + 1)
i * s; // result is an int
d * c; // result is long double
continue d
Chapter 3 Structure of a C Program 117
Results:
bool + char is char: B
int * short is int: 284700
float * char is float: 15944.500000 !
After execution...
char + true: B
float + char: 311.299988
-
bool = float: 1
Program 3 -7 Analysis Several points in this program require explanation. First, as we stated before, it is not
a good programming practice to code an expression in a print statement. We do it in
this program, however, to demonstrate the promotion or demotion of the expression.
The first print series displays the value of mixed type expressions. As you exam -
ine each result, note that the value printed is in the form of the higher ranked vari -
able. For example, in statement 18 the Boolean in the expression (b) is promoted to a
118 Section 3.5 Type Conversion
( float ) a
Note that in this operation, like any other unary operation , the value stored
in a is still of type int , hut the value of the expression is promoted to float .
One use of the cast is to ensure that the result of a divide is a real num -
ber. For example, il we calculated the average of a series of integer test scores
without a cast , the result would he an integer. To force a real result, we cast
the calculation as shown below .
average ( float ) totalScores / numScores ;
(float ) (a / 10 )
Are you surprised to find that
the result is ().()? Since no conversions are
required to divide integer 3 by
integer 10, C simply divides with an integer
result, 0. 1 he integer 0 is then
To get afloat result, c must explicitly converted to the lloating- point 0.0.
" cast one of the
numbers as shown below.
( float ) a / io
Chapter 3 Structure of a C Program 119
One final thought about casts: Even when the compiler can correctly cast
for you, it is sometimes better to code the cast explicitly as a reminder that
the cast is taking place.
Program 3 - 8 demonstrates the use of explicit casts. In this program, we
divide several mixed types. While the results are nonsense, they demonstrate
the effect of casting.
36 return 0;
37 y // main
Results:
aChar numeric : 0
intNuml contains: 100
intNum2 contains: 45
fltNuml contains: 100.00
fltNum2 contains: 45.00
2.00
(double)(intNuml / intNum2)
2.22
(double) intNuml / intNum2
2
(char)(fltNuml / fltNum2)
3.6 Statements
A statement causes an action to be performed by the program . It translates
directly into one or more executable computer instructions.
You may have noticed that we have used a semicolon at the end of the
statements in our programs. Most statements need a semicolon at the end ;
some do not . When we discuss statements, we identify those that do.
Statement Type
C defines eleven types of statements , which are shown in Figure 3 - 1 1 . In this
chapter we will discuss the first four; the other types will be covered in the
future chapters.
Null Statement
The null statement 15
is Just
i a semicolon ( the terminator ) as shown below:
// null statement
Although they do not arise often,
we must have a statement there are syntactical situations where
but no action is required. In
use the null statement. these situations, we
Chapter 3 Structure of a C Program 121
Expression Statement
An expression is turned into a statement by placing a semicolon ( ; ) after it .
Null
Expression
Return
Compound
Conditional
Statement Labeled
Switch
Iterative
Break
Continue
Goto
When C sees the semicolon , it completes any pending side effects and
discards the expression value before continuing with the next statement . An
expression without side effects does not cause an action . Its value exists and
it can be used , but unless a statement has a side effect , it does nothing.
Let us look at some expression statements. First , consider the expres-
sion statement
a = 2;
The effect of the expression statement is to store the value , 2 , in the vari -
able a . The value of the expression is 2. After the value has been stored , the
expression is terminated ( because there is a semicolon ) , and the value is dis-
carded . C then continues with the next statement .
The next expression statement is a little more complex.
a = b = 3;
a * (b = 3);
This statement has two side effects. The first is found in the scanf func-
tion. Reading an integer value from the keyboard and placing it into the vari -
able x ( note the address operator before the variable ) is a side eflect . I he
second side effect is storing the value returned by scanf which represents the
number of values that were converted correctly. In this case , the return value
could be EOF, 0, or 1 . Assuming that the user correctly keys the integer, the
value will he 1. The assignment operator stores the return value in ioResult .
I he expression then terminates, and the scant value is discarded .
In a similar fashion , printj has the effect of displaying data on the moni -
tor and returning a value , the number of characters displayed . This is seen in
the statement below.
numDisplayed = printf
( " x contains % d , y contains % d \ n " , x , y ) ;
print
^ "x contains %d , y contains % d \ n ", x , y ) ;
value
\ a l u c of ^ ^owin8 exPression statement. Assume that a has a
^ helore the expression is evaluated .
a + +;
In this postfix expression , the
. value
i changed. .by the
variable , a , before it 15 ,
of the expression is 5 , the value of the
side effect . Upon completion of the
expression statement a is
,
which is still 5 is discarded sntrtmumed
l
°55 The Vyluc of ^-expression ,
"
^ ^^ ^
fon W '3"re*a so expression
COmPlete
Although they are useless 0
I hey are useless because
they hav ° S C c
T statements,
assigned to a variable. We I K H I 1 . * ect ancI their values are not
> °n t use them , hut it is important to know
Chapter 3 Structure of a C Program 123
b; 3;
Return Statement
A .
return statement terminates a function All functions, including main ,
.
must have a return statement When there is no return statement at the end of
the function, the system inserts one with a void return value.
In addition, the return statement can return a value to the calling Iunc -
tion. For example, while we have not talked about it or used it, the scanj func -
.
tion returns the number of variables successfully read In the case of main , it
returns a value to the operating system rather than to another function. In all
of our programs, main returns 0 to the operating system. A return value of
zero tells the operating system that the program executed successfully.
Compound Statements
A compound statement is a unit of code consisting of zero or more state-
.
ments It is also known as a block. The compound statement allows a group
of statements to become one single entity. You used a compound statement in
your first program when you formed the body of the function main . All C
functions contain a compound statement known as the function body .
A compound statement consists ol an opening brace, an optional declara-
tion and definition section, and an optional statement section, followed by a
closing brace. Although both the declaration section and the statement sec -
.
tions are optional, one should be present If neither is present, then we have
a null statement , which doesn’t make much sense. Figure 3 - 1 2 shows the
makeup of a compound statement.
{
// Local Declarations
Opening int x;
Brace int y;
int z;
// Statements
Closing
x = 1;
y 2;
Brace
> // End Block
1 . In an optimized compiler, the translator determines that there is no effect and generates no code.
— —
One important point ° r
not need a semicolon . The open
iters lor the compoun s a
ZI.
“X
.
.„
Pg
«
-
amUclosinggFparentheses are simply delim-
^
"*'
a
statements 2 within
section and state-
semicolon.
yc
,
» reaming me sage .
„ *,
The most difficult part of this program is figuring out how to get the remainder. For -
tunately, C has a modulo operator ( % ) that does the job for us. The rest of the problem is
straightforward.
20 return 0;
21 } // main
Results:
continued
Chapter 3 Structure of a C Program 127
35
36 printf nsecond number
( " \ : %6d --
deviation: % 8.2f " ,
-
num2, num2 average);
37
38
39
printf("\nthird number:
num3, num3
%6d
-
average);
—
deviation: %8.2f" ,
Results:
Enter the first number: 23
Enter the second number: 12
Enter the third number: 45
Enter the fourth number: 23
first number:
second number:
23
12
—— deviation:
deviation:
-2.75
-13.75
third number:
fourth number:
45
23
—— deviation:
deviation:
19.25
-2.75
Proaram 3 * 11 Analysis Program 3-11 is a little more complex than the previous ones. At the beginning o
^ main are several variable declarations, five integers, and a floating- point number
The first four are for the variables read from the keyboard, the fifth is for the sum, and
the floating-point number is for the average.
The statements section starts by reading the data. Each read is preceded by a dis-
play so the user will know what to do. The specific instructions about what to input are
known as user prompts You should always tell the user what input is expected from the
keyboard. After the user has keyed the data, the program continues by adding the
numbers, placing the total in sum, and computing average. It then
displays the results.
Notice that the program displays the results in a format
verify that the program ran correctly. The program not
that allows the user to easily
repeats each input with its deviation from the
only prints the average but also
average. After completing its work, the
program concludes by returning to the
operating system.
Look at the results carefully. Note how each
they can be easily read. Taking the time to series of numbers is aligned so that
guishes a good programmer from an align output is one of the things that distin-
how your program presents its results toaverage programmer. Always pay attention to
pays off in the long run.
the user. Paying attention to these little details
Chapter 3 Structure of a C Program 129
Results:
Enter the angle in radians: 1.57080
1.571 radians is 90.000 degrees
Program 3 - 1 2 Analysis In this short program we introduce the defined constant. Defined constants are an excel -
lent way to document factors in a program. We could have just as easily used a mem-
ory constant. Factors are usually placed at the beginning of the program where they
can easily be found and changed as necessary. Remember that the area before main is
global and that it should be used only for declarations and constants — not variables.
continued
Chapter 3 Structure of a C Program 131
Results:
Enter number of items sold: 34
Enter the unit price: 12.89
Enter the discount rate ( per cent ): 7
Quantity sold: 34
Unit Price of items: 12.89
Subtotal : 438.26
Discount: 30.68
Discounted total: 407.58
Sales tax: + 34.64
Total sale: 442.23
Program 3 - 1 3 Analysis Look at the results of this program carefully. Do you see any problems? Just because a
program runs doesn't mean that it is running correctly. In this case, the total is incor -
rect ( 407.58 + 34.64 is not equal to 442.23 ). The problem is created by the floating-
point arithmetic and rounding errors. If we wanted absolute accuracy, we would have
to do the arithmetic in integer ( cents ) and then divide by 100 to print the report .
48 printf("Enter the
score for the second midterm: " );
continued
Chapter 3 Structure of a C Program 133
Results:
continued
134 Section 3.7 Sample Programs
the
Enter the score for
First Quiz 98
Second Quiz 89
Third Quiz 78
Fourth Quiz 79
Quiz Total 344
First Midterm 90
Second Midterm 100
Total Midterms 190
Final 92
Quiz 25.8%
Midterm 38.0%
Final 27.6%
Total 91.4%
KISS
Keep It Simple and Short ( KISS 4 ) is an old programming principle. Unfortu -
nately, many programmers tend to forget it , especially the simple part . They
seem to feel that just because they are working on a complex problem , the
solution must be complex, too. That is simply not true. Good programmers
solve the problem in the simplest possible way; they do not contribute to a
complex situation by writing obscure and complex code .
A trivial example will make the point . If you were writing a program that
reads floating- point numbers from the keyboard , you would not program it so
that the user had to enter the integral portion of the number first and then
the fractional part . Although this would work , it is unnecessarily complex,
even though it might be a fun way to solve the problem .
Unfortunately, C provides many operators and expression rules that make
it easy for a programmer to write obscure and difficult to follow code . Your
job as a programmer is to make sure that your code is always easy to read .
Your code should be unambiguous: It should not he written so that it is easy
to misread it .
Another old structured programming principle is that a function should
not be larger than one page of code. Now, for online programming in a work -
station environment , a function should be no longer than one screen about
20 lines of code . By breaking a problem down into small , easily understood
—
parts, we simplify it . Then we reassemble the simple components into a sim -
ple solution to a complex problem .
Parentheses
One programming technique to simplify code is to use parentheses, even
when unnecessary'. While this may lead to a few extra keystrokes, it can save
hours of debugging time created by a misunderstanding ol the precedence
4. KISS originally had a different , rather insulting , meaning . We prefer this interpretation .
and associativity rules. Whenever a
statement contains multiple expressions
the compiler will interpret it as you intended .
use parentheses to ensure that
User Communication
You should always make sure you communicate with your user from the very
first statement in your program to the very last . As mentioned previously, we
recommend that you start your program with a message that identifies the
program and end with a display that says the program is done.
When you give your user instructions , make sure that they are clear and
understandable. In Program 3- 13, we used three statements to give the user
complete and detailed instructions on what we wanted entered . We could
have simply said
"Enter data"
but that would have been vague and subject to interpretation . How would the
user know what specific data were required? For each input in Program 3- 13,
we told the users exactly what data we needed in terms that they understand .
If you don’t tell users exactly what data to input , they may do anything they
feel like, which is usually not what you wanted or expected .
One common mistake made by new programmers is to forget to tell the
user anything. What do you think would be the user ’s
response to
-
Program 3 15 when confronted with a blank screen and a
computer that is
doing nothing?
PROGRAM 3 - 15 Program That Will Confuse the User
1 linclude <stdio.h>
2 int main (void)
3 {
4 int i;
5 int j;
6 int sum ;
7
8 scant("%d%d", Si , S j ) ;
9 sum =
i + j;
10 printf("The SUm
of %d & is %d\n" , i
11 return 0; i , j, sum);
12 > // main
We now rewTite
i the program
addition of one print with clear uuser
communication. With the
statement, the user knows exactly
what is to be done.
Chapter 3 Structure of a C Program J 37
Results:
Enter two integers and key <return>
4 5
The sum of 4 & 5 is 9
We will return to these three concepts from time to time when we intro-
duce new structures that tend to be confusing or misunderstood.
3.9 Tips and Common Errors
. They are one of the main sources of
1. Be aware of expression side effects
confusion and logical errors in a program .
2 Use decrement/increment operators wisely
. . Understand the difference
between postfix and prefix decrement /increment operators before
using them .
.
3 Add parentheses whenever you feel they will help to make an expression
clearer.
4 It is a compile error to use a variable that has not been defined .
.
5. It is a compile error to forget the semicolon at the end of an expression
statement .
6. It is a compile error to code a variable declaration or definition once you
have started the statement section. To help yourself remember this rule,
use comments to separate these two sections within a function .
.
7 It is most likely a compile error to terminate a defined constant (#define)
with a semicolon . This is an especially difficult error to decipher because
—
you will not see it in your code you see the code you wrote, not the code
that the preprocessor substituted .
8. It is a compile error when the operand on the left of the assignment oper
ator is not a variable. For example, a + 3 is not a variable and cannot
-
receive the value of b * c.
(a + 3) = b * c;
.
9 It is a compile error to use the
increment or decrement operators with
any expression other than a variable identifier.
For example, the following
code is an error:
(a + 3 ) ++
10. It is a compile error to use
the modulus operator ( % ) with anything other
than integers.
11. It is a logic error to use i
a variable before it has
12. It is a logic error to
been assigned a value.
modifv> a variable li n an expression when
'
3.11 Summary
An expression is a sequence of operators and operands that reduces to a
single value .
An operator is a language-specific token that requires an action to
be taken .
An operand is the recipient of the action .
C has six kinds ol expressions: primary, postfix, prefix, unary, binary, and
ternary.
The most elementary type of expression is a primary expression . A primary
expression is an expression made up of only one operand . It can he a
name, a constant , or a parenthesized expression .
A postfix expression is an expression made up of an operand followed by an
operator. You studied function call and postfix increment /decrement
expressions in this chapter.
A unary expression is an expression made up of an operator followed by an
operand . You studied six in this chapter: prefix increment /decrement ,
sizeofy plus/minus , and cast expressions.
A binary expression is an expression made up of two operands with an
operator between them . You studied multiplicative , additive, and assign -
ment expressions in this chapter.
Precedence is a concept that determines the order in which different oper-
complex expression act on their operands.
ators in a
Associativity defines the order of evaluation when operators have the same
precedence.
The side effect of an expression is one ol the unique phenomena in C. An
expression can have a side effect in addition to a value.
140 Section 3.12 Practice Sets
.
6 A(n) is a sequence of operands and operators that
reduces to a single value.
.
a expression
b. category
c. formula
.
d function
.
e value
.
7 Which of the following is a unary expression?
.
a i+ j
.
b +a
c. ++ C
d. scanf ( ... )
e. x * 5 =
Exercises
13. Which of the following expressions are not postfix expressions ?
a . x ++
b. x
—
c. scanf ( ... )
.
d x * y
e. + + x
.
14 Which of the following are not unary expressions?
.
a ++x
b. x
. —
c sizeof
d. +5
(x)
e. x = 4
15. Which of the following is not a binary expression ?
a. 3 * 5
b. x + = 6
c. y = 5 + 2
d. z - 2
e. y % z
16. Which ol the following is not a valid
assignment expression ?
a. x = 23
b. 4 = x
c. y % = 5
d. x = 8 = 3
e. x = r = 5
17. It originally x - 4, what
is the value of x after the
lowing expression? evaluation of the fol-
a. x = 2
b. x += 4
.
C X += x + 3
d. x * = 2
e. x / = x + 2
Chapter 3 Structure of a C Program J 43
18. If originally x = 3 and y = 5 , what is the value of x and y after each of
the following expressions ?
a. x + + + y
.
b ++ x
c. x ++ + y ++
.
d ++ x + 2
.
e x
— y —
19. What is the value of each of the following expressions?
a. 2 4 6 * 2
b. -15 * 2 + 3
c. 72 / 5
d. 72 % 5
e. 5 * 2 / 6 + 1 5 % 4
20. What is the value ol each of the following expressions?
.
a 6.2 + 5.1 * 3.2
b. 2 . 0 + 3 . 0 / 1 . 2
c. 4 . 0 * ( 3 . 0 + 2 . 0 / 6 . 0 )
d. 6 . 0 / ( 2 . 0 + 4 . 0 * 1 . 2 )
e. 2 . 7 + 3 . 2 5.3 * 1.1
21 . Given the following definitions, which of the following statements are
valid assignments?
# d e f i n e NUM 10 10
int x;
i n t y = 15 ;
.
a x = 5;
.
b y = 5;
.
C X Y = 50 ;
.
d x = 50 = y ;
.
e x = x + 1;
.
f y = 1 + NUM 10 ;
-
g 5 = y;
22. II originally x = 2 , y = 3 , and z = 2 , what is the value of each of the
following expressions?
a . x ++ + y++
b. + + x - -- z
c.
d. x
—— —
x + y++
+ x - y —
e. x +
— =y
23. If originally x
- X + X++ y
2 , y = 3 , and z = 1, what is the value of each of the
following expressions?
.
a x + 2 / 6 + y
b. y - 3 * z + 2
c. z (x + z) % 2 + 4
144 Section 3.12 Practice Sets
d. x - 2 * ( 3 + z ) + y
—
e. y++ + z + x++
2 4. I f x = 2 9 4 5 , what is the
value of each of the following expressions?
a. x % 10
b. x / 10
c.(x / 10) % 10
d . x / 100
e.(x / 100) % 10
25. What is the output from the following
code fragment ?
int a;
int b;
a = b = 50;
printf ("%4d %4d", a, b);
a = a * 2;
b = b / 2;
printf ("%4d %4d", a, b);
Problems
26. Given the following pseudocode , write a program that executes it . Use
floating- point types for all values.
Algorithm Problem26
1 read x
2 read y
3 compute p = x * y
4 compute s = x + y
5 total = s2 + p * (s
6 print total
- x) * (p + y )
End Problem26
.
rSa7pZoanmofhaSrCtS^^^
30 Write a program that
SeC
°nd ri 8htmost di &il of the
.
from a user-ciirsrti .length and width.* Perimeter of a rectangle
calculates the area ant
suppliedi (/ scanfn)
.
31 We are all familiar
with the fart tW anges
i
minutes, and seconds Annthr " 0i afe measured in dc8rees’
3n8 C S 3 rad an ' A radian
is the angle formed by
two radii
‘ ' ^ '°
^^
radiUS
of their circle. One
‘ ^ * Vs
radian cquals / * 5779Q degrees l
. Write a program
M
Chapter 3 Structure of a C Program 145
that converts degrees into radians. Provide good user prompts. Include
the following test data in your run :
F = 32 + C x 180.0
100.0
34 . \ \ rite the C code lor each of the following formulas. Assume that all vari -
ables are defined as double .
a.
2
HIV
K •i n E n
w r r<
= -y-
b.
b+c
res = 2be
35. Write the C code to calculate and print the next two numbers in each of
the following series. You may use only one variable in each problem .
a . 0, 5 , 10, 15 , 20 , 25 , ? , ?
b. 0, 2, 4, 6, 8, 10, ?, ?
.
c 1 , 2 , 4 , 8, 1 6 , 3 2 , ? , ?
—
146 Section 3.12 Practice Sets
Projects
36. Write a program that converts
inches into
and prin s a user-supplied
' re ..,, ,
a. foot ( 12 inches )
b. yard ( 36 inches )
c. centimeter ( 2.54/inch
)
0 , 1 , 1 , 2 , 3 , 5 , 8 , 1 3 , 2 1 , ...
Write a program that calculates and prints the next three numbers in
the Fibonacci series. You are to use only three variables, fibl , fib2 .
and fib 3.
38. Write a program that prompts a user for an integer value in the range 0 to
32,767 and then prints the individual digits of the numbers on a line
with three spaces between the digits. The first line is to start with the
leftmost digit and print all five digits; the second line is to start with the
second digit from the left and print four digits, and so forth . For example,
if the user enters 1234 , your program should print
0 1 2 3 4
1 2 3 4
2 3 4
3 4
4
*^
iris:i su?., tr
SCt
‘ntcgers
U
representing the quantities
^
of each if
w ;
“ p,om* ,*
|> ,h
in
XX TV 400.00 XXXX XX .
XX VCR 220.00 XXXX XX .
XX REMOTE CTRLR 35.20 X X X X .X X
XX CD PLAYER 300.00 X X X X. X X
XX TAPE RECORDER 150.00 XXXX.XX
SUBTOTAL XXXXX XX .
TAX XXXX XX .
TOTAL XXXXX XX .
FIGURE 3 - 1 3 Output Format for Project 39
Use either defined constants or memory constants for the unit prices
and the tax rate . Use integer variables to store the quantities for each
item . Use floating- point variables to store the total price for each item ,
the bill subtotal, the tax amount , and the total amount of the bill . Run
your program twice with the following data:
SET 1 -> 2 1 4 1 2
SET 2 3 0 2 021
« •
Functions
In Chapter 2, we introduced data types. In the preceding chapters, we used
only the three non - derived types, void , integral, and floating-point . Although
these types are very usef ul, they can solve only a limited number of problems.
There are six derived types in C, as shown in Figure 4 - 1 .
In this chapter we introduce the first derived type, the function type. The
function type is derived from its return type. The return type can be any type
except an array, which we introduce in Chapter 8, or a function type .
Derived
Types
Function
Type i Array
Type
Chapter 8
i Pointer
Type
Chapter 9
i Structure
Type
Chapter 12
Union
Type
Chapter 12
Enumerated
Type
Chapter 12
Objectives
To design and implement programs with more than one function
To be able to design multi - function programs using structure charts
To understand the purpose of the function declaration, call, and definition
To understand the four basic function designs
To understand how two functions communicate through parameters
To understand the differences between global and local scope
To understand the software engineering principles of functional cohesion
and top-down development
149
150 Section 4.1 Designing Structured Programs
. .. ..
parts is a common practice. For
Breaking a complex problem into smaller
this year you dec de to dnve n a cir-
example, suppose that for your vacation onal parks as poss ble in
visit as many nat
cular route that will allow you to are very s mple: Visit as many
two weeks Your requirements for this problem
will you do it ? You might first gather
parks as possible in two weeks. But how
some data about national parks and then
calculate the distance between each
. Next , you would estimate how much
of them to figure out the travel time
time it would take to visit each park . Finally , you would put all your data
together to plan your itinerary . Then you would make your motel and camp
reservations and any other arrangements that had to be in place in advance.
The planning for large programs is similar f irst , you must understand
,
the problem as a whole; then you must break it into simpler, understandable
parts. We call each of these parts of a program a module and the process of
subdividing a problem into manageable parts top-down design .
The principles of top-down design and structured programming dictate
that a program should be divided into a main module and its related modules.
Each module should also be divided into submodules according to software
engineering principles that we discuss in Section 4.8 , “ Software Engineer-
ing. The division of modules proceeds until the module consists only of ele-
mentary' processes that are intrinsically understood and cannot be further
subdivided . This process is known as factoring .
In top-down design, a program is divided into a main module and its related
.
modules Each module is in turn divided into submodules until the resulting
modules are intrinsic; that is, until they are
implicitly understood without
further division .
ReferrinoTp 6
Ve ln " ," at
FlrSt the structure charl is read top-down,
,
’
^ ^^ ^ Mai Motlule' 1,1 this caSC'
Main Module represents our "
entire set of code to solve the problem .
Chapter 4 Functions 151
Main
Module
Moving down and left, we then read Module 1 . On the same level with
Module 1 are Module 2 and Module 3. The main module consists of three
submodules. At this point, however, we are dealing only with Module 1. We
now note that Module 1 is further subdivided into three modules, Module la,
Module lb, and Module lc. To write the code for Module 1 , therefore, we
will need to write code lor its three submodules. What does this concept say
about writing the code for the Main Module?
Now lor some more terminology. The Main Module is known as a calling
module because it has submodules. Each of the submodules is known as a
called module. But, because Modules 1, 2, and 3 also have submodules, they
are also calling modules; they are both called and calling modules.
Communication between modules in a structure chart is allowed only
through a calling module. If Module 1 needs to send data to Module 2, the
data must be passed through the calling module, Main Module. No commu -
nication can take place directly between modules that do not have a calling-
called relationship.
With this understanding, how can Module la send data to Module 3b? It
first sends the data to Module 1, which in turn sends it to the Main Module,
which passes it to Module 3, and then on to Module 3b. Although this
sounds complex, it is easily done.
The technique used to pass data to a function is known as parameter
passing. The parameters are contained in a list that is a definition of the data
passed to the function by the caller. The list serves as the formal declaration
of the data types and names.
4.2 Functions in C
.
In C, the idea of top-down design is done using functions A C program is
made of one or more functions, one and only one of which must he named
main . The execution of the program always starts and ends with main , but it
152 Section 4.2 Functions in C
^ ^j
nair
B
Function| Function Function
2 3
-
1 In Chapter 12 we
discuss how to turn multiple data values
using a structure.
Chapter 4 Functions 153
t
At most one data value
or structure can be returned.
-
J
We have already *veral fun
i
program . »'
jil^ ^ ^ by
>B rating our own functions. ISeW ^ c
rSTJaSl -
let * M at Program 4 1, which demo,,
strates how we write and call a function .
continued
Chapter 4 Functions 155
A function name is used three times: for declaration, in a call, and for definition .
Figure 4 - 5 shows the interrelationships among these function compo-
nents. As you study Figure 4- 5, note that the function name is used three
times: when the function is declared, when it is called, and when it is
defined.
return 0 ; i
} // main
* pHello World
Side Effect
.
Now let s call a function that has parameters but
still returns void . The func -
r pr ; -nt0ne> as seen in Figure 4-6,
*
this funet on returns nothing to the
receives an integer parameter Since .
calling function, main , its return type is
s uit 11 c greeting
function discussed previously, this function must
e coded as a stand-alone call
because it does not return a value; it cannot be
re
the
lmmoni
s noSP
tor
art
|
eS
’'^
"T LreXpreSSi effect
“ * 3 : The ’ -
n Note however that while printone
0 '
// Function Declarations
void printOne ( int x ); ^ a
int main ( void ) 5
{
// Local Declarations
int a = 5;
// Statements
printOne ( a ); // call
return 0;
> // main
continued
J
\m
158 Section 4.3 User-Defined Functions
( continued )
PROGRAM 4 - 2 void Function with a Parameter
22 operating system .
23 // Done. Return to
24 return 0;
25 > // main
26
27 / * =: = printOne
28 Print one integer value.
to be printed
29 Pre x contains number
30 Post value in x printed
31 * /
32 void printOne (int x)
33 {
34 // Statements
35 printf("%d\n" , x);
36 return ;
37 > // printOne
Results:
5
33
// Function Declaration
int getQuantity (void);
P\
int main ( void )
int getQuantity ( void )
// Local Declarations // Local Declarations
int amt ;
int qty ;
// Statements // Statements
amt = getQuantity ( ); printf( " Enter Quantity " )?
return 0; scanf ( " %d " , &qty );
} // main return qty ;
} // getQuantity
// Function Declaration
int sqr ( int x ); Returned
int main ( void ) stored here
{
// Local Declarations
int a ;
int b ; a b
// Statements
scant( " %d " , &a );
b = sqr (a);
printf( " %d squared: %d \ n " , a , b) ;
return 0;
} // main
/ / Expression Statement
. No side effect
sqr ( a ) ; b
b = sqr ( a ) ; / / Return value is stored in
only function calls, lo demon-
In large programs, main is written with
be done with our simple example, we combine our
strate how this would
square and print functions into Program 4 - 3.
i
=== getNum ===
continued
Chapter 4 Functions 161
62 */
63 void printOne ( int x )
64 {
65 // Statements
66 printf( "The value is: %d \n " , x );
67 return ;
68 > // printOne
Results:
Enter a number to be squared: 81
The value is: 6561
Program 4- 3 Analysis Our simple program has grown to four functions, including main . This is an example
of decomposition, the process of breaking a complex problem into simple parts.
While our example is not really complex, it still demonstrates the concept.
J
S,'!*,p .^ . '
o
* mete tolJ 9"«
“ ”*<"*•“’P"'i
Function Definition
Now that we have seen examples of the
functions in more detail. We
The function definition
begin
contains
with
the
the
code
.
basic function formats , let ’s look at
function dehmt on .
for a function . It is made up of
two parts: the function header and the function
body, which is a compound
statement must have opening and
statement . Remember that a compound
closing braces, and it has declaration and statement .
sections The function
definition format is shown in Figure 4 - 9 .
Function Header
return type functionname ( formal parameter list )
{
/ / Local Declarations
/ / Statements
} / / function name_
Function Body
Function Header
A function header consists of three parts :
the return type, the function
name , and the formal parameter list . A
semicolon is not used at the end of
the function definition header.
With the implementation of the C 99
standard , the return type became a
Ivpe istJ ‘men WKen nothinS is to be returned , the return
^
Function Body
ments. Thebodvsl^ l 0Cal dctlaralions and the function state-
by the function. A f t e r S p C C f y the variables neede!<
‘ -
stateml f T J
n statements. terminal
ing with a return
be written without a
return
ments should be explicitly
every function , even void fimrc C
I i
’
>
,^? ^
statement are cod d'' ( wction
*
' WCU°retu
C arity we
> ons, have a return
>
type is void , it can
® u s e we believe that default state-
{
return statement
{ should be used even if
^ nothing is returned
^
return ( x + 2 ); return;
} // first } // second
Local Variables
a function and used with -
A local variable is a variable that is defined inside
between functions. Figure 4-11
out having any role in the communication
shows an example of a function with both formal parameters and a local vari-
able, sum.
Function Declaration
Function declarations consist only ol a function header ; they contain no
code. Like I unction definition headers , function declaration headers consist
of three parts: the return type, the function name , and the formal parameter
list . Unlike the header lor the function definition , function declarations are
terminated with a semicolon .
I he return type and the parameter list are required entries. If the pro *
definition; on the other hand, if the types are not the same, we get a compile
error. The compiler checks the types in the function statements
with the
in the call to ensure that they are the same or at least compatihle. I he
types
major reason to include the identifiers in the function
declaration is docu -
mentation; therefore, their names should be meaning ful. Don’t use generic
identifiers such as a x or .
Declarations are placed in the global declaration section before m a i n .
Grouping all function declarations at the beginning of the program
makes
them available whenever they are needed. Function declaratio ns also provide
an excellent quick reference for functions used in the program, making
them
excellent documentation .
Figure 4 - 12 demonstrates several of these concepts. The function decla
-
// Function Declaration
int multiply (int multiplier, int multiplicand );
int main (void )
{
int product ;
product = multiply ( 6 , 7 );
return 0 ;
} // main
7
42
int multiply
{
lint
^
x, i n t y ) |
X
5 6
return x * y ;
y 7
} // multiply
Function Definition
in the
Figure 4 - 12 also demonstrates that the formal parameter names
declaration do not need to be the same as the actual parameter names. In this
the function declaratio ns are much more meaningf ul and
case the names in
therefore should have been used in the function definition.
,
and he fifth shows the function multiply ( a b as Us
,
own first parameter.
The last example sums it all up: Any expression that reduces to a smgle value
can be passed as a parameter .
multiply ( 6, 7 ) multiply ( a, 7 )
multiply ( 6, b ) multiply ( a + 6, 7 )
multiply ( multiply ( a, b ), 7 ) multiply ( )
expression ; expression
Function Examples
This section contains four examples of programs
in which functions call
functions. Look for the points they demonstrate.
continued
Chapter 4 Functions 167
Results:
Enter an integer: 27
charts.)
tro uble
understanding the structure
main
addTwo
Digits
secondDigit
Two Digits
FIGURE 4- 14 Design for Add
continued
Chapter 4 Functions 169
Results:
Run 1
Enter an integer:
23
two digits is : 5
Sum of last
Run 2
Enter an integer : 8
is : 8
Sum of last two digits
continued
Chapter 4 Functions 171
Results:
Run 1
Enter a number with up to 6 digits: 123456
Prnnrnm 4 - 6
. ~ aaain• , we Lnave „a cimnlp
Anolvsis Once
_ nroaram
simple p 9 that has the. , of a very useful func-
makings
9 as commas
,
S Land t S? [;
s i c e his °g c wiN be
^
'
, " ,
over again, it must be in its own fund on
Note, however that more work s
over
needed to print numbers less than 100 ,000 correctly. However, we have not yet unre-
duced the tools to do the complete job. , ,
Again, we have used two test cases to show that more work must be done. An even
bigger problem occurs if we try to format a small negative number. Can you see what
the problem is? If not, code the problem and run it to see. We will explain how to han-
die these problems in Chapter 5.
number of units each term . Each term , the students are charged S 10 per unit
plus a $ 10 registration fee. To discourage them from overloading, the college
charges $ 50 extra for each 12 units, or fraction thereof , a student takes after
the first 12 units. For example, if a student takes 13 units, the tuition is $ 190
( $ 10 for registration , plus 13 times $ 10 for units, plus a $ 50 penalty for the
one extra unit ). II a student takes 25 units, the tuition is $ 360 ( $ 10 for regis-
tration , plus 25 times $ 10 for units , plus $ 100 for two penalty fees ) . The
design lor this problem is shown in Figure 4 - 15.
Strange
College Fees
calculateFee
(continued )
PROGRAM 4- 7 Strange College Fees
44 =========== calculateFee the year.
45 Calculate the total fees for
to be taken each term.
46 pre The number of units
the annual fees.
47 Post Returns
48 */ , int secondTerm ,
49 int calculateFee (int firstTerm
50 int thirdTerm )
51 {
52 // Local Declarations
53 int fee;
54
55 // Statements
56 fee (firstTerm)
= termFee
57 + termFee (secondTerm )
58 + termFee (thirdTerm );
59 return fee;
60 } // calculateFee
61
62 /* === === termFee ============== =
63 Calculate the tuition for one term
64 Pre units contains units for the term
65 Post The fee is calculated and returned
66 */
67 int termFee ( int units )
68 {
69 // Local Declarations
70 int totalFees;
71
72 // Statements
73 totalFees =
REG FEE
74 + (( units - _
75
76
+ (units * UNIT FEE ); _
1 )/ 12 * EXCESS FEE )
return ( totalFees);
77 } // termFee
Results:
Enter units for first term: 10
Enter units for second term: 20
Enter units for third term:
30
The total tuition ii s :
780
c
times in one function• Let's look ,
at how t works
| ^° W We
. The key ^
ca termFee three different
statement foliows.
Chapter 4 Functions 175
A function call is a postfix expression. Therefore, it evaluates from the left. To eval-
uate the expression ( three function calls) on the right of the assignment operator, we first
evaluate the first expression, the call to termFee with the number of units for the first
term. When termFee completes the first time, the return value ( 110 ) replaces the call .
At this point, we have the expression shown below.
When termFee is executed a second time, its return value ( 260) becomes the
value of the second expression and we have
After the third call to termFee, the expression on the right of the assignment oper -
ator is ready for evaluation. Its value is 780, which is assigned to fee .
At least two more tests are needed to completely evaluate this program. We would
run it with all three terms having 0 units and then do another test of 11, 12, and
1 3 units.
Basic Concept
concept before we discuss how they are done
Let us first discuss the general
in C .
Downward Flow
the calling function sends data to the called
“ rivsinThe
^ ^ opposite direction . In this strategy, copies of
^
o
the data items are passed from the calling function to the called I unction.
The called function may change the values
passed , but the original values in
example of this type of communi-
the calling function remain untouched . An
cation is passing data to a print function .
Upward Flow
Upward communication occurs when the called function sends data hack to
the called function without receiving any data from it . A good example of this
type of communication is when the called function reads data from the key-
board that needs to be passed hack to the called function .
Bi-directional Flow
Bi - directional communication occurs when the calling function sends data
down to the called function . During or at the end of its processing, the called
function then sends data up to the calling function . For example, the calling
function may send data to the called function , which it manipulates and
sends up to the calling function .
C Implementation
Now let us examine how these three types of communication are imple-
mented in C . In the discussion that follows, it is important to remember that
the flow refers to the data . The data flow s down to the called function. The
'
data flows from the called function hack to the calling
function .
Most programming languages have three strategies for inter function
communication: pass by value , pass by
-
guage , unfortunately, uses only the first
reference, and return . The C lan-
and last strategies; there iis no pass by
reference in C.
,ue
communirat
communications between ° ^3SS
^ Va
a calling and a called
and return achieve three types of
function.
Downward Communication
calling function sends a copy of each value to the called function; no data
( lows upward.
Figure 4 - 1 7 demonstrates downward communication. Two data items are
passed from main to the downFun function. One data value is a literal, the
other is the value of a variable.
} // main
// Function Declaration
void downFun ( int x , int y );
int main ( void )
{
// Local Definitions
int a = 5; a 5 15
// Statements C prints 5 I T
downFun ( a , 15 );,
printf("%d\n" , a ) ; I I
return 0;
One - way
} // main communication
I
void downFun (int x , int y )
{
// Statements
tw X X 20 y 15
x = x + y;
return; Only a copy
} // downFun
Upward Communication
C provides only one upward direction flow, the return statement. \ \ hile it
works well, only one data item can he returned. The only way that a called
function can pass multiple data items up to the calling function is to access
-
178 Section 4.4 Inter-Function Communication
F'
not allow us o
I herefore , we
»
.
words, we cannot access a
needf
The solution is 0 h(
to the called function . Given the
then put the data in the calling
1st
variab|e in the calling function by its identifier.
solve the problem .
frallingfanction
variable
function
's
t0 Pass thc address of thc variable
.
address, the called function can
1 he calling function needs to
variable to receive the data . The called function needs to
declare a data
that it receives from the calling
declare a variable to store the address
function .
In C , a variable that can store data is different from
the variable that can
store an address. So the called function needs a special type of variable, an
variable , or as it is called in C , a pointer variable , that points to the
address
variable in the called function .
The calling function needs to pass the address of a variable to receive thc
data from the called function . This action is a pass - by-value mechanism
although what is passed is not data , it is an address.
To get the address of a variable, we use the address operator ( & ). If the
name of the variable in the calling function is x , we extract its address using
the address operator ( & x ) and pass it as a parameter in the function call. Thc
following example passes the addresses of a and b to a function named upFun.
I5 u ^ JJ
MTRESS
‘ aster sk signifies that the variables
variables
ax and ay are not data
variables holding the address of int variables. Note that the
asterisk as used , n the declaration
and ay ).
belongs to the type not the variables ( ax
asterisk is known as the inrli change the data For this * reason, the
*
*ax = 23 ;
> // main
// Function Declaration
void upFun ( int* ax, int* ay
int main ( void )
Type includes
{
// Local Declarations
int a ;
Address 23 8
int b; operators. a b
// Statements
upFun (&a , & b );
printf( " %d %d \n " , a, b);
return 0 ; Dereference
} // main Prints 23 andjjT
void upFun ( int* ax, int* ay )
{
// Statements ax ay
*ax = 23;
__ jay Address
— L
= 8;
>eturn;
^
(pointer )
Bi-direction Communication
The strategy described for the upward
direction can easily be augmented !
in both directions . The only difference is that tin
allow the communication
indirect reference must be used in both sides of the assignment statement
The variable in the called function first is accessed for retrieving using tk
side . The same parameter is accessed agar
address variable in the right - hand
to store a value in the left hand- side . We demonstrate bi -
directional accessii
Figure 4- 21 .
i n t main ( void )
{
void biFun ( i n t * ax , i n t * ay )
{
4
int a; * ax = * ax + 2 ;
i n t b; *ay = * ay / * ax ;
return;
biFun ( & a , & b ) ; } / / biFun
} / / main
Communication Summary
Let ’s summarize the rules for inter-function communication . Like man
aspects ol learning a language, they must be
memorized.
.
1 Rules lor downward
communication
a. Use values in the functiion call to pass data ,
.
h Use ,appropriate data types in
the functiion parameter list to receive tk
data values.
on to access the loe
Chapter 4 Functions 181
// Function Declaration
void biFun (int* ax, int* ay);
int main ( void )
{
// Local Definitions
int a = 2; & 4 £ 2
int b = 6;
a b
// Statements
return 0 ;
} // main
Dereferences
^
)
Communication Examples
In this section we write some short programs to demonstrate function com -
munication .
j
182 Section 4.4 Inter-Function Communication
not
.
F rst, make sure you
First
simply assign va
understand how to exchange
^ ^ as shown
two
below.
variables: We can-
/ / value of y s a v e d
hold = y;
/ / x now i n y
y = x;
x = hold ; / / o r i g i n a l y now i n x
The exchange function and its data flow are shown in Figure 23. First,
4-
examine the function declarations in Figure 4 - 23 carefully. Note the asterisk
in the declaration of numl and num 2 . The asterisk is used with the type decla-
—
ration to specify that the type is an address in this case , the address of an
integer. Now, look at the call statement in main. Since we will change the val-
ues of a and b in main, we need to pass their addresses. The address operators
( & ) tell the compiler that we want their addresses passed, not their values.
// Function Declarations
void exchange (int* numl, int* num2); Note that the type
includes an asterisk.
int main ( void )
{
// Local Definitions
int a ;
int b;
a b
// Statements
K
void exchange (int* numl, int
* num2)
numl num2
// Local Definitions
int hold; numl and num2
// Statements are addresses
hold = *numl; Note the indirection )
* numl = *num2; operator is used for
*num2 = hold; dereferencing.
return;
} // exchange
hold = *numl ;
In the same manner, we can now dereference num2 and copy it to numl
and then complete the exchange hy copying hold to num2. These statements
are seen below'.
*numl = *num2 ;
*num2 = hold ;
Note that with the exception of hold , all of the data movement is done in
the calling program ’s area . This is the power of the indirection operator.
- -
Here is another simple example, one that uses both pass by value and
pass- by address parameters. We need to write a function that , given two
-
numbers, calculates both the quotient and the remainder. Since we can t
’
return two values, we will pass the addresses where we want the quotient and
remainder stored . This problem is shown in Figure 4 - 24.
// Function Declarations
void divide ( int divnd , int divsr ,
tv a b quot rem
return 0 ;
} // main
j
184 Section 4.4 Inter-Function Communication
Now look at the call in main. Note that a and b are simply passed by
using their identifiers. They are
primary expressions whose value is the con-
tents of the variable. On the
other hand , quot and rem are passed as
. In this case, the value of the primary
addresses by using the address operator
and the identifier is the address of
expression created by the address operator
value rather than a data value.
the variable. Therefore, we pass an address
what needs to be passed , a value
As the programmer, it is our job to know
. Similarly, when we use the parameters in
or an address , when we write a call
the called program , we must remember to use the indirection operator when
we have an address .
Let ’s use divide in a program that reads two integers , divides them , and
then prints the quotient and the remainder. The design for this program is
shown in Figure 4 -25.
Quotient
Remainder
- ^
continue
Chapter 4 Functions 185
j
186 Section 4.5 Standard Functions
(continued )
PROGRAM 4 - 8 Quotient and Remainder
dividend % divisor;
56 remainder =
57 return ;
58 } // divide
°r "
he function declaration, f
• . ^
'
Chapter 4 Functions 18 /
^include
int main ( void)
^-
<stdio.h> t r
= Generated from
stdio header file Mint scanf(...);
int printf(...);
return 0; return 0;
} // main
} // main
int scant(...)
{
return ... f
Definitions } // scant
added by linker
int printf(...)
{
return ... ,
} // printf
Math Functions
Many important library functions are available for mathematical calculations.
Most of the function declarations for these functions are in either the math
header file ( math .h ) or standard library ( stdlib.h ). In general , the integer
functions are found in stdlib.h .
jw .=:«ss£Kites;
double version is named fabsl .
number); // math.h
double fabs (double // math.h
fabsf (float number );
float / / math.h
long double number );
long double fabsl (
abs ( 3) returns 3
-
fabs ( 3.4 ) returns 3.4
Ceiling Functions
A ceiling is the smallest integral value
greater than or equal to a numher. For
e \am|) e , t e cei ing o(
3.0000001 is 4. If we consider all numbers as a con-
muous range from minus infinity to plus
moves the number right to an integral infinity ( Figure 4-27 ) , this function
value.
-
t
u
Chapter 4 Functions 189
-
ceil ( 1.9 ) returns -2.0
1.0
ceil ( 1.1 ) returns
Floor Functions
A floor is the largest integral value that is equal to or less than a number ( see
Figure 4 - 28). For example, the floor of 3.99999 is 3.0. Again, looking at num -
bers as a continuum, this function moves the number left to an integral value.
floor (-1.1)
^ is -2.0 ^ .flooris +(+1.01.9^)
. -2.0 -1.0 0 1.0 2.0 +
-
floor ( 1.1) returns
floor ( 1.9) returns
2.0
1.0
-
Truncate Functions
The truncate functions return the
same as floor function for positive
..
integral in the d rect on of 0. They are the
numbers and the same as ceiling function
are
for negative numbers. Their function declarations
-
trunc ( 1.1) returns 1.0
trunc ( 1.9 ) returns 1.0
-
Round Functions
The round functions return the nearest integral value . Their function declara -
tions are
In addition to the real round functions, C provides two sets that return a long
int or a long long int . Note that there is not a round function that returns an
int .1 heir function declarations are
Power Function
The power ( pow ) function returns the value of the x raised to the power y
that is, xy . An error occurs if the base ( x) is negative and the exponent ( y ) is
not an integer, or if the base is zero and the exponent is not positive. The
power function declarations are
Random Numbers
A random number is a number selected from a set in which all members have
the same probability of being selected. Random numbers are useful in many
areas of computer science. Two examples are application testing and gaming.
Next
Previous Random Number Number
Number
Function
_
We can create the same series or a different series
— seri of random numbers
each time we run our program . Some programs require that the series be the
same each time the program runs; others require that the series be different
each time. We control the type of series by our choice of
seeds.
Random Numbers in C
c provides two functions
^ and rand m
° ^
Seed Random Number Function
to
-
build a random number series, seed random
Th e functions are found in stdlib.h .
srand (997 );
srand(time ( NULL));
Whichever series we need , srand shoidd be called only once lor each ran -
dom number series, usually only once in a program.
srand must be called only once for each random number series.
Numbers (continued )
PROGRAM 4 -9 Creating Tempora Random
|
Results:
F i r s t Run
9641
16041
6350
Second Run
31390
31457
21438
c-
continue*
\\
Chapter 4 Functions 195
Results:
First Run
10575
22303
4276
Second Run
10575
22303
4276
rand ( ) % 51
Modulus works well when our range starts at 0. But what il we need a
different range? In that case, we must shift the result . For example, suppose
is 0.99999999.
2. Note that 0.0 is included in the range but 1.0 is not. The maximum number
196 Section 4.5 Standard Functions
.-
correct range we determine our modulus
divjsor ( g) and then adding the starting point
« number. Th» , f«
makes the modulus di\isor 5.
I " *. 3 8'
oh RAND
Scaling
Shifting
4 RAND MAX *
7 RAND _ MAX
range = ( 20 - 10 ) + 1;
/ / 11
randNo = rand( ) % range + 10;
continued
iii
Chapter 4 Functions 197
Results:
10 11 16
Results :
0.782006 0.264260 0.348460
To scale a random real number into a range other than 0.0 to 1.0 ( exclu-
sive ) , we first generate the random number as we did in Program 4 - 12. Then
we re- scale it to the new range. For example , to generate random real num -
bers in the range 100.0 to 300.0 ( exclusive ), we multiply the random number
by the range ( 200.0 ) and then add the minimum ( 100.0 ). Using the results
from Program 4- 12, our random real numbers would be 256.4012, 152.852,
and 169.692.
4.6 Scope
Scope determines the region of the program in which a defined object is
—
visible that is, the part of the program in which we can use the object’s
name. Scope pertains to any object that can be declared , such as a variable or
a function declaration. It does not pertain directly to precompiler directives,
such as define statements; they have separate rules. Scope is a source pro-
gram concept: It has no direct bearing on the run - time program .
To discuss the concept of scope, we need to review two concepts. First , a
block is zero or more statements enclosed iin a set of braces . Recall that a
functions body is enclosed in a set of braces; thus, a body is also a block. A
block has a declarations section and a statement
section . This concept gives
us the ability to nest blocks within the
body of a function and allows each
block to be an independent group of
statements with its own isolated defini-
tions. Second , the global area of
our program consists of all statements that
are outside functions. Figure 4
-32 isi a graphical representation of the con-
cept of global area and blocks.
An object 's scope extends from its
variable is in scope if it is visible - ion until the end of its block . A
declarati
to the statement being examined . Variables
are in scope from their point
of declaration until the end of their block.
.
k i V V|i
Chapter 4 Functions 199
} // End of main
} // fun
Global Scope
The global scope is easily defined . Any object defined in the global area of a
program is visible from its definition until the end of the program . Referring
to Figure 4- 32 , the function declaration for fun is a global definition . It is vis-
ible everywhere in the program .
Local Scope
Variables defined within a block have local scope . They exist only from the
point of their declaration until the end ol the block ( usually a function ) in
which they are declared. Outside the block they are invisible.
In Figure 4 - 32 , we see two blocks in main . The first block is all of main .
Since the second block is nested within main , all definitions in main are visible
J
200 Section 4.7 Programming Example — IncrementalDeveJopmerU
inner
\ aridutc ara -
these circumstances, the integer floating-point variable a m the nested block.
of the
ion until the declaration the nested block. Any
At that point , main’s a can
no longer be referenced m
a will get the float version . Once we
statement in the block that references float a is no longer in scope and the
, the
reach the end of the nested block
integer a becomes visible again .
Although mains variables are visible inside the nested block, the reverse
is not true. The variables defined in the block, a , y , and z , exist only for the
duration of the block and are no longer visible after the end of the block.
,
Within the function fun , which is coded after main only its variables
and any global objects are visible. Thus, we are free to use any names we
want . In Figure 4- 32, we chose to use the names a and y even though they
had been used in main . This is an acceptable practice; there is nothing wrong
with it .
Calculator
i
getData
i add
I printRes
i
FIGURE 4 - 33 Calculator Program Design
Results:
: 8 13
Please enter two integer numbers
* * getData: a = 8 ; b = 13
* * main: a = 8 ; b = 13
Program 4- 13 Analysis It is very rare that we don't make some syntactical errors
when writing even the sim-
the
plest programs. By compiling program in its simplest state, we are able to easily
correct minor errors. We have also verified that the communication between functions
is valid; that is, we verify that the variable types match the parameter types and that
the data are being properly passed down and back up .
In this first incremental program, we pass the addresses of the two input variables
to the get data function. It then reads and prints the input to verify that the data are cor-
rectly read. When we are back in main, we again print the input data to verify that it
was properly passed up to main. Because we were careful to align the print formatting,
we can easily check that the values are the same.
One point to note in the program is that we prefix temporary print statements with
two asterisks. This allows us to tell the difference between permanent output and debug-
ging output. As we move to the next increment, we remove the temporary statements
from the debugged functions.
Results:
Please enter two integer numbers: 8 13
* * add : 8 + 13 = 21
* *main : 8 + 13 = 21
Program 4- 14 Analysis Once again, we include print statements to verify that the add function received the
correct data and properly returned it to main.
continue
Chapter 4 Functions 205
Structure Charts
The structure chart is the primary design tool for a program . Therefore, you
should create it before you start writing your program . An analogy will help
you understand the importance of designing before you start coding.
Assume that you have decided to build a house. You will spend a lot ol
time thinking about exactly what you want . How many rooms will it need ? Do
you want a family room or a great room ? Should the laundry he inside the
house or in the garage? To make sure everyone understands what you want ,
you will prepare formal blueprints that describe everything in detail. Even if
you are building something small , like a dollhouse for a child or a toolshed
for your back yard , you will make some sketches or plans.
Figuring out what you want in your house is comparable to determining
the requirements for a large system. Drawing up a set of blueprints parallels
the structure chart in the design of a program . All require advance planning;
only the level of detail changes.
Professional programmers use the structure chart for another purpose.
In a project team environment , before programmers start writing a program ,
they must have its design reviewed . This review process is called a structured
walk- through . The review' team consists of the systems analyst responsible for
the area of the project , a representative of the user community, a system test
engineer, and one or two programmers from the project.
The design walk- through serves three purposes: First , it ensures that you
understand how your program fits into the system by communicating your
design to the team . If the design has any omissions or communication errors ,
]
the team should detect them now . If you invite programmers w ho must inter-
! ?
face with your program , you will also ensure that the interprogram communi -
cation linkages are correct.
Second , the walk- through validates your design . In creating your design ,
you will have considered several alternative approaches to writing your pro-
gram . The review team will expect to see and understand the different
designs you considered and hear why you chose the design you are proposing.
They will challenge aspects of the design and suggest approaches you may
not have considered . The result of the review will be the best possible design .
Finally, the walk- through gives the test engineer the opportunity to assess
your programs testability. Ibis in turn ensures that the final program will be
robust and as error-free as possible.
208 Section 4.8 Software Engineering
Structure Chart Rules and Symbols that we use to write a structure chart.
Figure 4-34 shows the various
Although we include all symbols
symbols
here for completeness, we w ll d scuss only ..
fh two coloredto thesymbols
Indesigning
addition
. We discuss the other symbols m Chapters 5 and 6„.
symbols, we will discuss several
rules that we follow i
our structure chart .
Function Symbol
( Figure .4.-34 ) represents ,a function
Each rectangle in a structure chart see .
C libraries are not shown . The
that we write . Functions found in the standard
name in the rectangle is the name we
will give to the function when we write
the program . It should be meaningful . The software engineering principle
known as intelligent names states that the names used in a program should be
self - documenting ; that is , they should convey their intended usage to the
reader. In general , we use intelligent names for both functions and for data
names within our program .
(a) Function
T +T
( f ) exclusive or
it t:
( g) data flow ( h) flag
ou that we
have explained that all names should be descriptive, we will
ture
" cZriTU tbeCaUSe
*
^ M j ‘
WC W3nt t 0 concen rate on the format of a struc-
3 ) drt cu ar Program .
*
The names in Figure 4-35 iden-
*
tifv the various
tity vaHn modules for discussion .
Reading Structure Charts
consisT
( main )
According
oflbeelb W ^
to the left-right rule
’ In FiSure 4 35> Programme
'
Program i
Name
A B C
A1 A2
It is not necessary to show data flows ( Figure 4 - 34 g) and flags ( see Fig-
ure 4 - 34 h ) , both of which represent parameters, although it may be helpful
in certain circumstances. If they are shown , inputs are on the left of the ver-
tical line and outputs are on the right . When they are included , the name of
the data or flag should also be indicated .
The structure chart rules described in this section are summarized in
Table 4 - 1 .
^
andT HagU Sh Wn n the left of the vertical
3re
nous and Hags are shown on°the °
flows right . ^ °utPut
TABLE 4- 1 Structure Chart Rules
Chapter 4 Functions 211
Functional Cohesion
One of the most difficult structured programming concepts for new program -
mers is knowing when and how to create a function .
Functional cohesion is a measure of how closely the processes in a
function are related . A function that does one and only one process is func -
tionally cohesive. A function that contains totally unrelated processes is coin -
cidentally cohesive. We provide a few rules here to help you write cohesive
functions. For a complete discussion of the topic , see Page -Jones. *
Before we discuss the rules, however, you should understand why the
concept is important . Following are the three primary reasons for using struc -
turally cohesive functions:
1 . Correctness : By concentrating on only one thing as you write a function ,
you will be less apt to make an error. It is much easier to get a simple task
right than a complex task .
2. Maintainability: Production programs can live for years. The better struc -
tured a program , the easier it is to change. When programs are not well
structured , making a change in one part of the program often leads to
errors in other parts.
3. Reusability: Some processes are so common that they are found in many
programs. Good programmers build libraries of these functions so that
they don ’t have to reinvent the function each time it is needed . I his not
only leads to quicker program development hut also reduces debugging
time , since the library functions have already been debugged .
f
.
3 IVleilir Page Jones, The Practical Guide to Structured Systems
- Design , 2nd ed., Chap. 6 .
( Englewood Cliffs , N .J .: Prentice Hall , 1988 ) .
i
212 Section 4.8 Softwore Engineering
it should call subfunctions
to calculate taxes, and
taxes together in a function design seen in Figure 4 - 37.
tax. This is
to calculate each individual
1
calculate
Taxes
State Federal
FICA SDI SUI Witholding Witholding
In One Place
The corollary rule is that the one thing a function does should be done in
only one place. If the code for a process is scattered in several different and
unrelated parts of the program , it is very difficult to change. T herefore, all the
processing for a task should be placed in one function and , il necessary, its
subfunctions. This is the reason we created the function calculateTaxes in
Figure 4- 37.
An example of scattered code common among programmers is found in
printing reports. Suppose that we needed to write a program that , among
other things, prints a report that includes a heading, some data with a total,
and then an end -of - report message. A well -structured solution is seen in Fig-
ure 4 - 38. It is quite common , however, to find the statements for each of
these subtasks scattered in main and other parts of the program .
print
report
Testability
-
"
.
next section.
^ for this the
3
in
we -s,
Let us simnlv m |
a J
" 77from« TO
separately
| * 7/ a
h program to be
"
tested
the rest of
the program.
Chapter 4 Functions 213
Top-Down Development
It we have designed our program using structured programming concepts and
a structure chart , we can then proceed to implement it in a top-down fashion .
Referring again to Figure 4 - 35 , “Structure Chart Design , ” a top-down
implementation starts with the code for main only, shown in the structure
chart as ProgramName. The code for the first compile and test is shown in
Program 4 - 16.
Example (continued )
PROGRAM 4- 16 Top-down Development
36 {
37 / / statements
printf ( " In process : \ n
");
38
39 return 0 ;
40 } / / process
41 / * ========== =====
endOfJob
42 Stub for endOfJob
43 */
44 int endOfJob (void )
45 {
46 // Statements
47 printf( "In endOfJob: \n" );
48 return 0 ;
49 > // endOfJob
Results:
Begin program
In initialize:
In process:
In endOfJob:
Program 4 - 16 Analysis Note that this program has only the first four boxes from the structure chart:
ProgramName ( main), initialize, process, and endOfJob. For each of
main' s subfunctions, all that is included is a stub . A stub is the skeleton of a function
that is called and immediately returns. Although it is a complete function, it does
nothing other than to establish and verify the linkage between the caller and itself. But
this is a very important part of testing and verifying a program. At this point the pro-
gram should be compiled, linked, and run. Chances are that you will find some minor
problems, such as missing semicolons or errors between the function declarations
and the function definitions. Before you continue with the program, you should cor-
rect these problems.
t
Chapter 4 Functions 215
> // divide
> // divide
.
2 It is a compile errordefine local variables with the same identifiers as
to
formal parameters, as shown below.
} // divide
.
3 Using a void return with a function that expects a return value or using a
with a function that expects a void return is a compile error.
return value
.
4 Each parameter’s type must he individually specified; you cannot use
multiple definitions like you can in variables. For example, the following
is a compile error because y does not have a type:
printHello ; / / Not a c a l l
printHello ( ) ; / / A valid call
9. It is a compile error if the type of data in the return statement does not
match the function return type.
10. It is a logic error to call srand every time you call rand .
4.11 Summary
In structured programming,
a program is divjded
Each module is designed to d ^ modu cs
,
0 a specific
task ,
Modules in C are written as
functions.
Chapter 4 Functions 217
Each C program must have one and only one function called main .
A function can return only one value .
A function can he called for its returned value or for its side effect .
The function call includes the function name and the values of the actual
parameters to provide the called function with the data it needs to perform
its job .
Each actual parameter of the function is an expression . The expression
must have a value that can he evaluated at the time the function is called .
A local variable is known only in a function definition . Ihe local variables
do not take part in communication between the calling and the called
functions.
The general format for a function definition is
_
return type function name (parameter list )
{
// Local Declarations
// Statements
} _
// function name
—
6. The process of dividing a program into functions which in turn are
divided into functions until they consist of only elementary processing
that is intrinsically understood and cannot be further subdivided is
known as
—
a. charting
b. flow charting
c. factoring
.
d programming
e. structuring
/ . Which of the following statements about function declaration and defini-
tion is true?
a . The function call is found in
the called function .
b. The function declaration
c. 1 he function definition is
requires that the parameters be named.
done with a function declaration .
d. 1 he function
definition contains executable statements that perform
the function ’s task.
e. The function definition
header concludes with a semicolon ( ; ).
Chapter 4 Functions 219
.
8 Which of the following is not a part of a function header ?
.
a name
b. parameter list
.
c return type
d. title
9. Which of the following statements about function parameters is true?
a. Empty parameter lists are declared with the keyword void .
b. If there is only one parameter, the function list parentheses are not
required.
.
c In the definition of a function, the parameters are known as actual
parameters.
.
d Parameters are separated by semicolons.
.
e The parameters in a function definition are defined in the function’s
body ( local declaration section ).
10 . Which of the following statements about local variables is false?
a A .local variable’s value may be returned through a return statement .
.
b Local variables are defined inside a function.
c. Local variables cannot he referenced through their identifiers outside
the function.
.
d Local variables may be initialized with an initializer.
.
e Local variables’ names can he the same as the function s para-
’
meter names .
1 1 To tell the compiler to store data at an address, use the
.
.
address operator ( & )
a
.
b array operator ( [ ] )
.
c dereference operator ( # )
d. indirection operator ( * )
.
e pointer operator ( )
A
.
12 The function that returns the absolute value of a long integer is
.
a abs
b. dabs
.
c fabs
d. labs
.
e tabs
13. Which of the following statements will generate a random number in the
range 30-50 ?
.
a rand ( 33 )
.
b ( rand ( ) % 20 ) + 1
.
c ( rand ( ) % 21 ) + 20
.
d ( rand ( ) % 21 ) + 30
.
e ( rand ( ) % 51 ) + 1
220 Section 4.12 Practice Sets
about structure charts is false?
14. Which of the following statements
replacement for flowcharts.
a . Structure charts are a design tool for a program .
b. Structure charts are the primary
in a structured walk- through to validate the
c. Structure charts are used
design.
to assess the testability of a program.
d. Structure charts can be used before you start writing a program
e. Structure charts should be
created
Exercises
function definition :
1 5. Find any errors in the following
return z;
> // fun
return z;
> // fun
return ( t + 3 );
}
return z;
} / / fun
1 S. Find any errors in the
following function definition :
return ;
> // fun
.
19 Find any errors in the following function declarations:
a. i n t sun ( i n t x , y ) ;
b. i n t sun ( i n t x , i n t y )
c. void sun ( v o i d , v o i d ) ;
d . void sun ( x i n t , y f l o a t ) ;
.
20 Find any errors in the following function calls:
a . void f u n ( ) ;
b. fun ( void ) ;
c. void f u n ( i n t x , i n t y ) ;
d . fun ( );
23. Evaluate the value of the following expressions when x is 3.5 , 3.45 , 3.76 ,
3.234 , and 3.4567 :
a. f l o o r ( x * 10 + 0 . 5 ) / 10
b. f l o o r ( x * 100 + 0 . 5 ) / 100
c. f l o o r ( x * 1000 + 0 . 5 ) / 1000
.
24 Define the range of the random numbers generated by the following
expressions:
a . rand ( ) % 10
b.rand ( ) % 4
.
c rand ( ) % 10 + 1
.
d rand ( ) % 52
.
e rand ( ) % 2 + 1
.
f rand ( ) % 52 5 -
data?
Jill
5 as
25. What would he printed from Program 4- 17 when run using 3
222 Section 4.12 Practice Sets
25
PROGRAM 4-17 Program for Exercise
1 # include <stdio
.h>
2
3 // Function Declarations
, int y );
4 int strange ( int x
5
6 int main (void )
7 {
8 // Local Declarations
9 int a;
10 int b;
11 r; int
12 s; int
13
14 // Statements
15 scant("%d %d ", & a, & b );
16 r = strange ( a, b );
17 s = strange ( b, a );
18 printf( "%d %d ", r, s );
19 return 0;
20 > // main
21 / / === strange =====
22 int strange (int x , int y)
23 {
24 // Statements
25 return (x
// strange
- y);
26 >
26. What would be printed Irom Program 4- 18 when run using 3 5 4 6 as data?
continued
Chapter 4 Functions 223
1
PROGRAM 4- 1 8 Program for Exercise 26 (continued )
14 int s;
15 int t;
16 int u;
17 int v;
18 i
19 // Statements
20 scanf( " % d %d %d %d " , & a , & b , &c , &d );
21
22 r = strange ( a , b );
23 s = strange ( r , c );
24 t = strange ( strange ( s, d ), strange ( 4 , 2 ));
25 u = strange (t + 3 , s + 2);
26 v = strange ( strange (strange ( u , a ), b ), c );
27
28 printf( " % d %d %d %d %d " , r, s, t , u, v);
29 return 0;
30 > // main
31 // strange
32 int strange ( int x , int y )
33 {
34 // Local Declarations
35 int t ;
36 int z;
37
38 // Statements
39 t = x + y?
40 z = x * y;
41 return ( t + z);
42 > // strange
.
27 Draw the structure chart for Program 4 - 19. What output does it produce ?
11 int b;
12 int c;
13
14 // Statements
15 a = 10;
16 funB (a);
17 b = 5;
18 c = funA ( b);
19 funB(c );
20 printf("%3d %3d %3d'\ a, b, c );
21 return 0;
22 } // main
23
24 int funA (int x )
25 {
26 // Statements
27 return x * x;
28 > // funA
29
30 void funB (int x)
31 {
32 // Local Declarations
33 int y ;
34
35 // Statements
36 y = x % 2;
37 x /= 2;
38 printf ("\n%3d %3d\n" , x , y);
39 return ;
40 > // funB
Problems
28. Write a Junction to print your name, as shown below.
Write a call as it
would be coded in a calling function, such as w a i n .
*************** i<'
i'
1'iii i i ii i i +
'''''''' **
*
*
* Your Name Here *
*
*
**************** ***
***********
Chapter 4 Functions 225
29. Write a program that generates a random number from the following set :
1, 2, 3, 4, 5, 6
30. Write a program that generates a random number from the following set :
1, 4 , 7 , 10, 13, 16
c
a
FIGURE 4- 39 Problem 39
c2
area =
Projects
40. Prepare a payroll earnings statement For the sales Force at the Arctic Ice
Company. All of Arctics employees are on a straight commission basis oi
12.5% of sales. Each month , they also receive a bonus that varies
depending on the profit For the month and theii length of service. The
sales manager calculates the bonus separately and enters it with the
salesperson’s total sales for the month. Your program is also to calculate
the withholding taxes and retirement lor the month based on the lollow -
ing rates:
a . 25% Federal withholding
h . 10% State withholding
c . 8% Retirement plan
Use the test data in Table 4 - 2 to test the program .
.
41 \ \ rite a program that , given a
beginning balance in your savings account ,
calculates the balance at the end of 1 year. The
interest is 5.3% com -
pounded quarterly. Show the interest
earned and balance at the end of
each quarter. Present the data in tabular
columns with appropriate head-
ings. Use separate functions to
compute the interest and print the balance.
T
!
42. I he formul a for convert ing centigr ade temper atures to fahren heit is
180.0
F = 32 + C
100.0
Write a program that asks the user to enter a tempera ture readin g
in centigr ade and then prints the equiva lent Fahren heit value. It then
asks the user to enter a Fahren heit value and prints out the equiva lent
centigr ade value. Run the progra m severa l times . Be sure to include at
least one negativ e tempera ture readin g in your test cases. Provid e sepa -
rate functio ns as needed by your design. One possib le design is shown
in Figure 4 - 40 . ( Your main functi on should have only functio n calls
.)
temperatures
Celsius
i Fahrenheit
l
get
Celsius i Celsius to
Fahrenheit i print
Fahrenheit i get
Fahrenheit i Fahrenheit
to Celsius i print
Celsius i
FIGURE 4 - 40 A Possible Design for Project 44
may he written
43 Write a program that uses standar d functio ns. The program
.
entirel y in main and must follow the pseudo code shown in Algori thm 4- 1 .
riate caption s , and align the data .
Give the output approp
Length XXX ft
Width XXX ft
Area XXX square ft
CHARGES
I he program's design should use main and at least the six functions
described below:
n
a. Read data from the keyboard. This function is to use addresses to read
all data and place them in the calling function’s variables.
b. Calculate values. This function calls three subfunctions. Each func -
tion is to use addresses to store their results.
Calculate the installed price. This function calculates area, carpet
cost , labor cost, and installed price. The installed price is the cost
of the carpet and the cost of the labor.
Calculate the subtotal. This function calculates the discount and
subtotal.
Calculate the total price with discount and tax. This function calcu -
lates the tax and the total price.
c . Print the result. Use two subfunctions to print the results: one to print
the measurements, and one to print the charges.
lest your program with the test data shown in Table 4 - 3.
1 23 13 12 $ 14.20
2 35 8 0 $ 8.00
3 14 11 10 $22.25
Objectives
To understand how decisions are made in a computer
To understand the logical operators: and, or, and not
To understand how a C program evaluates a logical expression
To write programs using logical and comparative operators
To write programs that use two - way selection: if...else statements
To write programs that use multi - way selection: switch and else. .. if
To understand Cs classification of characters
To write programs that use Cs character functions
To be able to design a structure chart that specifies function selection
231
232 Section 5.1 Logical Data and Operators
Logical Data in C
Traditionally, C had no logical data type C
; programmers used other data
data . If data y’alue is zero, it is consid
types , such as hit , to represent logical
3 *
ered false. If it is nonzero, it is considered true . This concept of true and false
on a numeric scale is seen in Figure 5 - 1.
false
true
J true
Logical Operators
t has three logical operators for combining logical values and creating ..new .
logical values: not , and , and or. A common way to show' logical relationships is in
trill ta es '
^rutJ ^
1 ta es l st he values that each operand can assume and the
* *
resulting value. The truth tables for logical operators are shown in Figure 5- 2.
|
not Operator
^ e 15 in the Prece-
^ hT 01^
denct T
^
A r - operator vvit
^ precedenc
tme value to false and
false value to tme ' ' ^
3 3
Chapter 5 Selection — Making Decisions 233
not and or
x y xlly
false true false false false false false false
true false false true false false true true
true false false true false true
true true true true true true i
and Operator
The and operator ( & & ) is a binary operator with a precedence of 5 . Since and
is abinary operator, four distinct combinations of values in its operands are
possible. The result is true only when both operands are true ; it is false in all
other cases.
or Operator
The or operator ( | | ) is a binary operator with a precedence of 4. Again, since
it is a binary operator, four distinct combinations of values in its operands are
possible. The result is false if both operands are false ; it is true in all
other cases.
5j!
true II ( anything)
4 4
true
false
i_
and at the same time wants to
to find the value of the logical expression
increment the value of the second operand :
x && y ++
Everything works fine when the first operand is true . But if the first oper-
and is false , the second operand will never be evaluated and therefore will
never be incremented. It is the same with the following or example. If the
first operand is true , the second operand will never be incremented .
x || y + +
continued
l
A
Chapter 5 Selection — Making Decisions 235
Results:
1 AND Is 1
1 AND 0: 0
0 AND Is 0
1 OR 0: 1
0 OR Is 1
0 OR 0: 0
NOT 1 AND NOT 0: 0
NOT 1 AND 0: 0
1 AND NOT 0: 1
Program 5 - 1 Analysis Each print statement in Program 5- 1 contains a logical expression that evaluates
either to true or false. Make sure you understand why each of the expressions evalu-
ates as shown in the results . Note that even though the results are of type Boolean
,
they must be printed as integer d .
(% ) This is because while the expressions evaluate to
true or false, there is no conversion code for Boolean. Therefore, when we print
them,
we must use the integer field specification .
Comparative Operators
In addition to logical operators, C provides six comparative operators. Com
-
Meaning Precedence
Type Operator
< less than
<= less than or equal 10
Relational > greater than
>= greater than or equal
== equal 9
Equality not equal
complement >=
< 4 r
complement r
> 4
_ complement
t
\ -
II we want to simplify an expression involving the not and the less than
operator, we use the greater than or equal operator. This concept is impor -
tant lor simplifying expressions and coding expressions in good, clear
style. Table 5 - 1 shows an example of each expression and its simplified
version.
!( x > y) x <= y
!( x 1= y) x == y
! ( x <= y ) x > y
! ( X >= y )
x < y
I ( X == y )
x 1= y
Results:
5 < -
3 is 0
-
5 == 3 is 0
-
5 != 3 is 1
5 > --
3 is 1
5 <= 3 is 0
-
5 >= 3 is 1
, make
Program 5 - 2 Analysis Program 5 - 2 follows the same patterns we saw in Program 5 1 . Once again
-
sure that you understand why each of the representati ons in the results evaluates to
true or false .
if . . . else
C implements two-way selection with the if . . . else statement. An if . . . else state-
ment is a composite statement used to make a decision between two alterna -
tives. Figure 5-7 shows the logic flow for an if . . else statement . The expression
can be any C expression. After it has been evaluated, if its value is true ,
statement 1 is executed: otherwise, statement 2 is executed. It is impossible
for both statements to be executed in the same evaluation.
false true
(zero) (Izero) if (expression )
expression
statement 1
else
statement2
i statementl
i statement2
«#
.
3 The expression can have a side effect .
4. Both the true and the false statements can he any statement ( even
.
another if . . else statement ) or they can he a null statement .
5. Both statement 1 and statement 2 must he one and only one statement.
Remember, however, that multiple statements can he combined into a
compound statement through the use of braces.
6. We can swap the position of statement 1 and statement 2 if we use the
complement of the original expression .
if ( i
a ++ ;
— 3) The semicolons |
belong to the
expression statements, I
else not to the
if ... else statement
a — ;
expressions that read data as a side effect . As an example , consider what hap -
pens when we are writing a series of numbers, and when have
we written ten
a line
numbers we want to go to a new line. A simple solution increments
count and tests the limit in the same statement . The
code for this logic could
he written as shown in the following example .
240 Section 5.2 Two-Wayj>elecfion
printf( ... );
. The fact that any state-
Rules 4 and 5 in Table 5-2 are closely related
straightforward , but often new C program -
ment can be used in an if ...else is
compound statement for complex logic. The use of
mers will forget to use a
compound statements is seen in Figure 5 - 9. The first example shows a com -
pound statement only for the true condition . The second example shows
compound statements for both conditions. Note that the compound state-
ments begin with an open brace and end with a close brace
.
if '( j != 3) if (j != 5 && d == 2)
r< b++;
{
j++;
printf("%d\ b);
} // if Compound
;
d
—
printf("%d%d " , j, d )
else statements } // if
printf( " %d \ j ); are treated as else
one statement r< D --;
X - d++;
printf( ” %d%d " , j, d )
i } // else
I he sixth rule states that the true and false statements can he exchanged
hy complementing the expression . Recall from our discussion of relational
operators on Comparative Operators in Section 5.1 that any expression can
he complemented . When we find that we need to complement an if . ..else
statement, all we have to do is to switch the true and false statements. An
example ol this operation is shown in Figure 5 - 10.
if (expression)
i\
else else
— —
If the else condition is not required that is, if it is null it can be omit -
ted . This omission can he shown as a null else statement ( a null statement
consists of only a semicolon ) ; more commonly, the else statement is simply
omitted entirely, as shown in Figure 5 - 11 .
if ( expression)
{
\ if ( expression )
{
N
} // if } // if
else
/
De Morgans Rule
When we design the logical flow of a program, we often have a situation in which the
not operator is in front of a logical expression enclosed in the parentheses
. Human
engineering studies tell us, however , that positive logic is easier to read and under -
stand than negative logic. In these cases, therefore , if we want to make the total
expression positive by removing the parentheses, we apply the not operator in this
directly
to each operand. De Morgan 's rule governs the complemen ting of operators
situation. This rule is defined as follows:
-
When we remove the parentheses in a logical expression preceded by the not opera
tor, we must apply the not operator to each expression
while changing the logical
or ( | | ) to and ( & & ).
operators — that is, changing and ( & & ) to or ( | | ) and changing
continued
t
, (x || y ) -> ix && iy
Null
Statement
Results:
Please enter two integers: 10 15
10 <= 15
Nested if Statements
As we stated previously, for the if . ..else , the statements may be any statement,
including another if . ..else . When an if .. .else is included within an if . . else , it
is known as a nested if statement . Figure 5 - 1 3 shows a nested ij statement.
There is no limit to how many levels can be nested, but if there are more than
three, they can become difficult to read.
expression
1 if ( expression 1 )
if ( expression 2 )
Results:
Please enter two integers: 10 10
10 == 10
Program 5 - 4 Analysis You should be able to follow this simple program with relative ease . However, it does
contain a subtle software engineering principle.
^ '^
greater than condition to the outer else (see
, Study the if statements ( 17 and 18)
, er 6 j- ° We C[ eC or 9rea er than? The answer is that we default the
,
Statement 22). When coding a two-way
" ' t T11 *
Y
^
statements, code the most probable
expression
if (expression!) 1
if ( expression2 )
expression
2
statement 1
else
statement 2 | statement 1
statement 2
Compiler pairs
'
this if and else
( a) Code
T ( b ) Logic Flow
Simplifying if Statements
By now you should recognize that if ..else statements can become quite
com-
expression
if (expression 2 )
2 statement 1
statement 2 > // if
else
statement 1
statement 2
T
( a) Logic Flow (b) Code
Since the simplified statements in Table 5 - 3 are new, let’s look at them a
little more carefully. The expression a ! = 0 evaluates to either true or false. If
a is anything other than zero, then the
expression is true and statements is
executed . However, any integer can he used to
represent true or false . In this
ease , it a contains any value other
than zero, it is true ; otherwise, it is false.
et igure 1 . ) Therefore , since we want to
a ,s not zero, and since anything
execute statement 2 whenever
( ) t at is , as a is true .
other than zero is true , we code the expres -
Similarly, if we want to test for a equal to
z-ero, we stmply complement the
expression , making it ! a .
Chapter 5 Selection — Making Decisions 247
Conditional Expressions
( provides a convenient alternative to the traditional if ..else for two- way
selection—the ternary' conditional expression has a precedence of 3 ( see
inside front cover ).
I he conditional expression has three operands and a two- token opera -
tor. Each operand is an expression. The first token, a question mark ( ? ) , sepa -
rates the first two expressions. The second token, a colon ( : ), separates the
last two expressions. This gives it the following format:
a == b ? C++ : d++;
.
In this expression, only one of the two side effects take place II a is equal
to b, C++ is evaluated and one is added to c; expression2 is ignored On the .
other hand, if a is not equal to b, then d++ is evaluated and 1 is added to d ;
expression!is ignored If this sounds much like a simplified if . else , its
. .
because it is! Figure 5 - 16 shows the flowchart for the expression, which
could easily he coded as an if ...else.
a == b ? C++ : d++ ;
.
tn
write ten a variable that indicates either the
is
fiieFlag
,, , ,, „ ,
5
M' ? 10 : 15 );
, ,
necessary but make he Women more readable.)
numPerLine = (fiieFlag
nest conditional expressions, it is not
One final note: Although we can
complex, remember the MSS
recommended . When the logic begins to get
principle and use nested if statements .
Income in Bracket Tax Rate Tax Income in Bracket Tax Rate Tax
(1 ) 10,000 2% 200 ( 1 ) 10,000 2% 200
( 2) 10,000 5% 500 (2) 8,000 5% 400
(3) 3,000 7% 210 (3 ) none 7% 0
Total Tax 910 Total Tax 600
TABLE 5 - 4 Examples of Marginal Tax Rates
The design ( or the program to calculate taxes is shown in the structure
chart in Figure 5 - 17. The design has only four functions besides main . The
notation lor bracketTax is somewhat unusual: In the final code, it is called
live times in one expression . Therefore, we show it as a nested set of calls.
I he design lor each ol the functions is shown in
Figure 5 - 18. The logic
tor calculating the tax information requires three
major steps: ( 1 ) Calculate
, , ,
the taxable income. Taxable income is total
income adjusted for the number
° dcl n' c lts - n °ur example, we subtract $ 1,000 for each dependent .
. , ^a cu ate du taxes The tax due
- calculation uses the progressive
at thc be8inning of the section . Our example uses
fivebracket
^
( 3) Calculate the taxes due
or to be refunded , Note
withheld than are due , the taxes due is
a negative
that if more taxes were
number, indicating a refund.
Chapter 5 Selection — Making Decisions 249
calculate
tax
r
bracketTax j bracketTax
\ bracketTax bracketTax bracketTax
i
FIGURE 5 - 1 7 Design for Calculate Taxes
taxDue
>= tax = 0.0
o.o
?
c Return ) ?
?
expr: startLimit < income <= stopLimit ( Return )
8 3 20001 - 30000 7%
i
con tinned
\ it
Chapter 5 Selection — Making Decisions 251
92
numOfDpndnts );
93 scanf ( "%d ",
94 return ;
95 } // getData
96
97 /* === ===== calcTaxes === —
due.
98 This function calculates the taxes
& taxPaid
99 Pre Given income , numOfDpndnts ,
- tax , and tax due
100 Post Tax income, total
101 calculated
102 */
103 void calcTaxes (double totlnc ,
104 double taxPaid ,
105 int numOfDpndnts,
106 double* taxablelnc ,
107 double* totTax ,
108 double* taxDue )
109 {
110 // Statements
111 *taxablelnc = totlnc
112 ( numOfDpndnts* DEDN PER DPNDNT);
113 *totTax
114 bracketTax( *taxablelnc , LOWEST , LIMIT 1 , RATE1 )
115 + bracketTax(*taxablelnc , LIMIT1 , LIMIT2 , RATE2)
116 + bracketTax(*taxablelnc , LIMIT2 , LIMIT3 , RATE3 )
117 + bracketTax(*taxablelnc, LIMIT3, LIMIT4, RATE4)
118 + bracketTax( *taxablelnc , LIMIT4 , HIGHEST, RATE5 );
119
120
121
*taxDue = *totTax
return ;
taxPaid ;-
122 } // calcTaxes
123
124 /* - printlnformation =====
125 This function prints a table showing all information.
126 Pre The parameter list
127 Post Prints the table
128 */
129 void printlnformation
(double totallncome ,
130
double income ,
131
int numDpndnts ,
132
double totalTax ,
i
continue
Chapter 5 Selection — Making Decisions 253
Results:
Enter your total income for last year: 15000
Enter total of payroll deductions: 250
continued
254 Section 5.3 Multiway Selection
multiway
expression
switch ( expression )
{
ik
-
case constant 1: statement
:
statement
case constant-2: statement
statement
case constant-n: statement
:
statement
default : statement
:
statement
} // end switch
Although the switch expression can use any expression that reduces
to
common a unary expression in the lorm of an
an integral value, the most is
arate case label isdefined . Figure 5- 20 also shows the format for the case
case is zero or more statements. Every-
label Associated with each possible label is a sequence. The case label
thing from a case label to the next case
executing the code .
simply provides an entry point to start
the case label. It is executed when-
The default label is a special form of
matches the value in the switch expression.
ever none of the other case values
Note , however, that default is not required . If we do not provide a default , the
compiler will simply continue with the statement after the closing hrace in
the switch .
The switch statement is a puzzle that must he solved carefully to avoid
confusion. Think of the switch statement as a series of drawbridges, one for
each case and one for the default . As a result of the switch evaluation, one
and only one of the drawbridges will he closed , so that there will be a path for
the program to follow. ( II none of the drawbridges is closed , then the state-
ment is skipped and the program continues with the next statement after the
switch . ) The switch flow is shown in Figure 5 - 2 I .
fen feF)
^
sej)
switch
{
.case 2 .
case
as
- -- *
7F
Chapter 5 Selection — Making Decisions 257
shortly, but let ’s look at an example first . Program 5-6 demonstrates the
switch statement . Can you figure out what it prints?
Program 5 -6 has three different case labels. The first case identifies the
entry point to he used when printFlag is a 1 . The second case identifies the
entry point when printFlag is a 2. And finally; the default identifies the entry
point when printFlag is neither a 1 nor a 2. While default is not a required
condition in a switch statement , it should be included when all possible situa -
tions have not been covered by the case labels.
Have you figured out what is printed by Program 5 - 6 ? The answers are in
Figure 5 - 22 . Three results are possible , depending on the value in printFlag.
If printFlag is a 1 , then all three print statements are executed . If printFlag
is a 2 , then the first print statement is skipped and the last two are executed
.
if is neither a 1 nor a 2 , then only the statements defined
Finally , printFl ag
by the default are executed . This results in the first two print statements
being skipped and only the last one being executed .
pTtisis case 1
This is case 2
l^ This is case 2
This is default
l^This is default
This is default
? To
But what if we want to execute only one of the case - label sequences
. The break statement causes the pro-
do so, we must use break statements
out of the sn itch statement — that is , to go to the closing brace
gram to jump
and continue with the code that follows the switch. If we add
a break as the
5 - 23. Now only one print state-
last statement in each case , we have Figure
,
switch ( printFlag)
{
case 1:
printf
printFlag ( " This is case 1 " );
break ;
case 2:
printf
("This is case 2");
1 2 default break ;
This is This is This is default:
default printf
case 1 case 2 ( "This is default " );
break ;
} // switch
~
pThis is case 1 l^
This is case 2 PrhiTis default
Two or more case labels can be associated with the same set of actions.
In Program 5 - 7, for example , we print a message depending on whether
printFlag is even or odd .
t
! |[
As a matter of style, the last statement in the switch does not require a
break . We recommend , however, that you get in the habit of using it , especially
when the last statement is not the default . This good habit will eventually
save you hours of debugging time because you will not forget to add it when
you add a new case to the switch statement.
Table 5-5 summarizes some points you must remember about the switch
statement .
1 . The control expression that follows the keyword switch must be an inte-
gral type.
2 Each case label is the keyword case followed by a constant expression .
.
3. No two case labels can have the same constant expression value.
4. But two case labels can be associated with the same set ol actions.
5 The default label is not required. If the value of the expression does not
.
match with any labeled constant expression , the control transfers out -
side of the switch statement . However, we recommend that all switch
statements have a default label .
6 . The switch statement can include at most one default label. The default
label may be coded anywhere , but it is traditionally coded last .
switch Example
Program 5 -8 converts a numeric score to a letter grade. The grading scale is
the rather typical ‘‘absolute scale, ” in which 90 % or more is an A, 80 %—89 % is
a B , 70%-79 % is a C , and 60%— 69% is a I ). Anything below 60% is an F
.
13 // Local Declarations
14 int score;
15 char grade;
16
17 // Statements
18 -
printf(" Enter the test score ( 0 100 )
: " );
\U
n IT
Chapter 5 Selection — Making Decisions 261
Program 5 - 8 Analysis This example shows how we can use the integer division operator ( / ) to change a
of
range numbers to individual points to be used by the switch statement . The problem
definition requires that if the score is between 80% and 89%, it must be changed to
letter grade ' B ' . This condition cannot be used in a switch statement. But if we divide
the score by 10 ( integer division), the entire range ( such as 80-89 ) can be changed
to one single number (8), which can be used as a constant in the case label.
Note how the break statement works. This is an important part of the logic for
switch statements. Without the break , we would have determined and assigned the
score, and then proceeded to assign all of the lower scores down to ' F ' , with the result
that everyone would have failed. The break allows us to leave the body of the switch as
soon as we have completed the grade assignment.
One word of caution. If the user enters an invalid score, such as 110, this program
gives invalid results. We describe how to prevent this problem in Chapter 6.
score
=
> 90
grade = 'B
1
score
C
=
> 60
j
v
Y ?
FIGURE 5 - 24 The else-if Logic Design for Program 5 -9
262 Section 5.3 Multiway Selection
..
if code . As we said , it is really nothin g
-
What is different about the else indenting each / statement , we code
than
more than a style change. Rather
align it with the previous / I n tins way we sim-
-
the else if on a single line and
we see in the switch and its associated case
ulate the same formatting that
.
label. This style format is shown below
if ( score >= 90 )
grade = 'A';
else if (score >= 80)
grade = 'B' ;
One important point about the else if: Use - it only when the same basic
"> - 24 , the expressions < ue all based on
expression is being evaluated . In Figure
the variable score. If different variables were being evaluated
, we would use
-
The else if is an artificial C construct that is only used when
.
1 The selection variable is not an integral, and
2. The same variable is being tested in the expressions .
Results:
Enter the test score (0 100): 90 -
The grade is: A
Program 5 - 9 Analysis We used the else- if construct because our condition was not an integral; rather, it
tested several ranges of the same variable, score. Study the code carefully. Note
that, once the correct range is located, none of the following conditions will be tested.
For instance, if a score of 85 is entered, the test against 90% is false, so
we execute
than 80% Since this condition is true, we set grade
the else- if test for a score greater .
to ' B ' and skip all the remaining tests .
Also, note how the tests are ordered. In this case we first eliminate those scores
equal to or greater than 90%; then we check 80%, 70%, and 60% in turn. Sincefirst we
were checking for greater than, we could not have coded it in the reverse, 60% .
264 Section 5.4 More Standard Functions
eS'L
^ wil the lowest Calue
®
JSh LTaigeClueSn chSg than start
characters
%
control printable
space graphical
alphanumeric punctuation
alphabetic digit |
| upper lower
Characters are first broken down into control characters , such as car-
riage return and end of file , or into printable characters. This tells us that
control characters are not printable. The printable characters are either a
space or the rest of the printable characters, which are classified as graphical .
In turn , the graphical characters are broken down into alphanumeric and
punctuation characters. Alphanumeric means either an alphabetic character
or a digit. Finally, alphabetic characters are either upper- or lowercase.
Classifying Functions
Classifying functions examine a character and tell if it belongs to a given
classification as described previously. They all start with the prefix is and return
true if the actual parameter is in the specified class and false if it is not . The
prototypes of these functions are found in the ctype.h and cwtype .h files.
The general form of the prototype function is
where the function name starts with is or isw ; 2 for example , iscntrl ,
w hich stands for " is a control character.
"
returns false . For example , the isdigit function tests the character against the
decimal digits ( 0 through 9 ). If the character is a decimal digit , it returns
.
true ; if the character is not a decimal digit , it returns false fable 5 -6 summa -
rizes each function with a brief explanation .
Function Description
-
2 . For wide character functions.
266 Section 5.4 More Standard Functions
Description
Function
isupper Only uppercase alphabetic
islower Only lowercase alphabetic
isdigit Decimal digits (0...9)
Hexadecimal digits (0...9, A...F )
isxdigit
isodigit Octal digits (0...7)
Function Description
toupper Converts lower- to uppercase. If not lowercase, returns it
unchanged.
tolower Converts upper- to lowercase. If not uppercase, returns it
unchanged.
A Classification Program
Let s write a program that uses classification functions to examine a character
input from the keyboard. If you study Figure 5- 25, carefully, you will note that
most characters fall into more than one
classification . For instance, a digit is
printable, graphical, alphanumeric, and a digit.
Our program tests from the bottom up, so that
only one classification will
be printed lor each character. For a digit ,
only the fact that it is a digit will he
prime . ecausi it is a relatively
simple demonstration of the classification
functions, we wrote it using only main . The
solution is shown in Program 5 10. -
Chapter 5 Selection — Making Decisions 267
Results:
Enter a character to be examined: a
You entered a lowercase letter.
so at first analy -
Program 5 - 10 Analysis Note the use of the else - if in this program . While it may not appear for multiway
sis, this is actually a range analysis problem, which makes it suitable
selection.
268 Section 5.5 Incremental Development PartJl
exit
Whereas return terminates a function , exit terminates the program regardless
of where in the program it is executed . While we use it to terminate the pro-
gram because we detected an error, C considers it a normal termination. For
this reason , the termination is orderly; any pending Hie stream writes are first
completed and all streams are closed . The exit prototype statement is
shown below.
abort
The abort Junction is used to terminate a program abnormally. It is consid -
* ered a non -orderly termination because the output streams are not Hushed
and they are not closed . It is like drawing the "Go to Jail" card in Monopoly
you go directly to jail , you do not pass GO, and you do not collect $ 200.
—
\\ hen abort is called , the program immediately goes to the operating system .
I he abort function has no parameters. Its prototype statement is
shown below.
Calculator Design
Although it is an elementary example, our calculator program illustrates two
concepts , hirst , it shows how we can communicate with a user through a
menu . Menu ’s are a common design for programs that interact with users.
Second , it demonstrates incremental development through three levels in a i
-
program . Figure 5 26 shows the design of the program .
calculator
getOption
I getData calc
I printResult
In this program , we are doing four different things: ( 1 ) We must ask the user
what function is desired . Then we need to ( 2 ) get the data tor the operation ,
( 3 ) make the calculation , and finally ( 4 ) print the result . These four processes
are seen as called functions in main . In its turn , calc calls one ot lour I unc -
tions to perform an arithmetic operation . Finally, the print result function
displays the result .
choice " );
47 printf( " and key return: " );
48 scanf ( "%d " , option);
49 ^
piintf( " * * You selected option
%d \n" , option );
50 return option ;
51 > // getOption
Results:
************ *******
111
***************
A continuei
Chapter 5 Selection — Making Decisions 271
* 3. MULTIPLY
* 4. DIVIDE *
* *
********************************* *
*
Program 5 - 1 1 Analysis We use the same approach that we used previously. In main we call the get option
function. Just before returning, we print the option and then we re-print it in main so
that we can easily verify the results.
We incorporate one new style. Because it can be difficult to find the debugging
we leave
statements in a large function, we do not indent them in the function. Rather
them flush to the left margin so that they are easy to find and remove and we continue
the incremental development.
select the correct subfunctio n In.place of the actual calls , we return dummy
divide. You
values: 1.0 for add, 2.0 for subtract, 3.0 for multiply, and 4.0 for
, use
should recognize these as the option numbers. When testing a program
be easily verified . I he code is
test data that is easy to remember and that can
shown in Program S - 12.
( continued )
PROGRAM 5 - 12 Menu -driven Calculator -Third
Increment
6 # include <stdio.h>
7 # include <stdlib.h>
8
9 // Function Declarations
10 int getOption (void );
11 void getData ( int* numl ,
int* num2 );
12 float calc (int option, int numl , int num2);
13
14 int main ( void )
15 {
16 // Local Declarations
17 int option ;
18 int numl;
19 int num2 ;
20 float result;
21
22 // Statements
23 option = getOption();
24 getData (&numl , &num2 );
25 result = calc (option, numl , num2 );
26 printf( "**In main result is: %6.2f " , result );
27
28 return 0;
29 > // main
30
31 /* === getOption ===
32 This function shows a menu and reads the user option.
33 Pre Nothing
34 Post returns the option
35 */
36 int getOption ( void )
37 {
38 / / Local Declarations
39 int option ;
40
41 // Statements
42 printf ( "\t************* *** **
******** ** *** * ** ).
i
43 printf("\n \t*
MENU *»
44 printf( "\n\t*
45 *» )
printf("\n\t* 1. ADD
"
46 printf( "\n\t* 2.
SUBTRACT * ")
47 printf( M \n\t* 3.
48
MULTIPLY * •• )
printf( \n\t* 4.
M
49
DIVIDE * <• )
printf("\n\t*
*» )
i ;
continue
\i
Chapter 5 Selection — Making Decisions 273
Results:
**************************** ******
* MENU *
* *
* 1. ADD *
* 2. SUBTRACT *
* 3. MULTIPLY *
* 4. DIVIDE *
* *
**********************************
i
Chapter 5 Selection — Making Decisions 275
(continued )
PROGRAM 5- 13 Menu -driven Calculator -Fifth
Increment
24 // Statements
25 option = getOption();
26 getData (&numl, &num2);
, num 2 );
27 result = calc (option, numl
28
29
// Temporary code to be —
removed
printf("**In main result is:
%6.2f \n " , result );
30
31 return 0;
32 } // main
33
34 /* ===== ======== getOption ===== =======
35 This function shows a menu and reads the user option.
36 Pre Nothing
37 Post returns the option
38 */
39 int getOption (void)
40 {
41 // Local Declarations
42 int option;
43
44 // Statements
45 P3 "1ritf ( "\£******* ** A:* * ***** * **** *
• •
*** * * * ,, J
' ' '
^
46 printf( n t*
" \ \ MENU * •• )
47 printf( "\n\t* *"
48 printf( "\n\t* 1. ADD *» )
49 printf( "\n\t* 2. SUBTRACT »
continued
Chapter 5 Selection — Making Decisions 277
Increment (continued )
PROGRAM 5- 13 Menu-driven Calculator-Fifth
112
113 /* === and returns the sum.
114 This function adds two numbers
b contain values to be added
115 Pre a and
Post Returns a + b
116
117 */
118 float add ( int a , int b)
119 {
120 // Local Definitions
121 float sum;
122
123 // Statements
124 sum = a + b;
125 return sum ;
126 > // add
127
128 /* === ==
129 This function subtracts two numbers
130 Pre a and b contain values to be subtracted
131 Post Returns a + b
132 */
133 float sub ( int a , int b)
134 {
135 // Local Definitions
136 float dif;
137
138 // Statements
139 dif = a b; -
140 printf("**ln sub result is: %6.2f \n" , dif );
141 return dif ;
142 } // sub
Results:
*************************
*
^^^
MENU *
*
*
* 1. ADD *
* 2. SUBTRACT
*
* 3. MULTIPLY
*
* 4. DIVIDE
*
*
*
**************************
** ******
Please type your choice and
key return: 2
i
continue*
Chapter 5 Selection — Making Decisions 2 / 9
n
-
ft I
n
PROGRAM 5 - 13 Menu -driven Calculator — Fifth Increment ( continued )
Please enter two integer numbers: 13 8
**In sub result is: 5.00
**In calc result is: 5.00
**In main result is: 5.00 i
Program 5 - 1 3 Analysis Note how we display the results up the program to main. This verifies that the upward
communication is correct. If there should be a problem, we will know exactly where it
occurred.
Remaining Increments
At this point, we have demonstrated the incremental development concepts for
programs using multiple levels of subprograms. To complete the program, we
must write and debug the final three increments:
• Increment 6: multiply
• Increment 7: divide
• Increment 8 : print results
We leave the completion of the program for you as a problem at the end
of the chapter.
280 Section 5.6 Software Engineering
Assume that in this example, a has a value of 10 and b has a value of 20.
W hat value will he returned: First , look at Statement 6. The
assignment of x
in this example is dependent on the i in Statement
/
not indented , however, it is difficult to see the
5. Since Statement 6 is
dependency. Because the value
o a ( 10 ) is less than the value of b ( 20 ) , x
will he assigned the value 10.
Now examine Statement 9. It is
indented and therefore appears to be
epen ent on t e else statement . But is it ?
The answer is no. It just looks that
way and is therefore misleading.
-
Statement 9 will therefore execute regard -
expression in the if statement .
This relationship is much more
2l See ln
"
/
0 f
' tyW code on the right . The code on the right is
,W the
, rclationshiPs
foree the chance of misreading among the statements , and there-
the code is minimal .
L, ,
Chapter 5 Selection — Making Decisions 281
Negative Logic
Negative logic refers to any expression that begins with not or that contains
multiple not expressions within . Like the proverbial double- negative in the
English language , interpreting negative logic can be difficult . We therefore
try to avoid it .
In the discussion of Figure 5- 12 and in the section on simplifying the if
statement , one technique that we proposed was complementing a condi -
tional statement. This requires making a positive statement negative and a
5- 2,
negative statement positive. This can be done by following Rule 6 in Fable
” which states that the positions of
“ Syntactical Rules for if ...else Statements ,
if (x != 5) if (x == 5)
.
1 Code positive statements whenever possible.
Human engineering studies have shown that people make fewer errors
en rca * n 8 positive statements than
^ us is
i
when reading negative statements.
especially true when complex, compound Boolean statements are
—
involved. Therefore, the first rule in Table 5 10 is, whenever possible, code
your selection statements using positive
-
conditions.
low P, ! 7Td TCemS
FUle
‘he human expectations about what will fol -
confused if H
f ' to an cipate things. They will therefore be less
'
*
Chapter 5 Selection — Making Decisions 283
Finally, the third rule concerns the efficiency of the resulting program.
Coding the most probable conditions first is especially important in a multi-
way selection, such as the else - if . When you code the most probable test first,
then the program can skip the rest of the statements. Obviously, the more
statements skipped, the more efficient the resulting program.
As we mentioned previously, these rules often conflict with each other.
!
dolt { select :
: if (...)
5 if (...)
fun (...);
I
doA
(+)
doB
else
doA (~);
fun
I :
} // dolt
doB (...);
// select
}
Design Code
Design Code
(a ) conditional ( b) exclusive or
a sub-
In Figure 5 - 27 ( a ) , the function dolt contains a conditional call to
function, fun. If the condition in the // statement is true , we call dolt . II it is
. represent ed in a structure chart as a
not true, we skip dolt This situation is
small diamond on the vertical line between the two function blocks .
Figure 3 - 27 ( b ) represents the selection between two different functions
.
between and doB One and
In this example, the function select chooses doA .
is exe-
only one of them will be called each time the conditional statement
the alternative s is exe-
cuted. This is known as an exclusive or ; one of two
switch (color)
selectColoJ
{
case R': colorRed (...);
break ;
(+j U+ ) case ' B ':
colorBlue (...);
break ;
colorRed colorBlue I otherColor default : otherColor (...);
} // switch
if ( a++)
4. Encapsulate the statements inside braces if you have more than one
..
statement after ij or else in an if else statement .
3. Do not use the equal operator with a floating- point number, f loating-
point numbers are seldom exactly equal to a required value. When you
need to test for equality, such as a = = b , use the expression shown below.
.
6 Do not forget to use a break statement when the cases in a switch state -
ment are exclusive.
7. While not necessarily an error, it is poor programming practice to write a
switch statement without a clejault label . If the logic doesn ’t require one ,
code it with an error message to guard against unanticipated conditions.
This is shown below.
// if terminated here
if (a == b);
printf(...);
else // No matching if
printf(...);
286 Section 5.9 Summary
( a ++ & & — b)
5.9 Summary
Data are called logical if they convey the idea of true or false.
C 99 implements the logical type, bool . It also
supports integer logical
d t ata item is
nonzero, it is considered true ; if it is zero, it is
considered false .
three operators lor combining logical values to create new' values:
wot, and , or.
Six comparative operators are
used in C: <, <= > , > =, = = , and !=.
Selection in C is done usi
using two statements: if . .else and switch.
Chapter 5 Selection — Making Decisions 28 /
It
Chapter 5 Selection — Making Decisions 289
Exercises
1 5. Evaluate the following expressions to true or false . Show how you
arrived
at your answer by first adding the default parentheses and then showing
-
the value of each expression as it would be evaluated by C , one expres
sion to a line.
.
a ! ( 3 + 3 >= 6 )
.
b 1 + 6 = = 7 || 3 + 2 = = 1
.
c 1 > 5 | | 6 < 50 & & 2 < 5
d . 1 4 ! = 5 5 & & I ( 1 3 < 2 9 ) || 3 1 > 5 2
e. 6 < 7 > 5
290 Section 5.10 Practice Sets
b. x | | y & & z
c. ( x & & y ) || z
d. ( x || y ) & & z
e. ( x & & z ) || y
18. Simplify the following expressions by removing the !
parentheses: operator and the
a. ! ( x < y )
b. ! ( x > = y )
c . ( x == y )
* d . ( x != y )
e. ! ( l ( x > y ) )
19. If x = -2, y = 5, z 0, and t -4,
ing expressions?
= =
- what is the value of each of the follow-
.
a x + y < z + t
. -
b x 2 * y + y < z * 2 / 3
.
c 3 * y / 4 % 5 && y
d. t | | z < ( y + 5 ) && y
e. ! ( 4 + 5 * y >= z 4 ) && ( Z _2 )
20. 11 originally x 4,
= y = 0, and z - what is the value of x, y, and z after
»
executing the following code ?
if (x 1 = 0)
y = 3;
continued
r
21 . II originally x = 4, y 0, and z
= = 2, what is the value of x, y, and z alter
executing the following code? »
if ( z == 2)
y = 1;
else
x = 3;
.
22 If originally x = 4 , y = 0, and z = 2, what is the value of x, y, and z after
executing the following code?
if ( x && y )
x = 3;
else
y = 2;
|y
if ( x | z)
y = l;
else
z = 3;
.
24 If originally x = 0 , y = 0, and z = 1, what is the value of x, y, and z after
executing the following code?
if ( x )
if ( y )
z = 3;
else
z = 2;
if (z == 0| |x && !y )
if ( iz)
y = 1?
else
x = 2;
292 Section 5.10 Practice Sets
if ( x )
if ( y )
if (z)
z = 3;
else
z = 2;
.
27 If originally x = 0, y = 0, and z = 1, what is the value of x, y, and z after
executing the following coder
if (z = y )
{
y ++;
>
—
z ;
else
— x;
if ( z = x < y)
—
{
x += 3;
y 1?
>
else
x = y++;
3 . If originally x
° executing
= 0, y = 0, and z = 1 , what is the value of x, y, and z after
the following code?
switch (x)
continued
Chapter 5 Selection — Making Decisions 293
case 0 : x = 2
y = 3
case 1 : x 4
default : y = 3
x = 1 n
>
. =
31 II originally x = 2 , y 1 , and z = 1 , what is the value of x, y, and z after
executing the following code ?
switch ( x )
{
case 0 : x = 2;
Y = 3;
case 1 : x = 4;
break ;
default:
y = 3;
x = 1;
>
.
32 If originally x = 1 , y = 3, and z = 0, what is the value of x, y, and z after
executing the following coder
switch ( x )
{
case 0 : x = 2
y = 3
break
case 1 : x 4
break
default: y = 3
x = 1
}
Programs
assign the value 1 to the variable best if
35. Write an if statement that will
i
if (aChar == • E * )
C ++ ;
if (aChar == 'E' )
printf ( "Value is E\n" );
43. Rewrite the following code fragment using one switch statement:
if (ch == 'E * |
|ch == e ' )
countE++ ;
else if (ch = = 'A'|
|ch == 'a 1 )
< 4? countA++
else if (ch == '1' |
|Ch == • i’ )
countI++;
else
print ("Error Not A , E ,
— o r I \a \ n" ) ;
.
44 Write a code fragment that tests the value of an
integer numl . If the value
is 10 , square numl . If it is 9 ,
read a new' value into numl . If it is 2 or 3,
multiply numl by 99 and print out the result. Implement your code using
nested if statements , not a switch .
45. Rewrite Problem 44 using a switch
statement .
46
' fhTtU °de fragment for the flowchart shown
C
that the variables x and y are integers and z is a
in Figure 5- 29. Assume
-
float point number.
i.
Chapter 5 Selection — Making Decisions 295
yread x & yj
x>0
y = x-1
z=2 * x
l z=y z=x
y=x+1
l
z=z+1
9l +
/ /
write
/ ** /
'
2
.
47 Write a function called smallest that, given three integers, returns the
smallest value.
_
48. Write a function called day_of week that , given an integer between 0
and 6, prints the corresponding day of the week. Assume that the first day
of the week (0 ) is Sunday.
49. Write a function called month _of _ year that , given an integer between 1
and 12, prints the corresponding month of the year.
50. Write a function called parkingCharge that , given the type of vehicle
( c for car, b for bus, t for truck ) and the hours a vehicle spent in the
parking lot , returns the parking charge based on the rates shown below.
average of the second and third scores. If the average of the two is
greater than 70%, the grade is C; otherwise, it is D .
d . If the average score is less than 50 percent , then the grade is F.
The program’s main is to contain only call statements. At least three
subfunctions are required: one to read scores, one to determine the
grade, and one to print the results.
54. In Program 4 - 7 , "Strange College Fees, ” we wrote a program to calculate
college fees. Modify this program for Typical College. At Typical College,
the students pay a fee of $ 10 per unit for up to 12 units; once they have
paid for 12 units, they have no additional per- unit fee . The registration
fee remains $ 10 but is assessed only if courses are taken in the term .
55 . Given a point , a line from the point forms an angle with the horizontal axis
to the right of the line. The line is said to terminate in one of four quad -
rants based on its angle ( a ) from the horizontal , as shown in Figure 5- 30.
II I
Quadrants
I 0 < a < 90
90 < a < 180
III: 180 < a < 270
III IV IV: 270 < a < 360
0°, 48.3°, 90°, 179.8°, 180°, 186°, 270°, 300°, and 360°
36. I low many values of the variable num must be used to completely test all
branches of the following code fragment ?
if (num > 0)
if ( value < 25 )
{
value = 10 * num ;
if ( num < 12 )
value = value / 10;
} // if value
else
value = 20 * num ;
else
value = 30 * num;
57. Write a program that asks the user to enter the current date and a per -
son’s birth date in the form month , day, year. The program then calcu -
lates the person ’s age in integral years. Use separate lunctions to enter
the dates ( pass by address) , calculate the person s age, and print the
’
results. lest your program with the following dates: 1 1 / 14 / 1957 , 5 / 10/
1989, and 1/5/2000.
58. Write a C program to calculate the parking fare for customers who park
their cars in a parking lot when the following information is given :
a. A character showing the type of vehicle: C for car, B for bus ,
T for truck .
b. An integer between 0 and 24 showing the hour the vehicle entered
the lot .
c. An integer between 0 and 60 showing the minute the vehicle entered
the lot .
d. An integer between 0 and 24 showing the hour the vehicle leit the lot .
e. An integer between 0 and 60 showing the minute the vehicle
leit
the lot.
This is a public lot . To encourage people to park for a short period ot
time, the management uses two different rates for each type
of vehicle,
as shown in fable 5 11- .
298 Section 5.10 Practice Sets
Type of vehicle? C
Hour vehicle entered lot (0
Minute vehicle entered lot (0
-- 2460))?? 2314
Hour vehicle left lot
Minute vehicle left lot
(0
(0
-- 6024 )?)? 818
The output format is shown below.
Pro8ram , ,st first calculate the actual time spent in the park-
in I /
°. ,
f re
TU
° C
Jc e* ^*caliscumeans using modulo arithmetic to handle
ti
tZ
^
shownV I
* "
\
'
Y ,* * ^
11 ate is in many ways, one of which is
“ h e P H i n, l o, u* h e - y. .
.
a Compare the minute portion of the leaving and
the entering time.
If the first one is smaller than the
second,
Add 60 to the miminute portion of the leaving time.
Subtract 1 from the hour portion of the
leaving time.
b. Subtract the hour portions.
Chapter 5 Selection — Making Decisions 299
1 C 12 40 14 22
2 B 8 20 8 40
3 T 2 0 3 59
4 C 12 40 16 22
5 B 8 20 14 20
6 T 2 0 12 0
: 20
Your guess is low. Try again
high. Try again: 10
Your guess is
Your guess is low. Try again
: 18
Your guess is high. Try again
: 12
The formula determines the day based on the values as shown below.
Day 0: Sunday
Day 1: Monday
Day 2: Tuesday
Day 3: Wednesday
Day 4: Thursday
Day 5: Friday
Day 6: Saturday
Once you know the day for December 31 , you simply calculate the
days in the year before the month in question. Use a switch statement to
make this calculation. ( Hint: Use case 12 first , and then fall into case 11,
10 . . . 2 . ) If the desired month is 12 , add the number of
days for November
( 30 ) . If it is 11, add the
number of days for October ( 31 ) . If it is 3, add
the number of days for February ( 28).'lf it is 2, add the number of days
or January ( 31 ) . If you do not
use a break between the months, the
switch will add the days in each month
before the current month .
o t is gure, add the day in the
current month and then add the
resu t to t e day code for
December 31 . I his number modulo seven is
the day of the week.
There is one more refinement. If the
current year is a leap year, and
Th
tSlrt
I he ffollowing
i| r
3te S a ter February
' *
, -
you need to add 1 to the day code.
formula can be used to dete rmine if the year is a leap year.
( l (year % 4) && (year
1 100 ) )
» II 1 ( year % 400 )
iitl
x
Chapter 5 Selection — Making Decisions 301
Your program should have a function to get data from the user,
1
another to calculate the day of the week , and a third to print the result.
To test your program , run it with the following dates:
a . February 28, 1900, and March 1 , 1900
.
h February 28, 1955 , and March 1 , 1955 iw
63. Write a program that tests a user entered character and displays its clas -
-
sification according to the ASCII classifications shown in Figure 5 - 25 ,
“ Classifications of the Character Type.” Write the program starting at the
top of the classification tree and display all classifications that the char-
acter belongs to. For instance, if the user enters a digit , you should
dis-
play that it is printable, graphical , alphanumeric , and a digit .
(
64. Write a program to compute the real roots of a quadratic equation ax
+
ip
xl
-b+ Jb 2
- 4 ac and \2 =-
b - Jb 2
- 4 ac
2a 2a
a b
3 8 5
-6 7 8
0 9 -10
0 0 11
4
Repetition
The real power of computers is in their ability to repeat an operation or a
series of operations many times. This repetition , called looping, is one of the
basic structured programming concepts. In this chapter, we discuss looping
and introduce different looping constructs. First we define the basic concepts
of loops, including a most important concept: how to stop a loop. We then
present the three different loop constructs and take you through the C imple -
mentation of these three constructs. In this discussion , we include some
basic loop applications. After discussing the basic loop constructs, we intro -
duce an implicit loop construct , recursion . We conclude with a discussion of
some of the software engineering implications of loops.
Objectives
To understand basic loop concepts:
pretest loops and post- test loops
loop initialization and updating
event and counter -controlled loops
To understand and be able to select the best loop construct for a given
problem.
To write programs that use the while, for , or do. . . while statements.
To understand the basic concepts and usage of recursion algorithm
s.
To understand and be able to determine the efficiency of an algorithm
through an analysis of its looping constructs.
303
304 Section 6.2 Pretest and Post-test Loops
An action or a
series of actions
Pretest Loop
In each iteration, the control
.
expression is tested first If it is true, the loop
continues; otherwise, the loop is
terminated .
Post-test Loop
In each iteration, the loop action
(s) are executed. Then the control expression is
tested. If t is true, a new iteration
is started; otherwise, the loop terminates .
In the post -test loop , we always
execute the action at least once. The loop
» 1 control expression is then tested If tU .
the expression
• . . f
is true , the loop repeats; if
Chapter 6 Repetition 305
the expression is false, the loop terminates. The flowcharts in Figure 6- 2 show
these two loop types.
V An action or
series of actions
true
An action or
series of actions
—(
true \
Condition \M
\ \ false
( a) Pretest Loop ( b) Post - test Loop
I
Energy
? Do one
push up -
true
I
Do one / \
push-up
=\ Energy
|false
/
(a) Pretest Loop (b) Post- test Loop
As you can see, in the first strategy, you may not do any push - ups
. If you
for at least one push - up ,
are tired when you start and don ' t have the energy
you are done. In the second strategy , you must do at least one push - up. In
loop , the action may he done zero , one , or more
other words , in a pretest
306 Section 6.3 Initialization and Updating
[V
Body 1
exit
false
In a post -test loop, the body
must be executed at least once.
exit
Loop Initialization
Belore a loop can start , some preparation is usually required . We call this
preparation loop initialization . Initialization must be done before the first
execution of the loop body. It sets the stage lor the loop actions. Figure 6- 5
shows the initialization as a process box before the loop.
Initialization may be explicit or implicit . Explicit initialization is much
more common. When the initialization is explicit , we include code to set the
beginning values ol key loop variables. Implicit initialization provides no
direct initialization code; rather, it relies on a preexisting situation to control
the loop.
Loop Update
How can the condition that controls the
loop be true for a while and then
change to falser I he answer is that
something must happen inside the body
u oo|) to t tange the condition .
oop. For example , in the loops shown
Otherwise , we would have an infinite
'
izg2l “ 'Tr Jrfr
chanaes th
;“ j “T “ “ “
^
' ar l
' " " P
°
in Figure 6- 3, you gradually lose vour
y U Cannot continue the push - ups. This
update . Updating
The
J „.
each
h» cu*
deaden ,
, .
£;£iar
u | is nt in
t ^^ . «* *
ail
Chapter 6 Repetition 307
7H!
Initialization
I
tv Initialization
1
I
*
\ false
/
( Test Y- Action(s)
i
I true
Action(s)
Updating
I
Updating
s—(\ _7\ Test
false
exit exit
sumed in the process and your energy is reduced, which updates your energy
level. This is shown in Figure 6 - 6.
I tv
1
Create k
Create
Energy Energy I
*
( y r
Do One
Push - up
Energy
I
r
[true Reduced
Do One
Push - up I
- gy
1
Energy Ener
Reduced true OK ?
false
exit
exit
( a) Pretest Loop (b) Post -test Loop
Initialize Initialize
condition condition
false
Condition
true
Action( s )
Update
I
Action(s)
I condition I
Update
condition Condition
true
false
!! I
exit exit
(a) Pretest Loop (b) Post- test Loop
we use a
UP , or ,, WhiCh
controlled loop is shown in Figure 6-8.
““” “
counting down . The counter-
L
"
I
Set count
I
Set count K
to 0 toO
false
exit exit
(a) Pretest Loop ( b) Post- test Loop
Loop Comparison
The number of iterations of a loop is given as w. In a pretest loop, when we
come out of the loop, the limit test has been executed n + 1 times. In a
post - test loop, when we come out of the loop, the limit test has been executed
only n times. A summary of the two different loop concepts is shown in Table 6- 1 .
Initialization: 1 Initialization: 1
6.5 Loops in C
C has three loop statements: the while, the for , and the do. . . while
. I he first two
are pretest loops, and the do. . . while is a post - test loop . We can use all ol them
for event - controlled and counter -controlled loops . I he while and do . .. while are
most commonly used for event-controlled loops and
, the for is usually used for
counter-controlled loops. These loop constructs
are shown in Figure 6 -9 .
310 Section 6.5 Loops in C
for do . ..while
Note that all three of these loop constructs continue when the limit con -
trol test is true anti terminate when it is false. This consistency of design
makes it easy to write the limit test in C. On the other hand , general algo-
rithms are usually written just the opposite because analysts tend to think
about what will terminate the loop rather than what will continue it . There-
fore, we must complement or otherwise modify the limit test when we write
our program . This is one place where " De Morgan ’s Rule , ” discussed in
Chapter 5 , is very handy.
i
I
.false
expression while ( expression)
true statement
statement
I
(a) Flowchart
(b) Sample Code
L
single
^
sinole statement; that is, the *
n Figure 6 - 1 shows that the loop body iis a
body of the loo p must be one , and only one
TH
Chapter 6 Repetition 311
.
statement II we want to include multiple statements in the body, we must put
them in a compound statement ( block) This concept is shown in Figure 6- 1 1.
.
1\
while ( expression )
{
Action
Action
Action
} // while
Process-Control Loops
Perhaps the simplest loop is the loop that never ends. While it is virtually
never used in data processing, it is common in process -control loops such
as
, |
^ ^effectl^ir
a |it cons
Program 6- 1 Analysis The limit test in this s mp ejpjro
m
' ^ statement strl, is always
L : 6
con
however, that this is not a good
^
^ ^
f
°r
while (0)
Print Loops
A common while loop is shown in Program 6- 2. In this case we want to print
a series of numbers in descending order. To keep from running off the end of
the line, we have added a test to write a newline when ten numbers have
been written.
Results:
Enter an integer between 1 and 100: 15
15 14 13 12 11 10 9 8 7 6
5 4 3 2 1
Program 6 - 2 Analysis Find the basic elements of a loop in this program. First, look for loop initialization,
then the limit test, and finally the update.
The initialization is done by asking the user to enter num in Statement 14 Note
how we make sure that the user enters a number that is not too large. If the number is
over 100, we simply set it to 100, which is the maximum we told the user to enter. The
limit test is in Statement 22. As long as num is greater than 0, we continue printing out
the number series. The update is hidden in Statement 31; after printing num, we sub-
tract 1 from it. Although this is a common C programmer trick, the program would
have been easier to read if we had put the update (num — ) on the line after the print.
That way it would have been obvious.
File Loops
One of the most common loop applications in any language is reading data
until all the data have been processed—that is , until the end ol file is
reached. In C, the scan/ function returns the system constant EOF when it
detects an end- of- file.
Consider this application of the scanf feature: suppose we want to read
and process a list of numbers from the keyboard. We type all the numbers,
each one on a separate line . At the end, we can type end- of- file ( <ctrl+d> in
UNIX or <ctrl+ z> in DOS ) . This loop logic is shown in the next example.
> // while
But we can simplify this loop even further. Because the scanf function
returns a value , we can test for end-of -file in the while expression . We don ' t
need the variable, ioResult, so we can simply use the function value and
discard it after it has been checked. The result is the standard C loop for
reading and processing data from a file shown in Program 6- 3, which adds a
list of integers read from the keyboard and displays their sum .
Results:
Enter your numbers: < EOF > to stop.
15
22
3 Ad
The total is: 40
Chapter 6 Repetition ^
315
Program 6 - 3 Analysis Note that a compound statement (block) is not needed in the while loop, because
the
addition can be done in one statement. Another important point is that the statement
to print the sum is outside the loop. Since the user can see the input values on the
screen, all we need to show is the sum.
\
expri
expr2 M
\ exprl
"
t expr3
f
( a ) Flowchart (b) Expanded Flowchart
Expression 1 is executed when the for starts. Expression 2 is the limit test
-
expression. As shown in the expanded flowchart , it is tested before every itera
tion . Remember that since the /or is a pretest loop, the body is not executed if
the limit condition is false at the start of the loop.
Finally, Expression 3 is the update expression . It is executed at the end of
each loop. Note that the code in the for statement must be expressions. This
means that you cannot use statements, such as return , in the for statement
.
itself. Like the while statement , the for statement does not need a semicolon
C allows the limit test in Expression 2 to he a variable . It also allows us to
change the value of the variable during the execution of the loop . We
do not
recommend changing it in the loop , however . It is not a good structured pro-
gramming style and can lead to errors and perpetual loops .
The body of the for loop must he one, and only one, statement
. II we
the body , we must code them in
want to include more than one statement in
316 Section 6.5 Loops in C
Action Action
Action Action
Action
} // for
exit
(a) Flowchart ( b ) C Language
.
representation
/ ation update code,
. Figure 6- 14 shows a graphical
ol each side by side. Note that the
/or loop contains the initial
and limit test in one statement . This makes for very
readable code . All the control steps,
*
U
Chapter 6 Repetition 317
initialize
for ( initialize ;
while ( expression ) expression ;
update)
1
{ {
action action
action action
update
// while i
L
> // for
Now let ' s solve the same problem using both a while and a for loop. I he
code shown below contains a loop to read 20 numbers f rom the keyboard and
find their sum. This can he done both in a while loop and a for loop. But as
you can see, the for loop is more self-documenting.
l = 1?
sum = 0; sum = 0;
while ( i <= 20 ) for ( i = 1 ; i <= 20; i++)
{ {
scanf( "%d " , &a); scanf ("%d" , &a);
sum += a ; sum += a ;
i ++; } // for
} // while
C99 modified the /or syntax to allow the definition of loop variables to be
done in the for statement itself. This syntax is shown in the next example.
Note, however, that definitions of the for statement are local to the loop body;
they are not in scope when the loop terminates. If we need them after the
loop, they must be defined in the local declarations section.
Results:
Please enter the limit: 3
1
2
3
Program 6 - 4 Analysis This simple program is the model for many looping functions. Let's look at three sim-
ple modifications to it. First, how would you print only odd numbers? This requires a
change only to the update in the for statement.
Now let's change it to print the numbers backward. In this case, all the statements
in the for statement must be changed, but the rest of the program is still unchanged.
for (i
For the
= limit; i > 1; i )
—
final example, let's print the numbers in two columns, the odd numbers in
the first column and the even numbers in the second column. We must modify the
update statement in the for statement and also the print statement as shown below.
-
for (i 1; i <= limit ; i += 2 )
printf("\t%2d\t%2d\n" , i, i +
i );
We have used tabs and width specifications to
a e secon print va ue (i + l) is an
align the output in columns. Note
expression . It does not change the value of i.
There is no side effect, it is just a value.
.
Any statement, even another or
ement . Ls ng nested loops
/ loop, can be included in the body of a for
can create some interesting applications. Let’s
I\
look at a very simple one here . We will give other examples in Section 6.8
"Looping Applications,” later in the chapter.
Program 6- 5 uses a nested loop to print a series of numbers on
multiple lines.
11
PROGRAM 6 - 5 A Simple Nested for Loop
1 /* Print numbers on a line.
2 Written by:
3 Date:
4 */
5 # include <stdio.h >
6
7 int main (void )
8 {
9 // Statements
10 for ( int i = 1; i <= 3 ; i++ )
11 {
12 printf( "Row %d: ” , i );
13 for (int j = 1; j<= 5; j++)
14 printf( "% 3d ” , j);
15 printf("\n");
16 > // for i
17 return 0;
18 } // main
Results:
Row 1: 1 2 3 4 5
Row 2: 1 2 3 4 5
Row 3: 1 2 3 4 5
also uses an expression to control the loop, hut it tests this expression after
the execution of the body. The format of the do. . . while statement is shown
in
Figure 6- 15.
The body of the do. .. while loop must he one, and only one, statement If
.
in the body , we must put them in a
we need to include multiple statements
com pound statement . The second example in Figure 6 - 15 shows the logic
statement .
flow and code for a do. ..while statement that uses a compound
Look carefully at the code block in the second example . Note that the do and
follows the
the while braces are aligned . Note also that the while expression
the
line . This is a good style because it makes it easy for
brace on the same
the statement . Finally, note that the do .. . while is concluded
reader to see
constructs that we
with a semicolon. This differs from the other looping
have seen.
!
do
statement
statement
expression
true
while (expression ) ;
[false
do
tv
Action {
Action
Action
Action
Action
T
J
Action
expression
true'
} while ( expression);
false
Lets look at a simple program that uses a loop to print five numbers We .
code two loops, first a while loop and then a do. . . while . Although one loop is
a pretest and the other is a post- test, both print the same series. The code is
seen in Program 6- 6.
continued
Chapter 6 Repetition 321
Results
while loop 5 4 3 2 1
do...while loop: 5 4 3 2 1
Program 6-6 Analysis Because the do. ..while limit test isn't done until the end of the loop, we use it when we
know that the body of the loop must be done at least once. To demonstrate the impact
of the two loops, study the code in Figure 6- 16. in the while loop, the message is not
printed, because the limit condition is tested first. In the do...while loop, even though
the limit test is false, the message is printed because the message is printed before the
limit test.
^ Pretest
nothing
^
prints
while ( false) do
{ {
printf( " Hello World " ); printf( " Hello World " );
^Post -tesP' N
Hello... prints,
do
{
printf ("Enter a number between 10 & 20:
" );
Results:
Run 1:
Enter your numbers: <EOF> to stop.
10 15 20 25 Ad
Total: 70
Run 2:
Enter your numbers: <EOF> to
stop.
Ad
Total: 0
rn
Chapter 6 Repetition 323
Program 6 - 7 Analysis Since the do. ..while always executes the body of the loop at -
least once, we had to
make some changes. Compare Program 6- 3 and Program 6 7 carefully. Note that
the scanf is no longer in the loop limit test. Since the limit test is after the loop body,
the scanf must be moved to the beginning of the loop. The result of the input is saved
in a new variable, testEOF. Finally, before we can add the value we read to the
accumulator, we must ensure we are not at the end of the file. The add statement is
therefore guarded by an if statement. Although Program 6-7 is a little less efficient
than Program 6 - 3, it does the same job.
Comma expressions can he nested. When they are nested, all expression
values other than the last are discarded. Figure 6- 17 shows the format of a
nested comma expression .
n is the
A final word of caution. Remember that the value of the expressio
nded, if use
value of the rightmost expression. Although recomme
it is not you
second expressio n in a for loop , make sure that
a comma expression for the
the loop control is the last expression.
Lets use the comma expression to demonstrate
the difference between
. , Loop Comparisons,"
the while and the do . . .while As shown in Table 6
- 1
324 Section 6.5 Loops in C
Results:
while loop: 12 3 4 5 6
Loop Count: 7 8 9 10
11
Number of tests: 11
do...while loop:
.
12 3 4 5 6 7 8 9 10
> continue
A
Chapter 6 Repetition 325
Program 6 - 8 Analysis Look at Statements 17 and 27 carefully. They both contain comma expressions. This
technique of combining the counter and the limit test in one expression ensures that
the count will be accurate. Because the value of the whole comma expression is the
value of its last expression, however, the limit test must be coded last.
The results demonstrate that both loops count from one to ten. Since they are doing
exactly the same job, we expect that the loop bodies would also execute the same num-
ber of times. As predicted in Table 6 - 1 the only difference is in the number of tests: the
while loop control expression was evaluated 11 times; the do...while control expres-
sion was evaluated only 10 times.
for Loops
Example: Compound Interest
One classic loop problem is calculating the value of an investment . We want
to know the value of an investment over time, given its initial value and
annual interest rate. Program 6 - 9 displays a compound interest table.
Results:
Enter value of investment: 10000
Enter rate of return (nn.n ): 7.2
Enter number of years: 5
Year Value
1 10720.00
2 11491.84
3 12319.25
4 13206.24
5 14157.09
One final point. When defining multiple variables iin one statement, they must all
have the same type. Notice that we defined the for loop variables in the definition
sections. To define multiple variables in a for statement, they must all be the same
type because they are treated as a type list.
I' d
Example: Right Triangle
Lets write a program that will print a scries of numbers in the form of a right
triangle. We ask the user to enter a one-digit number. Each line, from the
first to the limit entered by the user, then prints a number series from one to
the current line number. For example, if a user enters 6, the program prints
1
12
123
1234
12345
123456
The flowchart and pseudocode for the loop are shown in Figure 6 - 18.
/
1
line = l false \
\ 1 set line to 1 k
2 loop (line not greater than limit)
^
» ine++ line <=
\ limit I
| true
> 1 set num to 1
2 loop (num not greater than line)
1 print num
ium = false 2 increment num
num <= M 3 end loop
line 4 advance line
I true 5 increment line
j print r\un\
J 3 end loop
I
j print "\n“ jf
( a) Flowchart ( b ) Pseudocode
Results:
Please enter a number between 1 and 9: 6
1
12
I 123
1234
12345
123456
the outer loop, we know that the for is controlling the number of lines we are printing.
Likewise, the name numCtrl clearly tells the reader that the for loop is controlling the
numbers.
Results:
Please enter a number between 1 and 9: 6
!*** *
22****
123* * *
continuer
330 Section 6.6 Loop Examples
( continued )
PROGRAM 6 - 11 Print Number Series Using User - specified
Limit
1234**
12345*
123456
Program 6 - 1 1 Analysis This program is an interesting variation of the previous program. Compare its inner
loop to the inner loop in Program 6- 10. This is the only part of the program that is
different. The first thing you should note is that the limit test expression is different; it
always goes to the maximum number of print positions. Within the inner loop, we test
the column number (col) to determine how many digits we print on the line. If the
expression is true, we print a digit. If it is false, we print an asterisk .
i con l i MM
i!i
w
1
Chapter 6 Repetition 331
Results:
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29
-
Program 6 - 12 Analysis This program is interesting for two reasons: first, it requires two for loops, one to posi
tion the printing for the first day of the month and one to print the dates. Second , the
logic to control the days of the week is simple yet efficient. In an effort
to eliminate
would use the modulo statement
one variable (weekDay), many programmers
shown below to determine the day of the week .
(dayCount + startDay ) % 7
-j
332 Section 6.6 Loop Examples
Although this logic works, it is very inefficient. Using a separate variable to control
is much more efficient. It also
the days of the week requires simple addition, which
avoids another poor programming style: using one variable to control two processes.
Although using one variable to control two processes may work initially, it often leads
to subtle bugs when the program is changed.
One final point. Examine the initialization in the two for loops. Do you see a differ-
ence? Because weekday is used in both loops, it cannot be declared local to the first
loop. We must declare it in the definition section.
while LOOPS
This section contains two examples of while loops. The first prints the sum of
the digits in an integer entered from the keyboard. The second prints a num-
her backwards.
continued
Chapter 6 Repetition 333
Results:
Enter an integer: 12345
Your number is: 12345
of
Program 6 - 1 3 Analysis This problem requires that we "peel" off one digit at a time and add it to the total
the previous digits. We can solve this problem in a couple of ways, but by far the
most straightforward is to use the modulus operator ( % ) to extract the rightmost digit
and then to divide the number by ten to remove the right digit. For example, given the
number 123, we first extract the 3 by
123 % 10
and then eliminate it by dividing by 10 to give 1 2. Note that since both the dividend
and the divisor are integers, the result is an integer. We loop until there is only one digit
left, at which time any digit divided by ten will result in zero and the loop terminates.
Results:
Enter a number and I'll print it backward: 12345678
87654321
Have a good day.
do . . . while LOOPS
This sections contains an example of a program that uses a do . . . while
-
loop . While it is a simple problem , it demonstrates a classic data validation
technique.
getNum binaryTo
Decimal
Validate
Binary
firstDigit powerTwo
ii
HU *
Chapter 6 Repetition 335
1 his design follows a classic program design: input process output. The
— —
input function is getNum; the process function is binaryToDecimal; and the
output function is printResults. Study the design of binaryToDecimal.
Note that it uses a loop and calls firstDigit and powerTwo in turn within
the loop . How would you implement this loop? Think about it for a minute ,
VI
and then look at Statement 65 in Program 6- 15 to see how we did it . II you
are not sure of your binary arithmetic and binary c o n v e r s i o n s, study
Appendix I ), " Numbering Systems. "
continued
Chapter 6 Repetition 337
Results:
Enter a binary number (zeros and ones): 10001
The binary number was: 10001
The decimal number is: 17
338 Section 6.7 Other Statements Related to Looping
^Thei
allows us
^note how we enclosed the call to the validation
to keep reading input until the user gives us
function in a do. . .while that
a binary number. Again, note
how we display an error message when the number is not valid. This is a standard data
validation technique.
Next, study the binaryToDecimal function that converts the binary number to
its decimal value. Note that when we extract a digit, it is either a zero or a one. We
then multiply the extracted digit by 2 raised to the digit position we are currently evalu-
ating, which gives us the binary value of that digit's position in the binary number. The
value is then added to the decimal number. Of course, if the digit is a zero, then the
product is zero and the value is unchanged. It is only when the digit is a one that we
add to the decimal number.
Finally, note that throughout this program we used long long for the binary and
decimal number. We did this because the decimal representation of a binary number
can get very big very fast. On a personal computer, long might not be able to hold a
large binary number.
break
I he first statement is break . We previously saw the break statement when we
discussed snitch in Chapter S . In a loop, the break statement causes a loop
to terminate. It is the same as setting the loop ’s limit test
to false . If we are in
a series of nested loops , break terminates only the
inner loop —the one we are
currently in. Figure 6 - 20 shows how break transfers control out of an inner
for loop and continues with the next statement in the while . Note that break
statement needs a semicolon.
I he bieak statement can he used in any of the
loop statements—while ,
’ —
for and do. ..while and in the selection switch
structured programming limits its use to the switch
statement . However, good
statement. It should not
)e use in an \ of the looping statements. If
you feel you must use break , reex-
amine your resign. You will
usually find that you have not properly structured
your logic . 1 1 1 J
u
Chapter 6 Repetition 339
while (condition )
{
for ( •••i
The break statement takes\
us out of the inner loop
(the for loop ). The while I
{ loop is still active. 1 S' '
if (otherCondition)
—
break ; !
} // for
> 7/ while
3
4 if (condition ) if ( Icondition )
5 break ; ... ,
6 else } // while
7
8 } // while
On the other hand , too many flags can make a function overly complex.
The use of break and flags needs to be tempered with simplicity and clarity of
logic . Finally, note that the flag is called breakFlag , which is a generic name.
You should choose a more descriptive name, such as accountFlag or
timeLimitFlag .
continue
I he continue statement does not terminate the loop but simply transfers to
the testing expression in while and do. .. while statements and transfers to the
updating expression in a for statement . These jumps are shown in Figure 6-21 .
Although the transfer is to different positions in pretest and post - test loops,
both can be logically thought of as a jump to the end of the loop s body.
I unction , the assignment is to read data and return the average of nonzero
numbers read . In other words, it skips zeros. Note how simply reversing the
conditional test eliminates the need for the continue .
Summation
very easily. But how can
As you have seen , we can add two or three numbers
we add many numbers or a variable series of numbers ? The solution is sim -
add operator in a loop . The concept of summation is graphically
ple: use the
shown in Figure 6- 22 .
342 Section 6.8 Looping Applications
sum
^ conditiony^
exit
Summation Product
Powers
Just as we can add a series of numbers in a loop, we can perform any mathe-
matical operation . A product loop is useful for two common applications:
raising a number to a power and calculating the factorial of a number. I or
example, Program 6-20 shows a function to return xn . Notice that this func -
tion also includes initialization logic to validate the parameter list . If either of
the parameters is invalid , we return zero as an error indicator.
: initial -
The summation and powers examples demonstrate a subtle point
is not the same for every problem .
ization must he based on the application ; it
For summation , the initialization sets the accumulator to 0 . I or product -
to 1 . II we use 0.
based applications, such as powers, we must initialize it
then the result is 0 because 0 multiplied by anything is 0 .
344 Section 6.8 Looping Applications
result = a < b ? a : b;
But how can we find the smallest of several numbers? We simply put the
same statement inside a loop . Each iteration then tests the current smallest
to the next number. If this new number is smaller than the current smallest ,
we replace the smallest . In other words, we determine the smallest number
by looping through a series while remembering the smallest number the loop
has found . This concept is shown in Figure 6- 23.
smallest
INT MAX —
exit
smallest largest
*
entirety
entirely
^
on the data being read .
smallest value found .
-
CCOmcs smallest Thereafter, the result depends
The disposition simply returns the
Program 6- 21 is the C implementation
of smallest .
u
Chapter 6 Repetition 345
We can find the largest number by simply reversing the less than operator
in the expression, making it greater than. We would also need to set the vari -
able, renamed largest, to INT MIN. _
Inquiries
An inquiry is simply a question asked of the computer program. In program
-
ming, we often encounter one of two basic inquiry types any : and all. We use
the inquiry type any when we have a list of data and we want to know at
il
least one of them meet a given criteria . The answer to the inquiry is yes if one
or more data meet the criteria. The answer is no if none of the data meet the
criteria.
(I
-
result *- fe/se|
false
t
exit
any all
II either or both ol the equal expressions are true , result is set to true.
Ibis works fine lor testing the first two items. To test the third item in the
series , however, we need to include the result ol the first two. This is easily
done with the next example.
Study this test carefully. If the previous tests were all false , then result
contains Juke hen the statement is executed . If
" the equal expression is false ,
tien resu t is assigned false , however, -
if the equal expression is true , then
result is assigned true. By placing this expression
in a loop , we can test all ol
the tallies in the series. For example ,
to test a series to determine if any of the
values are positive , we can use the following
code.
result = false ;
while ( s c a n f ( " % d M ,
{ ^ number )
result = result number > 0 ;
> / / while
111
,
‘ TV
Chapter 6 Repetition 34 /
Program 6 - 22 Analysis Note that anyPositive is a logical var iable [ bool] and is initialized to falseto
.
do not need to read the entire number series , we can use a simple test
Because we
we return true . If not, we
set anyPositive. If a positive number was entered,
prompt for the next number and loop back to the while expression .
To use the bool type , we must include the stdbool . h library in the program. This
bool and it to the C Boolean
library uses a define macro to declare the type equate
type, £>oo/. It also defines
_
true and false.
Study this test carefully. If the previous tests were all true , then
result contains true when the statement is executed . If the equal expres-
sion is also true , then result is assigned true ; however, if the equal
expression is false , then result is assigned false . By placing this expres-
sion in a loop , we can test all of the values in the series . For example , to
test a series to determine if all of the values are positive , we can use the
following code.
result = true;
while (scant( " %d " , Snumber )
{
result = result && number > 0;
} / / while
Once again , for efficient processing we may not need to examine all of
the numbers in the series. As soon as a nonpositive number is found , we
know the result and can return false .
This function is seen in Program 6- 23.
'
i cont mucL
UL
T
"
Once again you should study how we initialize the Boolean ( lags in these
two programs . To test for any item , we start with the flag false If and when it .
becomes true , the result has been determined . Similarly, to test for all items,
we initialize the Boolean flag to true . If and when it becomes false , the result
has been determined .
6.9 Recursion
In general , programmers use two approaches to writing repetitive algorithms.
One approach uses loops ; the other uses recursion . Recursion is a repetitive
process in which a function calls itself . Some older languages do not support
recursion . One major language that does not is COBOL.
Iterative Definition
To study a simple example , consider the calculation of a factorial. The facto -
rial of a number is the product of the integral values from 1 to the number.
This definition is shown in Formula 6- 1 .
factorial ( /2 ) = [I * ( /2 - !) * ( /? - 2) ... 3 * 2 * 1
if /2 = 0
if / 2 > 0 ]
FORMULA 6- 1 Iterative Factorial Definition
factorial (4) = 4 * 3 * 2 * 1 24
350 Section 6.9 Recursion
Recursive Definition
whenever the function appears
A repetitive function is defined recursively
itself . For example , the factorial function can be defined
within the definition
recursively, as shown in Formula 6 - 2 .
factorial ( n ) = [I * factorial (n - 1)
if n = 0
if n > 0 ]
FORMULA 6 - 2 Recursive Factorial Definition
T
Factorial (0) = 1
Iterative Solution
Let’s write a function to solve the
factorial problem iteratively. This solution
usually involves using a loop, as shown in
I
Program 6- 24 .
Chapter 6 Repetition 351
Recursive Solution
I he recursive solution to factorial is shown in Program 6- 25 . I his program
does not need a loop ; the concept itself involves repetition .
In the recursive version , we let the function factorial call itself , each
time with a different set of parameters. Figure 6- 26 shows the recursive exe -
cution with the parameters for each individual call.
Every recursive call must either solve part of the problem or reduce the size
of the problem .
In the factorial problem , once the base case has been reached , the solu -
tion begins. The program has found one part of the answer and can return
that part to the next more general statement. Thus, in Program 6- 25, after
the program has calculated that factorial ( 0 ) is 1 , then it returns value 1.
That leads to solving the next general case .
factorial ( 1 ) -> 1 * factorial ( 0 ) -> 1 * 1 -» 1
The program now returns the value of factorial ( 1 ) to the more gen -
era I case , factorial ( 2 ) , which we know to be
As the program solves each general case in turn , the program can solve
the next higher general case, until it finally solves the most general case, the
original problem .
The following are rules for designing a recursive function:
1 . First ,
determine the base case.
2. I hen ,determine the general case.
3. Finally, combine the base case and general
case into a function .
In combining the base and general cases into a function , we must pay
careful attention to the logic . Each call must reduce the size of the problem
and move , t toward the base case. The base case,
when reached , must termi-
te without a call to the recursive function ; that is,
it must execute a return.
Chapter 6 Repetition 353
Fibonacci Numbers
Another example of recursion is a function that generates Fibonacci num-
bers. Named after an Italian mathematician , Leonardo Fibonacci, who lived
in the early thirteenth century, Fibonacci numbers are a series in which each
number is the sum of the previous two numbers ( Figure 6 - 27 .
)
Fib(4)
Fibn
Fibn 1- 0 Fibn-2
-
Fib n 3 [T| F b n-4 Fib(1) [T] Fib(O )
' 1 0
0 , 1, 1 , 2 , 3, 5 , 8 , 13 , 21 , 34
lo start the series, however, we need to know the first two numbers. As
you can see from the above series, they are 0 and 1 . Since we are discussing
recursion , you should recognize these two numbers as the base cases.
We can generalize the Fibonacci series as follows:
Given:
Fibonacci0 = 0
Fibonaccij = 1
Then
Fibonacci = Fibonacci - i + Fibonaccin _ 2
continued
PROGRAM 6 - 26 Recursive Fibonacci (continued )
45
46
47
return ( fib (num
} // fib
- 1) + fib ( num - 2));
Results:
This program prints a Fibonacci series.
How many numbers do you want? 30
First 30 Fibonacci numbers:
0,
5,
1
8,
/1
13,
2,
21 ,
. 3
34
55, 89, 144 , 233, 377
610, 987, 1597, 2584, 4181
6765, 10946, 17711 , 28657, 46368
75025 , 121393, 196418 , 317811 , 514229
Program 6- 26 Analysis Compare fib in Program 6-26 with the solution in Figure 6- 27. To determine the
fourth number in the series, we call fib with num set to 4. To determine the answer
requires that fib be called recursively eight times, as shown in Figure 6-27, which
with the original call gives us a total of nine calls.
This sounds reasonable. Now, how many calls does it take to determine
Fibonacci?
The answer is 15 ( Table 6- 2). As you can see from Table 6 - 2, the number of calls goes
up quickly as we increase the size of the Fibonacci number we are calculating.
Calls Calls
1 1 11 287
2 3 12 465
3 5 13 753
4 9 14 1,219
5 15 15 1,973
6 25 20 21,891
7 41 25 242,785
8 67 30 2,692,573
9 109 35 29,860,703
10 177 40 331,160,281
TABLE 6- 2 Fibonacci Run Time
Limitations of Recursion
We have introduced only the briefest explanation of recursion in this section .
VVe have not attempted to demonstrate how recursion works. To understand
how it works, you will need to study data structures and concepts that are
beyond the scope of this text . 1
On the other hand , you do need to understand the two major limitations
ol recursion . F irst , recursive solutions may involve extensive overhead
because they use function calls. Second , each time you make a call , you use
—
up some ol your memory allocation . If the recursion is deep that is , if the
—
program has a large number of recursive calls then you may run out ol
memory. Both the factorial and Fibonacci numbers solutions are better devel -
oped iteratively.
Does this mean that iterative solutions are always better than recursive
functions? The answer is definitely no. Many algorithms are easier to imple -
ment recursively and are efficient. When you study data structures, you will
study many ol them . Unfortunately, most of those algorithms require data
structures beyond the scope of this text .
The legend said that when all 64 disks had been transferred to the desti -
nation needle, the stars would be extinguished and the world would end .
Today we know' that we need to have 264 - 1 moves to do this task. Figure 6- 28
shows the Towers of Hanoi with only three disks.
This problem is interesting for two reasons. First , the recursive solution
is much easier to code than the iterative solution would he . This is often the
case with good recursive solutions. Second , its solution pattern differs from
the simple examples we have been discussing. As you study the Towers solu -
tion , note that after each base case, we return to a decomposition of the gen -
eral case for several steps. In other words, the problem is divided into several
subproblems, each of which has a base case, moving one disk .
358 Section 6.9 Recursion
Now imagine that we have to move two disks. First , the top disk is moved
to the auxiliary needle . Then the second disk is moved to the destination.
Finally, the first disk is moved to the top of the second disk on the destina-
tion . These three steps are shown in Case 2.
-
Figure 6 29 traces the steps for two disks.
A B C
Step 1
A B C
Step 3
n n n
A B C A
A
B C
Step 2 Step 3
Step 4
A B
Step 5
C A B
Step 6
C A 3
Step 7
4
FIGURE 6 - 30 Towers of Hanoi Solution for Three Disks
I he first three steps move the top two disks from the source to the auxil -
iary needle. (To see how to do this, refer to Case 2 . ) In Step 4 . we move the
bottom disk to the destination . We now have one disk in place. This is an
example of Case 1 . It then takes three more steps to move the two disks on
the auxiliary needle to the destination . These steps are shown in Case 3.
Study the third step carefully. After we complete the move of the first
disk , the remaining disks are on the auxiliary needle. We need to move them
-
from the auxiliary needle to the destination . In this case , the original source
needle becomes the auxiliary needle. Remember that the positions of the
-
parameters in the called function are source , destination , auxiliary . The call -
ing function must remember which of the three needles is the source and
which is the destination for each call .
We can now put these three calls together with the appropriate
print statements to show the moves . The complete function is shown in
Program 6 - 27 .
L
12
13 // Statements
14 printf( "Towers ( %d , %c , %c , %c )\n " ,
15 n , source, dest, auxiliary);
16 if ( n == 1 )
17 printf( "\t\t\t\tStep % 3d: Move from %c to %c\n" ,
18 ++step, source, dest);
19 else
continue
Chapter 6 Repetition 361
Calls Output
Towers (3 , A , C, B )
Towers ( 2, A , B , C )
Towers ( 1 , A, C , B)
Towers ( 1 , A, C , B)
Step 7: Move from A to C
Program 6 - 28 Analysis As you look at the changes in this version of our program, first note the two loops in
main. The first loop ( statement 32) continues the calculator until the user says it's time
to quit. The second loop ( statement 39) gets and validates the numbers, making sure
that the user isn' t trying to divide by zero. ( Your computer will get very upset if you
divide by zero and will stop!)
We also modified our getOption function to add the quit option and to vali-
date the options. If the user makes a mistake, we correct it in getOption. Extending
the concept to a general principle, whenever we write a function to get data from a
user, the function should handle all data validation. This makes for much simpler code
in the rest of the program.
This simplification is also seen in the switch statement. Since we have validated the
numbers before the switch, we no longer need to test for a valid divisor in the fourth
case option. We also no longer need a default, since we know the options are valid.
The result is a simpler statement, much more in line with the KISS principle.
Chapter 6 Repetition 365
} // select
1 while ( ... )
{
Process ... ) ;
A (
B ( ... ) ;
<> if (
C (
)
... );
A B C
} / / while
( a ) Design ( b ) Code
/( n ) = efficiency
Linear Loops
Let ’s start with a simple loop. We want to know how many times the body of
the loop is repeated in the following code:
.
2. Gilles Brassard and Paul Bratley, Algorithmic* theory amt Practice ( Upper Saddle River NJ Prenti
Hall . 1988 ), p. xiii.
Chapter 6 Repetition 367
The answer is 1000 times. But the answer is not always straightforward
as it isin the previous example. Consider the following loop. How many times
is the body repeated in this loop? I lore the answer is 500 times. Why ?
fiu ) = n
Logarithmic Loops
Now consider a loop in which the controlling variable is multiplied or divided
in each loop. How many times will the body of the loops he repeated in the
following program segments?
Multiply Divide
Iteration i Iteration
1 1 1 1000
2 2 2 500
3 4 3 250
4 8 4 125
continued
TABLE 6 - 4 Analysis of Multiply / Divide Loops
8 Section 6.11 Software Engineering
)
Multiply Divide
Iteration Iteration i
5 16 5 62
6 32 6 31
7 64 7 15
8 128 8 7
9 256 9 3
10 512 10
(exit) 1024 ( exit) 0
Generalizing the analysis, we can say that the iterations in loops that
multiply or div ide are determined by the following formula:
/( / / ) = ceil (log2/i)
Nested Loops
When we analyze loops that contain loops, we must determine how many
iterations each loop completes. The total is then the product of the number
of iterations for the inner loop and the number of iterations in the outer loop.
Linear Logarithmic
The inner loop in the following code is a loop that multiplies. (To see the
multiplication , look at the update expression in the inner for statement . )
ceil (log210)
I lowever, since the inner loop is controlled by an outer loop, the above
formula must be multiplied by the number of times the outer loop executes,
which is 10. This gives us
which is generalized as
Dependent Quadratic
Now consider the nested loop shown in the following example:
The outer loop is the same as the previous loop. However, the inner loop
is executed only once in the first iteration , twice in the second iteration , three
times in the third iteration , and so forth . The number of iterations in the
body of the inner loop is mathematically stated as
1 + 2 + 3 + . . . + 9 + 10 = 55
which is generalized to
( n + 1)
f( n ) = n
2
370 Section 6.11 Software Engineering
Quadratic
In the final nested loop, each loop executes the same number of times, as
seen in the following example:
The outer — —
loop that is, the loop at the first for statement is executed
.
ten times For each iteration , the inner loop is also executed ten times. The
answer, therefore, is 100 , which is 10 * 10, the square of the loops. This for-
mula generalizes to
M = /i 2
Big -0 Notation
With the speed of computers today, we are not concerned with an exact mea -
surement of an algorithm’s efficiency as much as we are with its general mag-
nitude . If the analysis of two algorithms shows that one executes 1 5 iterations
while the other executes 25 iterations, they are both so fast that we can ’t see
the difference. On the other hand , il one iterates 15 times and the other
1500 times, we should he concerned .
We have shown that the number of statements executed in the function
for n elements ol data is a function of the number of elements, expressed as
/( » ) . While the equation derived for a function may be complex, usually, a
dominant factor in the equation determines the order of magnitude of the
result . I herefore, we don t need to determine the complete measure of effi-
ciency, only the factor that determines the magnitude . This factor is the big-
0, as in On - the-Order-Of , and expressed as O ( w ) , that is , on -the-order-of n.
-
I his simplification ol efficiency is known as big O analysis , For example,
if an algorithm is quadratic , we would say its efficiency is
0( n2 )
-order of w-squared .
or on - the
I he big- O notation can be derived from (w ) using the following steps:
/
1 . In each term , set the coefficient of the term to 1 .
2. Keep the largest term in the function , and discard the others. Terms are
ranked from lowest to highest, as shown below.
log n n n log n n1 3
/i nk 2"
- '
11
Chapter 6 Repetition 371
„ = nielli = i„ 2 + ^1 n
f( )
2 2 2
M
/i 2 +n
/i
2
0( f(/i )) = 0( /i 2 )
f ( /l ) = 371
k
+ 3y _, /1
k-1
+
— + 32 /I + 3
2
, /1
I
+ 3n
= nk + n - + •• +
k 1 2
f ( /# ) • /i +n+1
The largest term in this expression is the first one, so we can say that the
order of a polynomial expression is
0( f ( /» )) = 0(„*)
Efficiency -
Big 0 Iterations
logarithmic 0(log n) 14
linear O(n) 10,000
linear logarithmic 0( n(log n) 140,000
quadratic Ofn2) 10 ,0002
polynomial Q [ nk ) 10, 000fe
exponential 0( cn ) 2 io, ooo
factorial O(nl ) 10,000!
Looking at the problem from the other end , if we use a computer that
executes a million instructions per second and the loop contains ten instruc-
tions , then we spend .00001 second for each iteration of the loop. Table 6- 5
also contains an estimate of the time to solve the problem given different
efficiencies.
Chapter 6 Repetition 373
i = 0;
while (i != 13 )
{
> // while
.
6. It is a logic error to omit the update in the body of a while or do . . while loop.
Without an update, either explicit or implicit , the loop never terminates.
7 It is a common logic error to miscode the limit test in for statements. I he
.
result is usually a loop that executes one extra time or terminates one
iteration short . For example, the following statement executes nine times,
not ten:
i += 1;
} // for
374 Section 6.14 Summary
6.14 Summary
The real power of computers is in their ability to repeat an operation ora
series of operations many times.
J lo control the loop, we need a condition to determine if more processing
is needed .
J In a pretest loop, in each iteration , we check the condition first . II it is
true, we iterate once more; otherwise, we exit the loop.
J In a post - test loop, in each iteration , we do the processing. Then we check
the condition . It it is true, we start a new iteration ; otherwise, we exit
the loop.
J In a pretest loop, the processing is done zero or more times.
J In a post- test loop, the processing is done
one or more times.
J In a pretest loop , if the body is executed n times, the limit test is executed
n + 1 times .
Chapter 6 Repetition 375
1
J In a post -test loop, it the body is executed n times, the limit test is exe -
cuted n times.
J 1 he control expression in a loop must be explicitly or implicitly initialized .
IJ It you know exactly the number of times the body must he repeated , use a
counter-controlled loop ; it an event must occur to terminate a loop, use an
event-controlled loop.
C has three loop statements: while , for , and do ...while .
1 he while loop is a pretest loop. It can he used for a counter- controlled or
-
event controlled loop, hut it is usually used only for event control.
I he for loop is a pretest loop. It can be used for both counter-controlled
and event -controlled loops , hut it is used mostly in the first case.
The loop variable in a for loop may be locally defined in the for statement .
The do...while loop is a post -test loop. It is usually used when the body
must be executed at least once.
VVe discussed two C statements that are related to looping, break and
continue.
The break statement is used to terminate a loop prematurely. We strongly
recommend that you use the break statement only within switch statements.
The continue statement is used to skip the rest of the statements in a loop
and start a new iteration without terminating the loop. VVe strongly recom -
mend that you never use the continue statement .
The best loop for data validation is the do. .. .while loop.
Recursion is a repetitive process in which a I unction calls itself .
The statement that solves a recursive problem is known as the base case;
the rest of the function is known as the general case.
A loop in a structure chart is indicated by a circle on the line connecting it
to the called functions. Only loops that call other functions are shown .
a . True
b. False
4. Recursion is a repetitive process in which a function calls itself .
a. True
.
b False
.
5 Which of the following statements about pretest loops is true?
a. If a pretest loop limit test is false, the loop executes one more time.
b . Pretest loop initialization is clone first in the loop body.
c. Pretest loops execute a minimum ol one time .
.
d Pretest loops test the limit condition after each execution of the
loop body.
e. The update for a pretest loop must be a part of the loop body.
6. Which of the following statements about loop initialization is falser
a. Explicit initialization includes code to set the initial values of loop
variables.
b. Implicit initialization relies on preexisting values for loop variables.
c. Initialization code is explicitly required in all loops.
d . Initialization is preparation required for proper execution of a loop.
.
e Initialization must he done before the first execution ol the loop.
7. Which of the following statements about loop updates is false?
a . A loop update changes key variable(s) in a loop, thus allowing the loop
to terminate .
b. Loop updates may be made before or after a loop iteration .
c . Loops may use explicit or implicit updates.
d . In a for loop, updates are generally found in the for statement itself .
e. I he number ol updates always equals the number of loop iterations.
8. Which of the following statements about counter-controlled loops is false?
a . Counter-controlled loops are generally pretest loops.
b. Counter-controlled loops generally increment or decrement a counter.
c. Counter-controlled loops require a limit test .
d . The number of times a loop iterates must be a constant .
e. I he update in a counter-controlled loop is generally explicit .
9. Which ol the C loops is a pretest loop?
a . do... while
.
b for
c. while
d. both the do. . . while and the for
e .
both the for and the while
10. \ \ hich ol the lollowing statements about the while
statement is true?
a . Multiple statements are allowed in a while
loop.
b. I he limit test in a while loop is made before
each iteration .
r T
is false?
a . Both statements allow only one statement in the loop.
b. Both statements are pretest loops.
c . Both statements can he used for counter-controlled loops.
d. Both statements include initialization within the statement .
e. Both statements require an update statement.
..
12. Which of the following statements about the do . while loop is false ?
a. A do. . .while loop executes one or more iterations.
b . Anv statement may he used as the action in a do ... while .
c. The do ... while is best suited for use as an event-controlled loop.
d . The do...while is the only loop that requires a semicolon.
e. The limit test in a do...while loop is executed at the beginning ot each
iteration .
13. Nested loops have a standard measure of efficiency.
a. exponential
h . linear
.
c linear logarithmic
d . quadratic
e. linear logarithmic or quadratic
14. The standard measure of efficiency is considered the
most efficient .
a . exponential
b. linear
c . logarithmic
d . quadratic
e. polynomial
Exercises
13. What would he printed from each of the following program segments?
Compare and contrast your answers to parts a , b, and c.
a.
x = 12 ;
while ( x > 7)
printf("%d\n " , x );
3/ 8 Section 6.15 Practice Sets
b.
c.
x = 12;
do
printf( " %d\n " , x );
while (x > 7);
16. What would he printed from each of the following program segments?
Compare and contrast your answers to parts a , b, and c.
a.
x = 12;
while ( x > 7)
{
printf( "%d\n " , x);
x --;
>
b.
c.
x =12;
do
{
—
printf( "%d\n" , x ) ;
x ;
} while (x > 7);
17. What would be printed from each ol the following program segments?
Compare and contrast your answers to parts a and b.
a.
x = 12 ;
while ( x > 7)
{
printf( " %d\n" , x ) ;
-
x = 2;
>
Chapter 6 Repetition 3/9
b.
18. What would be printed from each of the following program segments ?
Compare and contrast your answers to parts a , b, and c.
a.
x = 12 ;
while ( x < 7 )
{
printf( " %d \n " , x );
x
;
—
} // while
b.
c.
x 12;
do
{
printf( "%d\n " , x );
x
— ;
} while ( x < 7);
x = 0;
while ( x < 10)
{
printf( "%d\n " , x );
x++ ;
}
b.
b.
-
for ( ; scanf( " %d " , &x) ! EOF ; )
printf ( "%d\n" , x);
x = 0;
do
{
printf( " %d\n" , x );
x++;
> while ( x < 100);
b.
do
{
res = scanf("%d " , &x );
> while (res != EOF );
26. Another programmer writes the following lor loop to print the numbers 1
to 10 . What is the output ? It the output is
incorrect, how would you cor-
rect it ?
b.
b.
x ;
—
printf( " %d\n " , x );
} // for
b.
.
30 What will be printed from the following program segments ?
a.
b.
for (int x
{
= 20; x >= 1; x
— )
for ( ; ; )
{
> // for
.
36 Write a program that asks the user to enter a list of integers. The pro-
gram is to determine the largest value entered and the number of times it
was entered , l or example , if the following series is entered
5 2 15 3 7 15 8 9 5 2 15 3 7
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7
1 2 3 4 5 6
1 2 3 4 5
1 2 3 4
1 2 3
1 2
1
38. Write a function that creates the following pattern , given the height
( number of rows ) and the width (asterisks per row ) :
* ******* *****
*** **** ** * * * *
**** * * ** * * ** *
* * * * * * ** * * * * * ,
39. Write a function that creates the following pattern , given the height
( number of rows ) and the width ( print characters per line ):
* *
* *
*
* *
40. Write a function that creates the following pattern , given the height
( number of rows ):
*
* **
* * ***
* * * ** * *
* ***** * **
** * ** * * * * * *
. Jd
384 Section 6.15 Practice Sets
41. Write a function that creates the following pattern , given the height
( number of rows ) :
*** * * * * * * * *
* ** * ** * * *
* * * * * **
** * * *
** *
.
42 Write a function that creates the following pattern , given the height
( number of rows ):
*
** *
* ****
* ** * * * *
** * * * * ***
* * * ** * * * *
* *** * **
** ***
* **
*
44 . Write a program that reads integer data from the standard input unit and
prints the minimum integer read , maximum integer read , and the average
of the list . Test vour program with the data shown below.
{24 7 31 -5 64 0 57 -23 7 63 31 15 7 -3 2 4 6}
45 . In “ I he do. ..while Loop" in Section 6.5, we
demonstrated the use of a
do ...while to validate input. I he code fragment
contains no message to
tell the user that an invalid number has been
entered . Write a function
that reads only positive even numbers from the keyboard . If a negative or
odd number is entered , it should print an
error message and ask the user
to enter another number. Each call to
the function is to read only one
'l l
{ 2 18 -18 5 7 100 1 -1 }
46. Write a function that reads integers from the keyboard . If any of the
numbers are negative, it returns a negative number. If all the numbers
are positive , it returns their average. ( Hint: See Program 6- 22. )
47 . Program 6 - 19 uses a while loop to read a series of numbers from the key-
board . Since you will always have at least one number in this program ,
rewrite it to use the do. ..while .
_ .
48. Program 6- 21 uses INT MAX from the limits h library to initialize the
smallest variable. Another solution is to read the first number and put
its value in smallest , then go into the loop to read the rest of the num -
bers. Modify Program 6- 21 to make this change .
49. Euler 's number, e , is used as the base of natural logarithms. It can be
approximated using the following formula :
e - l + I + I + I + I + I + I + ••• +
1! 2! 3! 4! 5! 6!
1
(w - 1)
+I
n\
1
0 = 6
lh h h
+ + + . .. +
limit 2
Write a function that uses this formula to calculate pi . Then write a test
driver, and run it once to test your function with a limit of 5 terms and a
limit of 10 terms. Display the result of each test.
5 1 . Write a program that reads an integer from the keyboard and then calls a
recursive function to print it out in reverse. For example, if the user
enters 4762 , it prints 2674.
52. Rewrite Program 6- 26 using an iterative solution .
Projects
to the arithmetic
53. Statisticians use many different algorithms in addition
average. Two other averages are thegeometric and the harmonic mean .
386 Section 6.15 Practice Sets
7xixx 2 x - • * xn
n •
I !
n
+ + ... +
1 _
X, X 2 x *
Write a program that reads a series of numbers and calculates the aver -
age , geometric mean , and harmonic mean .
54. Write a C program that can create four different patterns of different
sizes. The size of each pattern is determined by the number of columns
or rows. For example , a pattern of size 5 has 5 columns and 5 rows. Each
pattern is made of character $ and a digit , which shows the size . The size
must he between 2 and 9. Table 6 - 6 shows the four patterns in size 5 .
M E N U
1. Pattern One
2. Pattern Two
3. Pattern Three
i
continued
I
4. Pattern Four
5. Quit
Choose an Option ( between 1 and 5 ): 11
Your Option is incorrect. Please try again.
Choose an Option ( between 1 and 5 ): 3
Choose a Pattern Size ( between 2 and 9 ): 12
Your Pattern size is incorrect. Try again.
Choose a Pattern Size ( between 2 and 9 ): 4
Option
SET 1 1
SET 2 2 3
SET 3 3 4
SET 4 4 5
SET 5 6
SET 5 3 6
SET 6 2 10
SET 6 7
SET 7 5
2000
Enter the year for your calendar :
i
W
388 Section 6.15 Practice Sets
The output is a calendar for the whole year ( 12 months ) . One month is
shown in the next example.
JANUARY 2000
SUN MON TUE WED THU FRI SAT
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
To print the correct calendar for the requested year, you must first find which
day of the week is the first day of the that year. This can he done with the fol-
lowing formula . ( For a complete explanation , see Chapter 5, Project 61.)
You also must calculate leap years. The formula for this calculation is
also found in Chapter 5 , Project 61 .
Run your program once with the following sets of data 3:
SET Is 2005
SET 2: 0
SET 3: 2000
SET 4: 123
tion table.
NM = ( NY * 12)
Chapter 6 Repetition 389
IM = (IY / 12 ) / 100
P = (1 + IM)NM
Q = ( P / (P - 1) )
MP = (PR * IM * Q)
where
NY: Scheduled number ol years to amortize the loan
NM : Scheduled number of months lor the loan
IY: Interest rate per year ( as a percentage)
IM : Interest rate /month ( decimal )
PR : Principal ( the amount of the loan )
P: Fhe value of ( 1 + IM )NM
Q: The value of P / ( P - 1 )
MP: Monthly payment
Run your program once with the test data shown in Table 6-8.
10 , 000.00 12 l
Set 1
Set 2 5, 0 0 0 . 0 0 10 2
Set 3 1, 000.00
The output is shown in Table 6 - 9. Note: Your answer may look a little dif -
ferent ( a few pennies ) because ol different precision .
Number of years: 1
Number of months: 12
.
57 Write a program that reads a list ol integers from
the keyboard and cre -
ates the following information :
a. 1 inds and prints the sum and the average of
the integers
b. Finds and prints the largest and the smallest integer
.
c Prints a Boolean ( true or false ) if some of
them are less than 20
d . Prints a Boolean ( true or false ) if all of
them are between 10 and 90
raw
I V'
Chapter 6 Repetition 391
.
I he input data consist of a list of integers with a sentinel The program
must prompt the user to enter the integers, one by one, and enter the
sentinel when the end of the list has been reached. The prompt should
look like the following:
.
58 The formula for converting centigrade temperatures to Fahrenheit is
180.0
F = 32 + C x
100.0
Write a program that prints out conversion tables for Celsius to Fahren-
heit (0° to 100°) and Fahrenheit to Celsius ( 32° to 212°). Use separate
functions to convert Fahrenheit to Celsius and Celsius to Fahrenheit.
The output format for each table is to ( it on a standard monitor display,
80 columns by 20 rows.
59. Rewrite “Towers of Hanoi” Program 6 - 27 using an iterative solution.
1
I
Text Input/Oulput
A program is a data processor: It accepts input data, processes data, and cre -
ates output data. Handling input /output is a complex task, largely because of
the variety of devices and data formats. Data may come from many different
sources and may go to different destinations. For example, data may come
from such diverse sources as a keyboard, a file on the disk, a heating or air-
conditioning system, or a communication channel ( network or Internet ).
Data may also go to many destinations, such as a monitor or a file on the disk,
or to a communication channel. In this chapter, we concentrate on the key-
board and files as input entities and the monitor and files as output entities.
Objectives
To understand the basic properties and characteristics of external files
To understand the C implementation of file I /O using streams
To write programs that read and write text files using the formatting functions
To write programs that read and write text files using the C character I / O
functions
To write programs that handle simple I /O errors
To understand and implement basic data validation concepts
394 Section 7.1 Files
7.1 Files
.
A file is an external collection of related data treated as a unit 1he primary
purpose of a file is to keep a record of data. Since the contents of primary
memory are lost when the computer is shut down, we need files to store our
data in a more permanent form. Additionally, the collection ol data is often
too large to reside entirely in main memory at one time. I herefore, we must
have the ability to read and write portions of the data while the rest remain in
the file.
system .
As a file is being read, there eventually comes a point when all the data
have been input. At this point, the file is said to be at end of file . The end ol
file in an auxiliary device is detected automatically by the device and passed
to the program. It is the programmer s job to
test for end of file. This test is
often done in a loop control statement.
File Name
Lu i \ operating system uses a set ol rules for
to read or write auxiliary storage
.
naming its files When we want
files, therefore, we must use the operating
systems rules when we name the files We . refer to the operating systems
name as the file name in this text .
character in the file, and so on. C has predefined a file structure 1 to hold this
information. I he stdio.h header file defines the file structure; its identifier is
F I L E . When we need a file in our program, we declare it using the F I L E type .
7.2 Streams
Although the source and destination of data is a file or a physical device in C,
as we briefly discussed in Chapter 2, data are input to and output from a
stream. A stream can be associated with a physical device, such as a terminal,
or with a file stored in auxiliary memory. Figure 7 - 1 shows this idea.
Monitor
Data Destination
FIGURE 7- 1 Streams
in Chapter 12 .
I . Technically, a structure ( struct type ). We discuss structures
396 Section 7.2 Streams
name Once the file is opened, we read or write data; that is, we process the
file. When the processing is complete, we close the file. We describe the steps
to make this association in this section.
Creating a Stream
We create a stream when we declare it . I he declaration uses the FILE type as
shown in the following example. The FILE type is a structure that contains
the information needed lor reading and writing a file.
FILE * spData ;
Opening a File
Once the stream has been created, we are ready to associate the stream to a
file. I his is done, as we discuss in detail in the next section, through the stan-
dard open function. When the file is opened, the stream and the file are asso -
ciated with each other, and the FILE type is filled with the pertinent file
information. I he open function returns the address of the file type, which is
stored in the stream pointer variable, spData. The file open function creates
the stream, which we then refer to by its name.
Standard stream names have already been declared in the stdio.h header file
and cannot be declared again in our program .
The association between the three standard streams and the keyboard
and the monitor is also done automatically when the program starts. I here-
fore, we cannot open any of the standard streams in our code. Like file
streams, the standard streams must be closed at the end ol the program.
I lowever, they are closed automatically when the program terminates.
There is no need to open and close the standard streams. It is done auto -
matically by the operating system .
File
Open/Close
Formatted
Input/Output
Character
Input/Output
Categories of
I/O Functions
Line
Input/Output
l
Block
Input/Output
File
Positioning
System
File Operations
File
Status
uyl
Vr
•
because we do not need to see them. All we need to know is that we can store
the address of the file structure and use it to read or write the file . A complete
open statement is shown below. Continuing with our temperatures example,
we could open it for output ( writing ) with the following open statements. The
first is the basic format as it might he used for the current directory in UNIX
or Windows; the second is the Windows version to open a file for drive A. 2
Internal
int main (void) ile Variable ,
FILE* spData ;
} // main External
File Name
FILE
~ L,
I | J spData
-|
MYDATA . DAT
Physical File ^
C Stream Data
Next , note the name we have used for the file address. I he sp stands for
“stream pointer”; we use it in all of our stream identifiers. To the stream
pointer abbreviation we add the name of the file. Because this is a generic
example, we use “ Data . ” This combination gives a readable name that is easy
to remember.
Our stream pointer variable, spData, is assigned the address of the file
structure when we open the file; the return value from the fopen
function
contains the file structure address. Later in the program , when we need to
read or write the file, we use this pointer.
File Modes
When we open a file, we explicitly define its mode . The mode shows how we
will use the file: for reading, for writing, or for appending, which means add-
mg new data at the end of the current file. C has six different file modes. The
first three, which we discuss here , are used to read , write, or append text files.
In Chapter 13 we discuss the other modes, which allow both reading and
writing in the same file. The mode codes discussed in this chapter are shown
in Iable 7- 1.3
Mode Meaning
r Open text file in read mode
•If file exists, the marker is positioned at beginning .
•If file doesn't exist, error returned.
w Open text file in write mode
•If file exists, it is erased.
•If file doesn't exist, it is created.
a Open text file in append mode
•If file exists, the marker is positioned at end.
•If file doesn't exist, it is created.
TABLE 7- 1 Text File Modes
Figure 7- 4 describes the simple open modes.
Open
Open existing file Open new file
for reading existing file for writing
for writing vor create new file^
Read Mode
I he read mode ( r ) opens an existing file lor
reading. When a file is opened in
t M S mode , the file marker is
positioned at the ( the first
beginning of the file
3. There are more modes. We discuss them
in Chapter 13.
Chapter 7 Text Input/Output 401
character ). I he file marker is a logical element in the file structure that keeps
track of our current position in the file.
Hie file must already exist : If it does not , NULL is returned as an error.
Files opened for reading are shown in Figure 7- 4a . If we try to write to a file
1
'^
Write Mode
I he write mode ( w) opens a file for writing. If the file doesn ’t exist , it is cre-
ated . II it already exists, it is opened and all its data are deleted ; that is , it
assumes the status of an empty file. It is an error to try to read from a file
opened in write mode . A file opened for writing is shown in Figure 7 - 4 b.
Append Mode
I he append mode ( a ) also opens an existing file for writing. Instead ol creat -
ing a new file, however, the writing starts after the last character; that is, new
data are added , or appended , at the end of the file.
If the file doesn ’t exist , it is created and opened . In this case, the writing
will start at the beginning of the file ; the result will he logically the same as
opening a new file for writing. Files opened in append mode are shown in
Figure 7- 4 c . If we try to read a file opened for write append , we get an error
message.
// Statements
fclose( spTemps );
Program 7- 1 Analysis The most common mistake in testing for a successful open is getting the parentheses
wrong, as shown below.
vlj
if (spTemps = fopen("TEMPS.DAT" , "w") == NULL)
The above statement is syntactically correct ( we do not get a compile error) but
invalid. You must test the address returned by fopen after it has been assigned to
spTemps. In the above statement, spTemps is assigned the logical value of the
expression
because the equal operator has a higher precedence than the assignment operator.
Study the open in Program 7- 1 carefully, and make sure you understand the difference
between it and the incorrect version shown in the previous example.
The error testing for the close is much simpler; we can use a simple test for the error
code, EOF. Note that we have assigned a different return code to distinguish the open
failure from the close failure.
Stream Pointer
The first argument in the text input/output function , stream pointer, is the
pointer to the streams that has been declared and associated with a text file.
The following example demonstrates the use of an input stream .
FILE* spin ;
While we normally read data from the terminal keyboard using scanf , we
can also use fscanf . \\ hen we use fscanf we must specify that the stream
pointer is std m , as shown in the following example.
'
FILE* spOut;
Whitespace
Format control string whitespace is handled differently for input and output.
In an input function , one or more whitespaces in
the format string cause
zero, one , or moie whitespaces in the input stream
to be read and discarded ,
Thus, ,am sequence ol consecutive whitespace
characters in the format string
will match any sequence ol consecutive
whitesjnice characters, possibly ol
different length , in the input stream .
FI
Chapter 7 Text Input/Output 405
Text
Any text character other than a whitespace in an input format string must
match exactly the next character of the input stream. If it does not match, a
.
conflict occurs that causes the operation to be terminated The conflicting
input character remains in the input stream to be read by the next input oper -
ation on that stream. We therefore recommend that you do not use text char-
acters in an input format string.
Conversion Specificotion
The conversion specification consists of a percent character ( % ), optional for-
matting instructions, and a conversion code. With one exception, each con-
version specification must have a matching parameter in the parameter list
that follows the format string. The type in the conversion specification and
the type of the parameter must match.
The number, order, and type of the conversion specifications must match the
number, order, and type of the parameters in the list. Otherwise,
the result
will be unpredictable and may terminate the input /output function .
scanf /fscanf
maximum size conversion
width code
. Suppress h *
short
.
4
d i, u, o, x,
J I
I
L
long int
double
long double
c, s, p, n,
..
a t e, g, [
- left justify
+ sign ( + or - ) hh char
.. . .
d i, u, o x, X,
. . .
space if positive h short
I long int f, e E, g G, a A,
0 zero padding c s, p n, %
# alternate L long double
T T T
minimum
precision size conversion
% flag width code
printf /fprintf
Conversion Codes
The conversion code specifies the type of data that are being formatted. For
input, it specifies the type of variable into which the formatted data arc
stored. For output, it specifies the type of data in the parameter associated
with the specification. It is our responsibility to ensure that the data are of the
right type. II they are not, strange formatting may result. Conversion codes
are discussed in detail in the input Formatting { scanf andfscanf )" and " Out -
put Formatting ( printf and fprintf )" sections that follow.
scanf reads from stdin; fscanf reads from a user - specified stream.
addresses where the data are to be stored alter they have been formatted. A
comma must separate the format string from the variable list . If more than one
vaiiable is present, then separate the variables from each other by commas.
We must include a variable address in the address list for every Conver -
sion specification in the format string that requires data. If we do not, the ts
result is unpredictable and undefined.” This is a standard disclaimer that
means anything can happen. I he usual result is that the program doesn ’t do
what we expect and often crashes.
pointer P
integer (for count) none int), hh (char ), h ( short), I (long) , II n
(long ong)
set none (char), I ( wcharj) [
Flag
There is only one (lag for input formatting, the assignment suppression Hag (*).
More commonly associated with text files, the assignment suppression flag
tells scanf that the next input field is to he read but not stored. It is discarded.
The following scanif statement reads an integer, a character, and a floating-
point number from the input stream. The character is read and discarded.
The other fields are read, formatted, and stored. Note that there is no match-
ing address parameter for the data to be discarded.
Width
I he width specifies the maximum width of the input ( in characters) This .
allows us to break out a code that may he stored in the input without spaces .
Consider what happens when we read a Social Security number from a file
anti the number has no formatting; it is just nine digits in a row, followed by a
space. We could read it into three variables, thus allowing us to format it with
dashes in our output , with the following format specifications:
scanf( " % 3 d % 2 d % 4 d..." , & ssnl , & ssn 2, & ssn 3 , ...);
Note that the width is a maximum. If the amount of data is less than
required, the scan terminates. What determines the end of the data depends
on the type of data, but generally whitespace will
terminate most scans.
Size
The si /.e specification is a modifier for the conversion code. Used in combi-
nation with the conversion code, it specifies the
type of the associated vari-
able, lor example, a long double (Lf ) . The
size codes, along with their
associated conversion codes, are explained in Table 7 3, Sizes and Conversion
Code tor scan ) Family.”
- "
II
Chapter 7 Text Input/Output 409
Conversion Codes
\\ ith the exception of string and pointer, this section discusses the input con -
.
version codes I he pointer conversion code is discussed in Chapter 9 ; the
string conversion code is discussed in Chapter 1 1 .
' 4«
Integer (d ) I he decimal ( d) format code accepts a value from the input stream
and formats it into the specified variables. It reads only decimal digits and an
optional plus or minus sign as the first character of the value.
Integer ( i ) The integer format code (i) allows the user to key a decimal, octal,
or hexadecimal number. Numbers starting with any digit other than zero are
read and stored as decimal values. Numbers starting with / ero are interpreted
as octal values and are converted to decimal and stored. Hexadecimal num-
bers must he prefixed with Ox or OX; the hexadecimal value is converted to
decimal and stored. We strongly recommend that the integer format code be
used only when non-decimal data is being entered—such as when program -
mers are entering technical data; in all other cases, the decimal format code
( d) should be used.
Octal and Hexadecimal (o, x ) The octal ( o) and hexadecimal ( x ) conversion codes
perform unsigned conversion. For octal, the only valid input digits are 0...7.
For hexadecimal input, the valid digits are 0... 9, a... f, and A...F. If you are
not familiar with octal or hexadecimal numbers, sec Appendix I). Numbering
"
Systems."
Scientific Notation (e , g , a ) The C language uses three real format codes lor scien -
tific notation. In scientific notation, the significand and exponent are speci -
fied separately. The significand part is a floating-point number that contains
as many significant digits as possible. For example, if it contains six digits,
then the number is significant only to six digits; if it has twelve digits, then it
is significant to twelve digits. The larger the significance, the greater the pre -
cision. Therefore, long double may he more precise than double, which may
he more precise than float .
The exponent specifies the magnitude of the number. It may be either
positive or negative. If it is positive, then the number is the significand times
ten to the power of the exponent, which may be a very large
number. If it is
negative, then the number is the significant times the reciprocal of the base
ten exponent , which may be a very small number . These forms are
shown below.
2e2
3e -1.0e-3 0.1+el
d
410 Section 7.4 Formatting Input/Output Functions
For input , all conversion codes ( f , e , g , a ) accept all three scientific nota -
tions as well as algebraic notation.
When we read data, we should verify that the data were read correctly.
This is easily done by checking the return value from the scan function.
Program 7 - 2 is a program fragment that shows how the two numbers in
Figure 7 - 7 could he checked.
Results:
Enter amount and price: ? 15.25
-
Amount incorrect. Re enter both fields
Program 7- 2 Analysis We have introduced a new define statement, FLUSH , in this program segment.
Why is this statement necessary? Recall that when scant encounters an error, it
leaves the invalid data in the input stream. If we simply print the user messages and
then return to the scant statement, the invalid data are still there. We need to get rid of
them somehow! This is the purpose of the FLUSH statement. It reads all data from the
invalid input to the end of the line. For a complete discussion of the flush statement, see
"Testing for Valid User Input" in the software engineering section.
Also note how the error messages tell the user exactly what went wrong, rather
than giving a general "The numbers entered are incorrect" type of message. Whenever
possible, you should tell the user exactly what went wrong.
Invalid Address
Fhe first common mistake is to use a data value rather than an address for
the input parameter. The scan function places the formatted input at the
address specified in the parameter list . When we pass data rather than an
address , C interprets the data as an address . This causes a part of memory to
be destroyed . II we are lucky, our program fails immediately. If we are
unlucky, it runs until the destroyed part of the program is needed and then
fails . This problem is shown in Figure 7 - 8 .
A missing address
operator will cause
vour program to fail.
he filied again until it has been completely processed or flushed by the pro-
gram. I he next input operation will read from the unused stream instead of
1
reading from a new input stream. The result : Everything is a mess!
In figure 7 -9, the user meant to enter the number 235 but instead
entered 23r. ( I his is a common error. Note that the r is just below and to the
lei t of the 5 on the keyboard.) When scanf reads the input stream, it interprets
the number 23 but stops with the r and returns 1 , indicating that one decimal
number was successfully formatted. The r remains in the input stream, wait -
ing lor the next scanf . The typical result of this error is a perpetual loop look -
ing for an integer value. Even it we detect and flush the error when the r is
read, we still have processed the wrong data ( 23, not 233 ) in the first read!
2 3 r \n
printf(" \nPlease enter the price : ");
scanf( " %d " , &a); Input stream
after scanf ,
s scanf completed x) ,
V V a l u e o f a i s 2 3! v 2 3 r \n
Input
n=2 Stream
n = fscanf ( fp, " %d %d " , &a , & b); 1234 752 -
i
n=1
%d ", &a , & b); 1234 F -•
n = fscanf ( fp , " %d
n = EOF
<EOF>
n = fscanf ( fp, " %d %d " , &a, & b);
Input Examples
In this section , we present several examples of scanf loimatting. Some ol
them are rarely used , hut they illustrate some of the finer points of format-
ting. We suggest that you cover up the answer until you have tried to write
the statement yourself .
—
EXAMPLE 7- 1 Write the scanf statement that will read three values a real number, an inte-
—
ger, and a character from the standard input file . I he input is
3.1416 31416 x
—
EXAMPLE 7- 2 Write the scanf statement that will read three values a real number, an inte-
—
ger, and a character from the standard input file. All data are on different
lines. The input is
3.1416
31416
X
Note that this statement is the same as the first example. Since the new-
line is whitespace, it makes no difference if the data are separated by new-
lines, spaces, or tabs. However, the following code fails when it tries to read
the character!
Do you see the mistake - Recall that the character conversion code ( c )
J
does not skip whitespace. I herefore, it reads the return character from the
integei input. Io prevent this problem , code a space before the character for-
mat specification. The space discards all whitespace
characters before it
reads the character.
EXAMPLE 7- 3 Write the scanf statement that will read two fractions, such as 14 /26 and 25/
66 , 11 om tlu standard input unit and place the numerators
and denominators
in integer variables.
-
\ \ rite the fscanf statement that will read a date , such as 5 10- 1936, Format -
ted with dashes ( - ) into three different integer fields. The input is to he read
1
1
Note that with this statement , an error results if the user enters 5 / 10/ 1997 .
EXAMPLE 7-5 \ \ rite the fscanf statement that will read three integers from a file opened as
spData and assign the number of successfully formatted variables to the
identifier result .
EXAMPLE 7-6 Given a file with four integers in each line , write a fscanf statement to read
only the first , second , and fourth integer from a line; that is, discard the third
element . Read the data from the file spData.
EXAMPLE 7- 7 Write the fscanf statement that will read three integers from a file opened as
spData and assign the number of successfully formatted variables to the
identifier result. Use a defined constant for the format string.
3. The scanf function does not terminate until the 1 ormut sii ing is exhausted ,
that is , until all specified operations are complete. W hitespace characters
in the format string cause whitespace characters in the buffer to be read
and discarded. When a whitespace character occurs at the end of the for-
mat string, scanf reads and discards the return character in the buffer
and then waits for more input. It cannot terminate until it finds a non -
whitespace character.
Lets look at some examples.
EXAMPLE 7-8 In this example, we have just one scanf call in our program that reads two
integers. In this case , the return character remains in the buffer, but it is not
a problem. When the program terminates, the operating system flushes all
buffers.
EXAMPLE 7-9 In this example, we explicitly consume the return character by reading and
suppressing it.
EXAMPLE 7- 10 In this example, we let the return key stay in the buffer and let the second
call discard it . Note that in this case, it does not matter if the next scanf fol -
lows immediately or after several lines of code. The first conversion specifica-
tion ( % d ) at the beginning ol the second scanf discards the return character
left hv the first scanf I he return character for the second input stream , how-
ever, remains in the buffer.
EXAMPLE 7- 1 1 When the first format specification in a scanf format string uses a character
oi edit set conversion code , any
return character left in the buffer by a previ-
H I S scanf operation must he
(
manually consumed . This can be problematic
because the leftover return character is read first as shown below.
// Preferred Solution
scanf ( " %d %d " , &numl , &num2);
scanf (" %c %d" , &aChr, &numl);
Alternate Solution:
scanf ( " %d %d%*c" , &numl, &num2);
scanf ( " %c %d " , &aChr , &numl );
EXAMPLE 7- 1 2 The scanf function can hang our program . For example, in the following frag-
ment of code , we expect that the printf function be called after we entered
two integers . The program , however, hangs . The reason is that the whitespace
at the end of the control string tells scanf to discard any whitespace it sees in
the buffer. Therefore, scanf deletes our return character. But , to complete its
operation , scanf must find a return character. It does not matter how many
return keys we enter on the keyboard , scanj discards all ol them looking lor a
non - whitespace character.
Note, however, that the extra character we enter ( x ) remains in the buffer
and will be read next time scanf is executed . This extra character is undoubt -
edly not valid for the next read , which means that our program will produce
invalid results.
One of the first differences to note is that the value list is optional.
Whereas you always need a variable when you are reading, in many situa-
tions , such as user prompts , you display strings without a value list .
Three examples of printf output that might be found in a program arc
shown below. The first is a greeting message at the start of the program, the
second displays the result of a calculation, and the last iis a closing message.
The fprintf function works just like printf except that it specifies the file
.
in which the data will be displayed The file can be the
standard output ( stdout )
.
or standard error ( stderr ) files For example, to write the three
previous lines
to a report file , we use the following code:
pointer P
to print % %
Flog
There are four output flags: justification , padding, sign , and alternate form
.
Table 7 - 5 shows the output flag options.
Sign Flog I he sign flag defines the use or absence of a sign in a numeric value .
We can specify one of three formats: default formatting, print signed values,
or prefix positive values with a leading space. Default formatting inserts a
.
sign only when the value is negative Positive values are formatted without a
sign . \ \ hen the flag is set to a plus ( + ), signs are printed for both positive and
negative values. If the flag is a space, then positive numbers are printed with
a leading space and negative numbers with a
minus sign.
Alternate Flag The alternate flag ( # ) is used with the real, engineering, hexadeci-
mal, and octal conversion codes. The alternate flag is discussed with the con-
version codes to which it applies.
Width
.
I he meaning of the width field is
In t in the opposite direction.
conceptually the same for input and output,
For input, the width specifies the maximum
> p
width. I or output specifications, the width provides the minimum output width.
However, if the data are wider than the specified width, C prints all the data .
Precision
Precision is specified as a period followed by an integer. Precision has mean -
ing only lor output fields, and it is an error to use it for input. Precision can
control the following: 5
1 . I he minimum number ol digits for integers. If the number has fewer sig-
nificant digits than the precision specified, leading zeros will be printed.
.
2 The number of digits after the decimal point in float.
3. The number of significant digits in g and G output .
Size
The size specification is a modifier for the conversion code. Used in combi -
nation with the conversion code, it specifies the type of the associated vari -
able, lor example, a long double (Lf ) . The size codes with their associated
conversion codes are explained in Table 7 - 4, “Flags, Sizes, and Conversion
Codes for printf Family.”
Conversion Codes
With the exception of string and pointer, this section discusses the input con -
version codes. I he pointer conversion code is discussed in Chapter 9 ; the
string conversion code is discussed in Chapter I 1 .
Systems.”
When the alternative format flag is used, the hexadecimal values are pre-
fixed with ox or OX and octal numbers are printed with a zero prefix.
Real ( f ) The real format code ( f ) is discussed in Chapter 2. If the precision is
0, the decimal point is printed only if the alternate llag ( // ) is also present.
Scientific notation (e , E, g, G, a, AJ These codes are the same as described for the scanf .
The e and E codes format the number using ( f ), precision may be used
to control the number of digits after the decimal point
. If no precision is
used. II the precision is 0, no digits aie used altei the
specified, six digits are
decimal point and the decimal point itself is shown only when the alternate
form flag ( # ) is used .
The g and G codes are also scientific notation. However, the scientific
notation is used only when the exponent is less than -4 or greater than the
specified precision . Otherwise, the real formatting rules are used . When the
value is Formatted in scientific notation , the uppercase G iormats the number
with an uppercase E .
The a and A codes generate signed hexadecimal fractional representa-
-
tions with a decimal power-of two exponent. The formatted result with A is
formatted as ±0Xh.hhhP +dd, where h represents hexadecimal digits and d
represents decimal digits.
double x = 1234.5678;
printf( "|% #.4 |
f |% #.4e||% #.4G
||% #.4A|" , X , x, x, x ) ;
Results:
|1234.5678| ||1235.||0X 1.34A4P+10|
1.2346e+03
1 2
1 x 16 + 3 x 16 3
(
+ 4 x 16 + A x 16 4
+ 4 x 16 ) x 2* °
F
Chapter 7 Text Input/Output 423
value
Output Examples
In this section we present several examples of print formatting. Although
some of them are rarely used , they do illustrate some of the finer points of
formatting. The sample output is shown in hold characters. You may want to
cover up the answer until you have answered the question yourself .
EXAMPLE 7- 14 Write the print statement that prints three columns of numbers. The first col -
umn contains a two-digit integer, the second column contains up to seven
digits, and the third column contains a float with four integral numbers and
three decimal places.
15 10 15.010
78 1234567 1234.123
EXAMPLE 7- 15 Write the print statement that prints the tax due as stored in a float named x.
The output is
The tax is: 233.12
EXAMPLE 7 - 16 Modify the previous example to add the words ‘ dollars this year after the
”
EXAMPLE 7- 17 Write the print statement that prints three integer variables to a file opened
as spOut. Each write is to append a newline character ( -» ) to the output.
100 200 300 * “
EXAMPLE 7- 1 8 Write the print statement that prints three floating-point values. The first is a
float with two places of precision; the second is a double , printed in scientific
notation (G); and the third is long double , printed using scientific notation
(E). Each value prints on a separate line.
3.14
200
4.408946E+00
EXAMPLE 7 - 1 9 Write the print statement that prints three integral values. The first is a short ,
printed in octal: the second is an int, printed using lowercase hexadecimal
digits; and the third is a long , printed using uppercase hexadecimal digits.
End the line with a newline character.
25547 7e44 7864CB
EXAMPLE 7 - 20 W rite the print statement that prints three real numbers in scientific notation
( e oi g ) as shown
below. Use the alternate format flag so that the value prints
with a decimal point even if it is zero.
3.1 7.893E 05 - 0.
Results:
1 2 3 4 5 6 7 8 9 10
Program 7- 3 Analysis Note how we open the file in this small program. To make it easier to read the code,
into two statements. This does
we siplit the file open and the test for successful open
not affect the program 's efficiency and makes it much easier to code and read the
statements.
I
Now look at the read in the while loop. We loop as long as the fscanf succeeds, as
indicated by a return value of 1 . When the end of file is reached, the while statement
will be false and the program terminates. There is one slight risk to this program: If a
read error is caused by a bad disk, we get the same results. In Chapter 1 3 we show
you how to detect this type of error.
Copy File
In Program 7-4, we copy a text file of integers to a new file .
20 {
21 printf( " Could not open input file\a\n " );
22 exit ( 101 );
23 > // if open fail
24
25 spOut = fopen( "P07 04.DAT" , "w" );
26 if (!spOut )
-
27 {
28 printf( "Could not open output file\a\n" );
29 exit ( 102 );
30 > // if open fail
31
32 while (( fscanf(spin , " %d " , &numln
33
)) == 1)
fprintf( spOut, " %d\n " , numln
);
34
35 closeResult = fclose( spOut);
continued
n1
Chapter 7 Text Input/Output 427
Results:
Running file copy
File copy complete
Program 7 4 Analysis Because this program has no display output, we start with a message that says we
*
will copy a file, and we end with a message that says the copy succeeded. Users
need to know that the program ran. If we don't print these messages, users will not
know if the program ran. In fact, it is a good idea to print start and end messages
with every program. This is a common standard in large production systems.
Now study the format string in statement 33 carefully. Note that it requires a new-
line after the data have been written. It is important to use whitespace between the data
in the file or it will not be readable. To verify that the data are correctly written, the file
should be read and printed by Program 7- 3 or a similar program .
Finally, note that we close the output file at the end of the program. While input
files don't need to be closed, you should always close output files and check to make
sure that they closed successfully. If the close fails, we print an error message. If it
doesn't, we print the successful copy message.
Append File
Now lets append some data to the end of a hie. After we wrote Program 7 - 5 ,
we ran it .
without a file Because append creates a file i ( it doesn t exist , the
’
first run created our file. Then we ran it a second time and verified that the
new data were appended to the original file .
PROGRAM 7- 5 Append Data to File
1 /* Copy a text file of integers.
2 Written by:
3 Date:
4 */
5 #include <stdio.h>
6 # include <stdlib.h>
7
8 int main ( void )
9 {
continuec
428 Section 7.4 Formatting Input/Output Functions
Results:
This program appends data to a file
Please enter first number: 1
Enter next number or <EOF>: 2
Enter next number or <E0F>: 3
Enter next number or <EOF>:Ad
File append complete
Student Grades
Programs 7 - 3 to 7 - 5 were all written in main . As our last example, let ’s write a
program that reads and writes a student grades file to be turned in at the end
of the term. The code is shown in Program 7-6.
con tinned
PROGRAM 7-6 Student Grades (continued )
84 } // getStu
85
Chapter 7 Text Input/Output 431
Results:
Begin student grading
End student grading
con t i n net
432 Section 7.5 Character Input/Output Functions
Input
0090 90 90 90
0089 88 90 89
0081 80 82 81
0079 79 79 79
0070 70 70 70
0069 69 69 69
0060 60 60 60
0059 59 59 59
Output
0090 90 A \ n
0089 89 B \ n
0 0 8 1 81 B \ n
0079 79 C \ n
0070 70 C \ n
0069 69 D \ n
0060 60 D \ n
0059 59 F \ n
Program 7- 6 Analysis This program has several points to study. Let's start at the top. We open and close the
files in main . Often, programmers write subfunctions for program initialization and
conclusion, but in this case we decided to write them in main . The processing loop
uses the return value from getStu to control the loop ( see statement 44). if data are
read, then the return value is true ( 1 ); if there is an error or all data have been read,
the return value is false (0). This design results in a very simple while loop.
Within the loop are three calls: one to read the student data (in the while limit test),
one to calculate the grade, and one to write the grades file. Study the parameters care-
fully. We have used a combination of data values and addresses. Note especially the file
parameters. In the function headers, the file parameters are coded as FILE*, which you
should recognize as a file address. We do not use an address operator when we pass
the files, however, because they are already addresses. (See statements 44 and 48 .)
Now study the results. The student ID is written as four digits with leading zeros
because it is really a code, not a number. Note how we created the test data to verify
that all of the boundaries in our multiway selection work correctly. To make it easy to
verify, we made the student ID the same as the average . Witb these clues to the
expected output, it only takes a quick review of the results to confirm that the program
ran successfully.
Terminal Any
Only Stream
C provides two parallel sets of functions , one for characters and one for
wide-characters. They are virtually identical except for the type. Because
wide-characters are not commonly used and because their functions operate
identically, we limit our discussion to the character type. The wide-character
functions are listed in Appendix F.
might
Note that the return type is an integer and not a character as you
expect. The return type is an integer because EOF is defined as an integer
files . I here is
( i n t ) in the standard definition stddef .h and other headei
( lag is not a character.
another reason for this: C guarantees that the EOF
, or
set it is using : ASCII , EBCDIC
This is true regardless of what character
control characters in ASCII you will find none
whatever. If you examine the
,
434 Section 7.5 Character Input/Output Functions
for end of file. Traditionally, EOF is defined as -1, but this is not prescribed
by ANSI/ISO. An individual implementation could therefore choose a differ -
ent value.
Note that we have discarded the return value. Although we may occa -
sionally find a use for it , it is almost universally discarded .
stored in text format with newlines only where input by the user.
4 */
5 # include <stdio.h>
6 int main (void )
7 { i
8 // Local Declarations
9 FILE* spText ;
10 int c;
11 int closeStatus;
12
13 // Statements
14 printf( "This program copies input to a file.Xn ");
15 printf( "When you are through , enter < EOF>.\ n\n " );
16
17 if (!(spText = fopen( "P07-07.DAT ” ,"w " )))
18 {
19 printf( " Error opening P07 07.DAT for writing " );
-
20 return ( 1 );
21 } // if open
22
23 while ((c = getchar( )) != EOF )
24 fputc(c , spText );
25
26 closeStatus = fclose(spText );
27 if ( closeStatus == EOF )
28 {
continued
Chapter 7 Text Input/Output 437
iff
PROGRAM 7-7 Create Text File ( continued )
29 printf( "Error closing file\a \n " );
30 return 100;
31 > // if
32
33 printf( " \n \nYour file is complete\n " );
34 return 0 ;
35 > // main
Results:
This program copies input to a file.
When you are through, enter <EOF>.
Program 7- 7 Analysis This simple program is the beginning of a text editor. The biggest element missing is
word wrap. Word wrap prevents a word from being split between two lines on a page.
Study the program carefully and note that we used integer for the character type
(c ) in statement 10. This is an important point whenever you are checking for end of
file, as we did in statement 23. Because EOF will not be correctly represented in a char -
acter, we must use integer. But, what about when we have a character ? C automatically
casts the integer to a character wherever it is necessary.
26 } // if open output
27
28 while ((c = fgetc( spl )) 1 = EOF )
29 fputc(c, sp2);
30
31 fclose(spl );
32 closeStatus = fclose( sp2 );
33 if ( closeStatus == EOF )
34 {
35 printf( "File close error.\a\n " );
36 return 201 ;
37 > // if close error
38 printf( "File successfully created \n" );
39 return 0;
40 > // main
Program 7-8 Analysis This program contains two style points that need comments. First, we have used
generic names for the files, spl and sp2. Since this program simply copies and cre-
ates text files, we cannot give the files names that reflect their data Better names
.
might be spin and spOut, but they are also generic.
Second, the program has two potential file open errors. We use different error
codes for those operating systems whose job control can distinguish between different
completion codes. We have also included the file names in the error messages so that
the user knows exactly which file had the problem .
Finally, a subtle point: Note that we have arranged the local declarations in order
of increasing complexity. First, we define the character and integer
types: then we
define the files. It is good practice to group the definitions by
type and in order of
increasing complexity.
36 fclose ( spl );
37 return 0;
38 > // main
Results:
Number of characters: 74
Number of lines: 2
Program 7- 9 Analysis Program 7- 9 is rather straightforward. The only real problem is in making sure that
the end of the
the last line is counted even if there is no newline. We can check this at
440 Section 7.5 Character Input/Output Functions
file by making sure that the last character we read, stored in preCh, was a newline.
If it isn't, we add 1 to the line count.
Once again, we have used the integer type for our input character. Everything
works correctly, even statement 31, which compares the integer type (preCh) to a
character literal.
continual
'
V
Results:
The number of words is: 15
Program 7- 10 Analysis The selection logic for this problem is similar to the previous program. We must deter -
mine when we are in a word and when we are between words. We are between
words when we start the program and whenever we are at whitespace. To keep track
of where we are, we use a flag, word. When word contains the letter I, we are in a
word; when it contains the letter 0, we are out of a word. We increment the counter
only at the beginning of a word, when we set the word flag toI.
Note that the problem handles multiple whitespace characters by simply setting the
word flag to 0. Note also how we use a preprocessor define statement to define
whitespace. This has no effect on the efficiency of the program, but it makes it easier
to read.
442 Section 7.6 Software Engineering
than a newline, the statement is true. It it reads a newline, then the state-
ment is false. The while statement will therefore read the input stream and
throw away all characters until it sees a newline. When it finds the newline, it
will throw it away too, but at that point, the loop stops. In other words, it
flushes the input stream to a newline character. This is exactly what we want
‘II '
to do when the user accidentally keys the wrong character when the program
expects a digit.
One more point . Note that we coded the null statement on its own line.
I his is to make it clear that we intended to code a null statement . It also
eliminates a warning messagefrom the compiler —good compilers look for
common errors like unintended semicolons.
Now, what ’s the easiest way to implement this handy statement ? Well, we
could simply code it everywhere you need it, but chances are that we would
make some mistakes that would have to be found and debugged. A better
solution is to use the preprocessor define declarative and code the statement
only once, as shown below.
Value Errors
In Chapter 6, we discussed some of the techniques for data validation. But
the subtleties of data validation can be quite complex. Consider another type
of human error, the partially correct input. In our program above, we
assumed that the user entered invalid data for the first digit of the number.
What if the error occurs on the second or third digit r Then the scanf function
is happy, for the time being anyway, and returns a 1,
indicating that it read
one number successfully. How do we guard against this ?
The best way is to echo the input to the user and ask for verification that
it is correct . Although this greatly slows down the input process , for critical
444 Section 7.6 Software Engineering
data it is necessary. The code for this situation is shown in Program 7- 12.
Here we present it as a complete function.
32 > // if
33 > while (!valid );
34 return unitsSold ;
35 } // getUnitsSold
Program 7- 1 2 Analysis This function merits some discussion. First, note the good
user communication
throughout the function. It begins with a complete prompt and provides clear error
messages whenever problems are detected.
We have implemented this logic with a do. . .while
statement, which always loops
at least once, since we know that there will
always be input. This is the standard loop
for validating user input. Within the loop are two
different validation steps. The first
n
l; '
tests for totally invalid input, the second asks the user to verify the input. If either test
indicates a problem, the input is flushed. Note that the user messages are different
j
depending on the circumstances.
The function cannot end unless the valid flag is true. The if statement in the loop
will set it true if the user replies positively. Otherwise, it is set false and the input is
flushed. This code again demonstrates two principles of good human engineering. The N
if statement is coded in a positive manner, and the expected event, good input, is
checked first.
Many other things can go wrong when we are reading a file, but these two exam-
ples cover most of them.
Data Terminology
Computer specialists who deal with data use a set of specific terms to
describe their data. These terms deal with data that are stored in files. What
we call a variable in our program , they call a field or a data item . A field is
the smallest named unit of data in a file . If we were working with data about
the ten western states in the continental United States, we would have data
like Name, Capital , Number, Square Miles , Population , and Number of
Counties. The first two fields arc strings ( delimited arrays of characters that
we study in Chapter 1 1 ) and the last four are integers.
These six fields grouped together make up a state record . A record is a
collection of related data , in this case state data , treated as a unit . Each
record has a key, one or more fields that uniquely identify the record . In our
state s record , the key could be the name. Names normally do not make good
keys , because they are not guaranteed to be unique. A better choice is the
state number, which represents the order in which the states entered the
union . This field is guaranteed to be unique.
With text files we cannot create a record . We must wait for binary files
and structures to do that . But we can simulate a record by grouping these
data on the same line, with each field separated from the next by whitespace.
The data for the ten western states are shown in Table / -6.
State Capital No . .
Sq Miles Population No .
Counties
47 121,335 1,819,046 33
New Mexico Santa Fe
33 96,184 3,421,399 36
Oregon Salem
Olympia 42 66,511 5,894,121 39
Washington
Cheyenne 44 96,989 493,782 23
Wyoming
you already have an existing file with the same name , it could he deleted .
4 . 11 you want to write at the end of an existing text file, open the file for
appending, not for writing.
5. C output is right justified when using printf and fprintf functions. If you
—
want your data to he left justified , use the left - justify flag ( ) explicitly.
6. It is a compile error to misplace the file pointer in the parameter list . The
file pointer is coded last in all file functions except fscanf and fprintf , in
which it is coded first.
7. An error that may cause your program run to terminate is to use a format
code that does not match the variable type being read .
.
8 An error that will give invalid output is to use a format code that does not
match the variable being printed .
.
9 Several common errors are created when a file operation is included in a
selection statement . Three are shown below.
a . The following code does not properly test to make sure that the file
was opened correctly:
h. The following code does not store the character read in ch . It stores
the logical result of the compare in ch .
.
e In a similar way, the following code is invalid :
// logic error
while ( ioResult = scanf(...) != EOF )
// good code
while (( ioResult = scanf(...)) != EOF )
as a char-
d . It is a logic error to define ioResult in the above statement
—
acter because EOF ( 1 ) cannot he stored in a character.
448 Section 7.9 Summary
7.9 Summary
A file is a collection of related data treated as a unit .
Data in a text file are stored as human- readable characters. The data in a
text file arc usually divided into lines separated by a newline character.
A stream is a sequence of elements in time.
A stream is associated with a file in C.
J I here are three standard file streams in C: standard input , standard out-
put , and standard error.
I he standard input is associated with the keyboard . The standard output
and the standard error are associated with the monitor.
-J I he standard file streams can he accessed respectively using stdin , stclout ,
and stderr. I hese are pointers (addresses) to tables (structures ) containing
the information about the standard streams.
To manipulate and access files, there are different
types of input/output
functions.
nil
J I he open /close functions, /open and /close , are used to open and close the
association between external files and internal streams.
J A (Tie in C can be any of the three basic modes: reading, writing, and
appending.
When a file is opened in reading mode, the file marker is positioned at the
beginning of the existing file . The file is ready to be read .
When a file is opened for writing , the file marker is positioned at the
beginning of a newly created empty file, before the end -of -file character.
The file is ready to be written .
When a file is opened for appending , the marker is positioned at the end of
the existing file ( or at the beginning of a newly created file ) , before the
end - of -file marker. The file is then ready to be written .
Formatted input/output functions allow us to read data from and write
data to files character by character while formatting them to the desired
data types such as char , int , and float . The functions scan / and /scan/ are
used for reading. The functions print/ and / print/ are used for w riting.
Character input/output functions allow' us to read or write files character
by character. The functions fgetcy getc , and getchar can be used for read -
ing. The functions fputcy putc , and pntchar can be used lor writing.
C99 added a set of wide character functions. They are fgetwc , getuc ,
getwchar , fputwc , putuc , and putivchar .
When we are reading data from files , we should validate the data.
5. One of the problems with testing files is that some errors, such as physi-
cally bad media , cannot be created so that they can be tested .
a . True
.
b False
6. Which of the following is considered auxiliary storage?
a. disk
b. random access memory ( RAM )
.
c read only memory ( ROM )
d . tape
.
e both disk and tape
.
7 Which of the following is not a standard file stream ?
a . stdin
b. stderr
c. stdfile
d . stdont
8. The C library that contains the prototype statements for file operations is
a. ftle.h
h . proto , h
c. stdfile .h
d . stdio .h
e. stdlib . h
9. II a file is opened in the r mode , C
a. opens the file for reading and sets the file marker at the beginning,
h . opens the file for reading and sets the file marker at the end .
c . opens the file lor writing and sets the file marker at the beginning.
d . opens the file lor writing and sets the file marker at the end .
e. returns a null file address indicating an error if the file exists .
10. II a file is not opened successfully, C
a. aborts the program .
b. continues processing with the next statement after the open .
c . changes the file mode to write and opens a new file.
d . displays a message and waits for an answer.
e. returns a file address.
1 1 . The specifies the type of data that are being format -
ted in formatted input/output .
a . conversion code
b. data code
c. field code
d . format code
e. pcode
Chapter 7 Text Input/Output 451
rn
12. I he two input functions that read text data and convert the data to the
types specified by a format string are
a. frecid and readd . read and readf
h . fscanf and fprintfe. scanf and printf
c. fscanf and scanf
13. Which of the following statements about the formatted input function
( scanf ) is false?
a . scanj implicitly casts data to match the conversion code and its corre -
sponding address parameter.
b. scanf reads data from the keyboard and formats it according to conver-
sion specifications.
c . scanf returns an integer containing the number of variables success-
fully formatted for EOF.
d . scanf stops formatting data when an inappropriate character lor the
type being formatted is encountered .
e. To function properly, scanj requires at least one address.
14. The function reads the next character from the stan -
dard input stream and returns its value.
a. fgetc
b. getc
.
c getchar
.
d readchar
e. ungetc
1 5 . When scanf finds invalid data in the input stream , it
.
a flushes the input to a newline.
.
b prints an error message and terminates the program.
c. prints an error message and terminates the scanf function .
d . skips the data and formats the next conversion specification , if any.
e. terminates the scanf function and leaves the invalid character in the
input stream .
Exercises
16. Given the following declaration :
int il ?
int i2 ;
float fl ;
char cl ;
char c2 ;
char c3;
452 Section 7.10 Practice Sets
14 23 76 CD
int il ;
int i2 ;
float fl ;
char cl ;
char c2;
14.2 67 67.9
what would be the value ol i l , i 2 , fl, cl, and c 2 after the following
statement?
int il ;
int i2 ;
int i3;
char cl ;
char c2 ;
char c3 ;
c l 45 d l 23 34.7
' If
Chapter 7 Text Input/ Output 453
int 11 = 123;
int 12 = -234;
int 13 = -7;
float fl = 23.5; m
float f2 = 12.09 ;
float f 3 = 98.34;
char cl = 65 ;
char c2 = '\n' ;
char c3 = 'E';
printf( " %06d , %06d , %06d %c" ,
il , i2 , i3, c2);
- -
printf("% 6d , % % , \" , \\t, % 06d " , i2, il );
printf( " %c %d ", cl, c2 );
printf( "%c %c % # x", cl + 32, c3 + 3 , c 2 + 5 );
20. W hat output is produced Irom the following program ? Include in your
answer the contents of any files created .
Problems
For the problems and projects in this chapter, you need to create a test file
using your text editor. End each line with a newline
character; do not write in
paragraph format. We suggest the Gettysburg Address as a suitable subject
for these files:
Four score and seven years ago our fathers brought forth on this continent
a new nation , conceived in liberty, and dedicated to the proposition that all
continued
454 Section 7.10 Practice Sets
men are created equal. Now we are engaged in a great civil war, testing
whether that nation , or any nation so conceived and so dedicated , can long
endure . We are met on a great battlefield of that war. We have come to
dedicate a portion of that field as a final resting-place for those who here
gave their lives that that nation might live. It is altogether fitting and
proper that we should do this. But in a larger sense, we cannot dedicate,
we cannot consecrate, we cannot hallow, this ground . The brave men , liv-
ing and dead , who struggled here have consecrated it far above our poor
power to add or detract . The world will little note, nor long remember what
we say here, but it can never forget what they did here . It is for us the liv-
ing , rather to be dedicated here to the unfinished work which they who
fought here have thus far so nobly advanced . It is rather for us, to he here
dedicated to the great task remaining before us, that from these honored
dead we take increased devotion to that cause for which they gave the last
full measure of devotion ; that we here highly resolve that these dead shall
not have died in vain ; that this nation , under God , shall have a new birth of
freedom , and that government of the people, by the people, for the people,
shall not perish from the earth .
21. Write a function that appends one file at the end ol the other.
.
22 Write a function that appends a file to itself .
23. Write a function that accepts a file of varying- length lines and changes it
to a formatted file with 60 characters in each line.
24. Write a function that calculates the average number of characters per
line in a file.
25 . Write a function that deletes the last line of any file.
26. Write a I unction that deletes the blank lines in a file. A blank line is a line
with only one single character in it : newline .
.
27 Write a program that prints itself .
28. Write a program that copies one text file to another and inserts blank
lines between paragraphs in the new' file. Paragraphs are identified by a
newline character.
29. W rite a program to copy only lines beginning with a user- specified
character.
.
30 Write a program to parse words onto separate lines ; that is , locate and
write each word to its own line . Words are defined as one or more charac-
ters separated by whitespace .
31 . W'hen scanf encounters an error, the invalid data are left in the input
stream , sometimes making it impossible to continue . Write a function
that reads three pieces of numeric data . If an error is detected ( return
value not EOF but less than 3) , flush the erroneous data . ( See "Softw'are
Engineering" for details on how to write a flush statement . ) Then test the
Chapter 7 Text Input/Output 455
Projects
43. Write a program that will read a text file and count the number of alpha-
betic characters (isalpha), digits (isdigit ), punctuation characters
( ispunct ), and whitespace characters ( isspace ) in the file. At the end of
the file, the program is to display an appropriate report. (The classifying
functions are covered in Chapter 5.)
44. Write a text analyzer program that reads any text file. The program prints
a menu that gives the user the options of counting
lines, words, charac -
more words ending in a period ) , or all of the
ters , or sentences ( one or
above. Provide separate
a function for each option . At the end of the
analysis, w rite an appropriate report.
456 Section 7.10 Practice Sets
45. Write a menu -driven text utility program. The program will have the fol-
lowing capabilities:
a. Copy a user- named hie to a new file .
b Append a user- named file to another user- named file.
.
c. Change the hie format to be double spaced.
-
d Remove all blank lines (change a double-spaced file to single-spaced ).
.
e. Display the contents of the file as a series of 60 character lines with no
words split between lines.
.
46 Using an editor, create an inventory file using the data shown in Table 7-7
( do not include the column captions, just the data ) .
W rite a program to read the employee file and create a payroll register.
The register will contain the following data :
a . Employee number ( print left - justified )
b. Department
c. Pay rate
.
d Exempt IT:
e. I lours worked
.
f Base pay ( pay rate * hours worked )
1
Arrays
In Chapter 4 we discussed the first derived data type , the function. In this
chapter we discuss the second derived type, the array. Figure 8- 1 recaps the
six derived types and the chapters in which they are covered .
Derived
Types
Function
Type
Chapter 4
Array
Type i Pointer
Type
Chapter 9
Structure
Type
Chapter 12
Union
Type
Chapter 12
Enumerated
Type
Chapter 12
i
FIGURE 8 - 1 Derived Types
Objectives
To understand the basic concepts and uses of arrays
To be able to define C arrays
To be able to pass arrays and array elements to functions
To understand the classical approaches to sorting arrays: selection, bubble,
and insertion sorting
To write programs that sort data using the three classical algorithms
To be able to analyze the efficiency of a sort algorithm
To understand the two classical search algorithms: sequential and binary
To write programs that search arrays
To be able to design test cases for sorting and searching algorithms
To be able to analyze the efficiency of searching algorithms
it
459
460 Section 8.1 Concepts
8.1 Concepts
Imagine we have a problem that requires us to read, process, and print
10 integers. We must also keep the integers in memory for the duration of the
program . To begin , we can declare and define 10 variables, each with a differ-
ent name, as shown in Figure 8- 2 .
scoreO score5
score1 score6
score2 score7
score3 score8
score 4 score9
scoreso
In a similar fashion , we refer to the second score as scores and the third
\
-
score as scores2 Continuing the series , the last score would be scores We
can geneialize this concept in the following fashion where the subscripts^
indicate the ordinal number of the element counting from the beginning of
the array:
scores0 scoresj,
/ scores n -1
nr
/
(
/ ReadX
/
START
scoresO
^
X
X
/ scoresO
/
Print X
X
Chapter 8 Arrays 461
X
I
//
Read Print
/ scores 1 X scores 1 X
/ Read X / Print X
/ s c o r e s2 X / scores2 X
/ Read X / Print X
/ scores3 X / scores3 X
/ Read X Process / Print X
/ scores 4 X 10 scores
/ scores 4 X
/ Read X / Print //
/ scores5 g / scores5 X
~
/ i /
g/
Read Print
/ scores6 X ! scores6
~
/ t
/
/
/
Read
scores7
Read
^
X
/
/
/
Print
scores7
Print
X
~
t
/ scoresS X / scores8 X
/
/
/ Read
scores9
^
X /
(
Print
scores9
STOP
/
X
^
FIGURE 8 - 3 Process 10 variables
What we have seen is that the elements of the array are individually
addressed through their subscripts, a concept shown graphically in Figure 8 - 4.
The array as a whole has a name, scores, hut each member can he accessed
individually using its subscript.
The advantages of the array would he limited if we didn’t also have pro-
gramming constructs that would allow us to process the data more conve-
niently. Fortunately, there is a powerful set of programming constructs—
loops— that makes array processing easy.
We can use loops to read and write the elements in an array; to add, sub -
tract , multiply, and divide the elements; and even for more complex process
-
ing such as calculating averages, searching, or sorting. Now it does not
to he processed,
matter if there are I , 10, 100, 1,000, or 10,000 elements
because loops make it easy to handle them all.
One question remains: How can we write an instruction so that one
time
to another
it refers to the first element of an array and the next time it refers
element? The answer is really quite simple : We simply borrow from the sub -
, howcvcr , we
script concept we have been using. Rather than using
subscripts
will place the subscript value in brackets. This format is known as indexing .
Using the index notation, we would refer to scoresg as
scores [ 0 ]
462 Section 8.1 Concepts
scores0 scores[0]
scores 1 scores[1 ]
scores2 scores[2]
scores3 scores[ 3 ]
scores4 scores[ 4 ]
scores 5 scores[ 5]
scores6 scores[ 6]
scores7 scores[ 7 ]
scores8 scores[8]
scores 9 scores[9]
scores scores
(a) Subscript Format (b) Index Format
scores [ i ]
i++
Start
i = 0
o
Process /
©
i = 0
i < 10 10 scores
r i < 10
/
/ Read
~
scores [ i ] m
k
o / scores
/
Write
/
[ i ]M
o Stop
ui
n
Chapter 8 Arrays 463
Indexes
scores [ 0]
scores [1]
scores [2]
scores [3]
C provides for two different array types, fixed-length array and variable -
length array. In a fixed -length array, the size of the array is known when the
program is written. In a variable -length array, inroduced in C 99, the size ol
the array is not known until the program is run.
int scores [ 10 ] ;
[0] [1 ] [ 2] [3] [4] [5] [6] [7] [8] [ 9]
type of each scores
. element .
char name [ 10 ] ;
[0] [1 ] [2 ] [3] [4] [5] [6] [ 7] [8] [9 ]
name of name
the array
The declaration format lor a variable- length array is the same as for a
fixed-length array except that the array size is a variable. When the program is
executed, the array size is determined and the array is defined. Once defined,
its size cannot be changed. Following standard C syntax rules, the array size
must he declared and initialized before it is used in the variable - length array
definition.
scores[ 0 ]
for ( i = 0 ; i < 1 0 ; i ++ )
process ( scores[i]);
T
Chapter 8 Arrays 465
array’s name, therefore, we are actually referring to the first byte of the array.
1 he index represents an offset from the beginning of the array to the element
.
being referenced With these two pieces of data, C can calculate the address
ol any element in the array using the following simple formula:
Initialization
Just as with variables, initialization of the elements in a fixed-length array can
he done when it is defined. Variable-length arrays cannot be initialized when
they are defined.
For each element in the array, we provide a value. The only difference is
that the values must be enclosed in braces and, if there is more than one,
separated by commas. It is a compile error to specify more values than there
are elements in the array.
Figure 8 - 8 contains four examples of array initialization. The first exam -
ple (Figure 8- 8( a)) is a simple array declaration of five integers and is typical
initial -
of the way array initialization is coded. When the array is completely
ized, the programmer does not need to specify the size of the array . This case
3 1 7|0|0|
0
int lotsOfNumbers [1000]
r-r
0 0
—.
= {0};
Inputting Values
Another way to fill the array is to read the values from the keyboard or a file.
I his method of inputting values can be done using a loop . When the array is
to be completely filled , the most appropriate loop is the for loop because the
number of elements is fixed and known .
Assigning Values
We can assign values to individual elements using the assignment operator.
\ n> alue that reduces to the proper type can he assigned to an individual
'
array element. A simple assignment statement for scores is seen below.
scores [ 4 ] = 23;
1
Chapter 8 Arrays 467
On the other hand, we cannot assign one array to another array, even if
they match fully in type and size. We have to copy arrays at the individual ele-
ment level . For example, to copy an array of 25 integers to a second array of
25 integers, we could use a loop, as shown below.
II the values ol an array follow a pattern, we can use a loop to assign val -
ues. For example, the following loop assigns a value that is twice the index
number to array scores:
For another example, the following code assigns the odd numbers 1
through I 7 to the elements of an array named value:
Exchanging Values
A common application is to exchange the contents of two elements. We see
this operation later in the chapter when we talk about sorting arrays. When
we exchange variables, we swap the values of elements without knowing
what ’s in them.
For example, imagine we want to swap numbers[ 3] and numbers [ 1 ] in
Figure 8 -8. A common beginner’s mistake would be simply to assign
each ele-
ment to the other, as shown below.
numbers [ 1 ]; // Error
numbers [ 3 ] =
numbers [ 1 ] = numbers [ 3 ];
code care-
Although this code looks as if it will do the job, if we trace the
. is moved to numbers [ 3 ],
fully, we find that it does only half the job numbers !1 ]
both elements have the same
but the second half isn’t done. The result is that
value. Figure 8- 9 traces the steps.
468 Section 8.2 Using Arrays in C
'
1
After
temp = numbers[3 ];
numbers [3 ] = numbers [ 1 ];
numbers [ 1 ] = temp ;
[0]
f
11 ] !2] !31 [ 4] l
numbers !1 ] = temp ; I 12 I I 45
3 24
After
7
1 temp
24
Printing Values
Another common application is printing the contents of an array. This is eas -
ily done with a for loop, as shown below.
In this example, all the data are printed on one line. After the for loop
completes, a final printf statement advances to the next line. But what if we
had 100 values to be printed? In that case, we couldn’t put them all on one
line. Given a relatively small number width, however, we could put 10 on a
line. We would then need 10 lines to print all the data. This rather common
situation is easily handled by adding a counter to track the number of ele -
ments we have printed on one line. This logic is shown in Program 8 - 1 .
In this case, numbers [ 4 ] has a higher precedence ( 16) than the addition
operator ( 12 ) , so it is evaluated first . The result is then
numbers[3] = 45 + 15;
After this statement has been executed , numbers[3] has been changed from
24 to 60.
\ \ hen dealing with array processing, be very careful at the beginning and
end of the array. A careful examination of the previous code discloses that we
erroneously started at 1 instead of 0. So we fix it as shown below, only to find
that it still doesn ’t work!
The moral of this example is to examine our logic. If we made one mis -
take , we may well have made two. Although we corrected the error lor initial -
ization ( the beginning of the array ) , there is still an error at the other end . If
you can ’t see it , check the original code in “ Inputting Values. ”
r -
.
1
' V'l
Results:
Element Square
0 0
1 1
2 4
3 9
4 16
1
Results:
You may enter up to 50 integers:
How many would you like to enter? 12
Program 8 - 3 Analysis First, note how we validate the number of integers to be read. If the user requests
more than 50, we simply set readNum to 50 . This ensures that we will not run off the
end of the array.
This is an interesting loop because we start at the end and work to the front. While
many arrays are processed from the beginning, it is often necessary to process from the
end. You will see more in the sorting functions later in the chapter.
Finally, note that we incorporate the logic from Program 8 - 1 to print ten numbers
per line. Had we made Program 8 - 1 a function, we could have reused the code rather
than repeating it.
Note how only one element is passed by using the indexed expression ,
ary [ 3 ] .
Since the value of this expression is a single integer, it matches the
formal parameter type in fun . As far as fun is concerned , it doesn 't know or
care that the value it is working with came from an array.
Passing Addresses
In C hapter 4 , we saw that we can use two - way communication by passing an
address. We can pass the address ol an individual element in an array just like
we can pass the address of any variable.
To pass an array element’s address , we prefix the address operator to the
element’s indexed reference . Thus, to pass the address of ary [ 3 ] we use the
code shown in the following example.
& ary [ 3 ]
'1
Chopter 8 Arroys 475
fun ( ary );
process x
.
void fun ( int fAry[ * ])
} // fun
> // fun
Fixed-length Arrays
To pass the whole array, we simply use the array name as the actual parame-
ter. In the called function , we declare that the corresponding formal parame-
ter is an cirra ) We do not need to specify the number of elements in a fixed-
length array. Since the array is actually defined elsewhere, all that is impor-
tant is that the compiler knows it s an array.
The function declaration for an array can declare the array in two ways.
First , it can use an array declaration with an empty set of brackets in the
parameter list. Second , it can specify that the array parameter is a pointer. We
prefer the array declaration over the pointer declaration because it is clearer.
Roth are shown in the following example, which declares arrays of integers .
// Function Declarations
void fun (int ary[], ...)/*
void fun (int* ary , ...);
i
When the called function receives a variable- length array, we must declare
and define it as variable length . In a function declaration , we declare an array
as variable using an asterisk for the array size or by using a variable for the
array size . In the function definition , we must use the variable name, and fol -
lowing standard syntax rules, it must be defined before the array. The follow-
ing example demonstrates these points.
// Function Declaration
float calcArrayAvg (int size, float avgArray [ * ]);
// Function Definition
float calcArrayAvg ( int size, float avgArray [size])
{
I
} // calcArrayAvg
continued
H1
Chapter 8 Arrays 479
Results
How many numbers do you want to average? 5
Enter number 1: 3
Enter number 2: 6
Enter number 3: 9
continued
480 Section 8.3 Inter-function Communication
Program 8 - 5 Analysis There are two points to study in this program . First, in main , we use a variable- length
array. This requires that the array size be known before the array is declared. We
accomplish this by using a block within main. The array size is declared in main' s
local declarations; the array itself is declared in the block local declarations.
Second, note how the variable array is declared in the function definition. We can
do it by just declaring it as a variable array with an asterisk in the array brackets, or
we can put the variable size in the brackets. Using size is better documentation
because it declares our intent. When we use size, however, it must be declared
before the array, in this case as the first parameter.
Frequency Arrays
Two common statistical applications that use arrays are frequency distribu -
tions and histograms . A frequency array shows the number of elements with
an identical value found in a series of numbers. For example , suppose we
have taken a sample of 100 values between 0 and 19 . We want to know how
many of the values are 0 , how many are 1 , how many are 2 , and so forth up
through 19.
We can read these numbers into an array called numbers. Then we create
an array of 20 elements that will show the frequency of each
number in the
series. This design is shown in figure 8 - 14 .
do we write
With the data structure shown in Figure 8 - 14 in mind , how
there exactly 100 elements , we can
the application ? Since we know that arc
examine each value in the array . But how can we relate the
use a for loop to
value in numbers to a location in the frequency " .
to an index and
One way to do it is to assign the value from the data array
then use the index to access the frequency array. This technique is
shown below'.
f = numbers[i];
frequency [ f ]++ ;
exam pie. The value of numbers [ i ] is determined first , and then that value is
used to index into frequency.
numbers [1 ] frequency [ 1 ]
This value shows
how many Os are
numbers [2]
numbers [3]
frequency [ 2] in numbers
-
numbers [ 4]
frequency[ 18]
frequency[19 ]
requency
This value shows
numbers[98] how many 19s are
v. in numbers
numbers[99] ^
numbers
Histograms
A histogram is a pictorial representation of a frequency array. Instead of
printing the values of the elements to show the frequency of each number, we
print a histogram in the form of a bar chart . For example, Figure 8- 15 is a his *
togram lor a set of numbers in the range 0... 19. In this example, asterisks ( *
are used to build the bar. Each asterisk represents one occurrence of the '
data value .
four 1s
0 0
1 4 •* » •
2 7 seven 3s
3 7
18 2 •• zero 19 s
19 0
L
Chapter 8 Arrays 483
'• n
Let’s write a program that builds a frequency array for data values in the
range 0 .. . 19 and then prints their histogram . The data are read from a file. To
provide flexibility, the getData function may only partially fill the array. The
function that loads it also guards against too much data. The design for the
program is shown in Figure 8- 16.
Frequency
Histogram
make make
getData printData Frequency Histogram
continue
It
I 1
1 2 3 4 5 6 7 8 7 10
2 12 13 13 15 16 17 18 17 7
3 4 6 8 10 2 4 6 8 10
4 3 5 7 1 3 7 7 11 13
5 10 11 12 13 16 18 11 12 7
6 1 2 2 3 3 3 4 4 4
7 7 8 7 6 5 4 1 2 2
8 11 11 13 13 13 17 17 7 7
continued
in
Chapter 8 Arrays 487
0 0
1 4 * ***
2 7 *******
3 7 *******
4 8 * * * * ** * *
5 4 ****
6 5 *****
7 12 * * * ** * * * * * * *
8 5 *****
9 0
10 4 ****
11 5 *****
12 3 ***
13 8 ********
14 0
15 3 ***
16 2 **
17 8 ******
18 2 **
19 0
Program 8 - 7 Analysis Remember our discussion of what happens when an index gets out of range? What if
one of the numbers in our data is greater than 19? We would destroy some other
part of our program ! To protect against this possibility, we test each data value to
make sure that it is within the indexing range of frequency . If it is not, we display
an error message and read the next number.
Similarly, we guard against too much data . If the file contains more than 100 valid
numbers, then we stop reading. After the read loop, we test to make sure we are pro-
cessing all of the data. If there were too many numbers, we print an error message
.
Finally, note that we print the data to make it easy to verify the results.
randNos[0] 8 haveRand[0] 0
O means random
randNos[1] 3 haveRand[ 1] 1 not generated
randNos[2] 5 haveRand[2 ] 0
randNos[3] 1 haveRand[3] 1
1 means random
7 haveRand[ 4 ] 0 (v
^^
randNos[4] generated
randNos[5] haveRand[ 5] 1
randNos[6] haveRand[6] 0
randNos[7] haveRand[7] 1
randNos[8] haveRand[8] 1
randNos[9] haveRand[9] 0
randNos haveRand
As you study Figure 8- 17, note that only the first five random numbers
have been placed in the permutation. For each random number in the ran-
dom number array, its corresponding location in the have- random array is set
to 1 . Those locations representing numbers that have not yet been generated
are still set to 0. The implementation of the design is shown in Program 8- 81.
continued
Results:
Begin Random Permutation Generation
18 13 15 11 7 10 19 12 6 9
4 0 5 3 17 14 2 16 1 8
8.5 Sorting
One of the most common applications in computer science is sorting the
process through which data arc arranged according to their values. We are
—
surrounded by data. II the data are not ordered , we would spend hours trying
to find a single piece of information . Imagine the difficulty of finding some -
one’s telephone number in a telephone book that is not ordered in name
sequence!
In this chapter we introduce three sorting algorithms: the selection sort ,
bubble sort , and insertion sort. In each section we will first introduce the
basic concept, then use the idea in an example, and finally develop the code
for the algorithm .
One programming concept common to the sorting algorithms we discuss
in this section is the swapping of data between two elements in a list . You
might want to review our discussion in "Exchanging Values ” in Section 8.2.
m
Chapter 8 Arrays 491 1
Selection Sort
In the selection sort, the list is divided into two sublists, sorted and
.
unsorted, which are divided by an imaginary wall We find the smallest ele -
ment from the unsorted sublist and swap it with the element at the beginning
.
of the unsorted sublist After each selection and swapping, the wall between
the two sublists moves one element ahead, increasing the number of sorted
elements and decreasing the number of unsorted ones Each time we move .
one element from the unsorted sublist to the sorted sublist, we say that we
have completed a .
sort pass If we have a list of n elements, we need n - 1
passes to completely rearrange the data. The selection sort is graphically pre -
sented in Figure 8- 18.
Swap y
m smallest (a[k]...a[n - 1])
0 n-1
4
Sorted Unsorted
Figure 8 - 19 traces a set of six integers as we sort them. It shows how the
wall between the sorted and unsorted sublists moves in each pass. As you
study the figure, you will see that the array is sorted after five passes, which is
one fewer than the number of elements in the array. This means
that our sort
loop has one less iteration than the number of elements in the array.
BJ .
3 n
23 78 45 8 32 56 Original list 8 23 32 78 45 56 After pass 3
c4
4 4
4 Sorted Unsorted
Unsorted
PLi 8
an
23 32 45 78 56 After pass 4
8 78 45 23 32 56 After pass 1
4 4 4 4 »
4
Unsorted Sorted
8 23 32 45 56 78 After pass 5
8 23 45 78 32 56 After pass 2
4 4*
Sorted
4
^ ^ Unsorted
/
(Selectior
^ ^\
cur = 0
ort
/
0
walk = \
/ cur X
*
/waik EHLL! \
\ ++ cur < last M \ ++ walk <= last m
smlst = cur
/ |ist[ walk ] \
0
Exchange
smlst = walk
continuei
1n
l Ti
If |
discussed in
Program 8 -9 Analysis In this algorithm we see two elements that are common to all three sorts e the
this section. First, each algorithm makes use of an inner loop either to determin
proper location of the next elemen t or to identify and exchang e two element s . Each
iteration of the inner loop is one sort pass.
Second, each time we need to move data, we must use a temporary storage area.
This technique is found in every sort algorithm except those that use two sorting areas.
In the selection sort, the temporary area is used to exchange the two element
s.
Bubble Sort
unsorted.
In the bubble sort, the list is divided into two sublists, sorted
and
the unsorted sublist and moved to the
I he smallest element is bubbled from
the sorted list , the wall
sorted sublist. After moving the smalles t element to
Bubble up
Sorted Unsorted
8 32 78 45 56 After pass 2
* Unsorted
I he bubble sort was originally written to “bubble up " the highest element
in the list . From an efficiency point ol view, it makes no difference whether
the high element is bubbled or the low element is bubbled. From a consis-
tency point of view, however, it makes comparisons between the sorts easier il
all three of them work in the same manner. For that reason, we have chosen
to bubble the lowest key in each pass.
Like the selection sort, the bubble sort is quite simple. In each pass
through the data, controlled by the outer for loop, the lowest element is
bubbled to the beginning ol the unsorted segment of the array. The bubbling
process is done in the inner loop.
1 ach time it is executed, the inner for loop makes one pass through the
data. Whenever it finds two elements out ol sequence, it exchanges them. It
then continues with the next element.1bis process allows the smallest element
to be bubbled to the beginning of the
array, while at the same time adjacent ele -
ments along the way are rearranged. The
design is shown in Figure 8 - 23.
The bubble sort is shown in Program 8 - 10.
pi
Chapter 8 Arrays 495
1
( Bubble Sort
^\ /
0 \X
//cur
cur = 0 walk = last
X wwalk
Vf + cur < last M V~ “ walk > cur M
0 list[walk ]
,< list [ walk-1
o
Exchange
( Return
^ cur & smlst
?
0
FIGURE 8 - 23 Bubble Sort Design
Program 8 - 1 0 Analysis If the data being sorted are already in sequence, the inner loop still goes through the
array element by element. One common modification is to stop the sort if there are
no exchanges in the inner loop . This change requires the addition of a " sorted" flag.
We leave this modification to the practice sets at the end of the chapter.
Insertion Sort
The insertion sort algorithm is one of the most common sorting techniques
used by card players. As they pick up each card , they insert it into the proper
sequence in their hand.2
In the insertion sort , the list is divided into two parts: sorted and
unsorted. In each pass, the first element of the unsorted sublist is picked up
and transferred into the sorted sublist by inserting it at the appropriate place.
—
If we have a list of n elements, it will take at most n 1 passes to sort the data
( Figure 8- 24 ).
5
:
0 n-1
5r 4k
* Sorted
L-
Unsorted
Figure 8- 2 S traces the insertion sort through our list of six numbers,
l ach pass moves the wall as an element is removed from the unsorted sublist
and inserted into the sorted sublists.
•
n
Chapter 8 Arrays 497
:
~
[ 23 E
78 45 8 32 56
Original
List
32 56
After
pass 3
Unsorted Sorted
After
45 8 32 56
After
pass 1 8 | 23 32 45 78
I pass 4
Unsorted Sorted
3 After After
23 45 8 32 56 8 23 32 45 56 78
3 pass 2 pass 5
The design of the insertion sort follows the same pattern we saw in both
the selection sort and the bubble sort ; each iteration of the outer loop is a
sort pass. The inner loop inserts the first element from the unsorted list into
its proper position relative to the rest of the data in the sorted list . I he design
is shown in Figure 8 - 26.
Insertion Sort
\ /
o
walk = \
cur = 1
V-
cur <= last' t M
M
I K\ _ cur ' 1
walk >= 0 & &#
JlocatedJ
\
located = false
temp <
list [walk]
list[ walk + 1 ]
= temp temp = list(cur) list [walk + 1 ]
= list[ walk ]
o
located = true
walk - -
o ?
I
Return
J
o
FIGURE 8 - 26 Insertion Sort Design
Program 811 Analysis Note how the exchange is worked in this sort. Before the inner loop starts, we put the
data from the current element into the hold area (temp). This is the first step in the
exchange . The loop then determines the correct position to place the element by start -
ing with the largest element in the sorted list and working toward the beginning. As it
searches, it spreads the sorted portion of the list by shifting each element one position
higher in the list. When it locates the correct position, therefore, the data have
already been moved right one position and the current location is ''empty"; the sort
therefore simply places the saved element in its proper location, completing the
exchange.
Chapter 8 Arrays 499 1
Testing Sorts
Now that we have written three sorts, howr do we test them? The answer is
that we write a simple test driver. The same test driver can he used for all three
sorts. The only changes necessary are in the include statement, the prototype
statement, and the function call ( see highlighted statements). Program 8- 1 2
is set up to test the insertion sort .
Results:
19 98 5 77 39 59 61
Unsorted: 89 72 3 15 21 57 61 44
57 59 61 61 72 77 89 98
Sorted : 3 5 15 19 21 39 44
500 Section 8.5 Sorting
Sorts Compared
It is interesting to compare the three sorting algorithms. All three use two
nes ted loops. The outer loop is the same in all three; it moves the wall one
element toward the beginning of the array. The processing in the inner loop,
however, changes with each sort .
In the selection sort , the inner loop starts at the unsorted portion of the
array and finds the smallest element in the unsorted area . Once located , it
swaps the smallest element with the first unsorted element . It then moves the
wall and continues the sort . Note that all exchanges take place in the
unsorted portion of the array; the sorted elements are never touched .
While the selection sort starts at the beginning of the unsorted portion of
the array, the bubble sort starts at the end . Another difference between the
two is that the bubble sort may make multiple exchanges in each pass. While
bubbling its way to the beginning of the array, it exchanges all elements that
are out of sequence. Like the selection sort , once the wall is moved , the
sorted data is never touched .
The insertion sort takes a completely different approach. In each pass,
the first unsorted clement is inserted into the sorted portion of the array.
While it might appear that there are no exchanges in the insertion sort , there
are . As explained previously, the first unsorted element is moved to a hold
area . This is the first step in an exchange. Then , as it loops through the
unsorted portion of the array, elements are shifted higher. Each shift is the
equivalent of the second step in an exchange. Finally, when the correct loca-
tion is found , the third step ol the exchange moves the temporarily stored ele -
ment to its correct position .
I able 8- 1 summarizes the exchange and shift characteristics of these
three sorts.
Sort Conclusions
In this section , we have covered three classic sorts. With the exception of the
insertion sort , you generally will not find them implemented in production
systems. I he insertion sort is used as a subfunction
in both Quicksort and
m
Chapter 8 Arrays 501
8.6 Searching
Another common operation in computer science is searching, which is the
process used to find the location of a target among a list of objects. In the
case of an array, searching means that given a value , we want to find the loca -
tion ( index) of the first element in the array that contains that value. The
search concept is shown in Figure 8- 27 .
Location wanted
(4)
Target given
( 62 )
The algorithm used to search a list depends to a large extent on the struc-
ture of the list . Since our structure is currently limited to arrays , we will
study
searches that work with arrays . As we study other structures , we will study
different search techniques .
There are two basic searches for arrays: the sequential search and the
binary search . The sequential search can be used to locate an item in any
to be sorted .
arrav. The binary search , on the other hand, requires the list
Sequential Search
The sequential search is used whenever the list is not ordered
. Generally, we
lists lists that are not searched often. In
use the technique only for small or
other cases we would first sort the list and then search it using the binary
search discussed later.
In the sequential search, we start searching for the target from the begin-
ning of the list, and we continue until we find the target or until we are sure
that it is not in the list . This gives us two possibilities; either we find it or we
reach the end of the list. Figure 8 - 28 traces the steps to find the value 62. We
.
first check the data at index 0 then 1, 2, and 3 before finding the 62 in the
fifth element ( index 4 ) .
location wanted'
4
index \
a(0] a[1] a[ 2] a[ 31 a[ 4 ] a [ 5 ] a[ 6] a [ 7 ] a(8 ] a[ 9 ] a [ 10 ) a [ 11 ]
4 l
21 36 14 62 91 8 22 7 81 77 10
1 Target given
62 != 4
index ^ < 621
afO] a[ 1) a[ 2] a[ 3] a[ 4 ] a[ 5] a[ 6] a( 7 ] a[ 8) a[ 9] a[10] a|11 ]
4 | 21 ; 36 14 62 91 8 22 7 81 77 10
2
62 != 21
index
afO) a[l ) a[ 2 ) a[ 3 ] a[ 4 ] a[ 5 ] a (6 ) a[ 7 ] a [ 8 ] a [ 9] a [ 10 ) a [ 11 )
4 21 [ 36» 14 62 91 8 22 7 81 77 10
3
62 != 36
index
a [ 0] a[ 1 ] a[ 2 j a[ 3] a [ 4 ] a[ 5) a [ 6 ] a [ 7 ] 3( 8 ] a[ 9 ] a[ 10] a [ 11 ]
4 21 36 14 62 91 8 22 7 81 77 10
4
62 != 14
index
a[0] a[ 1 ] a[2] a[3T a[ 4 ] a[ 5 ] a[ 6] a[7 ] a|8) a[ 9 j a[ 10) a [ 11 ]
4 21 36 14 62 | 91 8 22 7 81 77 10
62 == 62
But what il the target is not in the list ? Then we have to examine each cle-
ment until we reach the end ol the list . Figure 8 - 29 traces the search fora target
ol 2. W hen we detect the end ol the list, we know that the target does not exist.
Let s write the sequential search function. A search function needs to tell
the calling function two things: Did it find the data it was looking lor? If it
did. what is the index at which the data were found?
But a function can return only one value. For search functions, we use
the ietuin value to designate whether we found the
target or not. To ‘‘ return
the index location where the data were found, we will use call by-address
- .
I he search Junction requires four parameters: the
list we are searching,
the index to the last element in the list, the
target, and the address where the
found element 's index location will he stored. Althougl1 we could write it
without passing the index to the last element, that
would mean the search
Chapter 8 Arrays 503 1
would have to know how many elements are in the list. To make the function
as flexible as possible, therefore, we pass the index of the last data value in
the array. I his is also a good structured design technique. With this informa-
tion, we are now ready to create the design. It is shown in Figure 8- 30.
\
I Target
index
.a[0]
A
a( 1) a( 2] a[3] a[ 4) a[ 5 ] a[6 ] a[ 71 a[ 01 a[9 ] a( 10 ) a [ 11 ]
4 21 36 14 62 91 8 22 7 81 77 10
1 72 1= 4
index
a[0Pa[ 1 ) a|2] a[ 3] a[ 4 ] a|5 ] a [ 6 ] a[7 ] a(8) a(9) a[10 ] a[ 11 ]
4 21 ] 36 14 62 91 0 22 7 81 77 10
5 72 != 21
index
a(0) a|i) a|2) a [ 3 ] a]
^
a[ 5) a [ 6 ] a { 7) a[8 ] a[ 9) a|10] a[ll]
4 I 21 I 36 I 14 I 62 I 91 I 0 | 22 I 7 I 81 1 77 I 10
12 72 != 91
index
a (0 ] a ( 1 ] a [ 2 ] a [ 3! a( 4 ] a[ 5 j a(h ] n • r
4 21 36 14 62 91 8 22 7 81 77 10
Index off
Note : Not all test points are shown end of list
( SeqSearch
looker = 0
^
/
/ looker < last
&& target !=
\\
\ list[ looker ] M
looker++
* locn = looker
set found
( Return
J
FIGURE 8 - 30 Sequential Search Design
Program 8 - 1 3 Analysis Program 8 - 13 is simple, but it does merit some discussion. First, why did we use a
while statement? Even though we know the limits of the array, it is still an event -
controlled loop. We search until we find what we are looking for or reach the end of
the list. Finding something is an event, so we use an event loop.
Next, note that there are two tests in the limit expression of the loop. We have
coded the test for the end of the array first. In this case, it doesn' t make any difference
which test is first from an execution point of view, but in other search loops it might. You
should get in the habit of coding the limit test first because it doesn' t use an indexed
value and is therefore safer.
The call-by - address use for locn also merits discussion. Since we need to pass the
found location back to the variable in the calling program, we need to pass its address
to the function. A typical call to the search would look like the
statement shown below.
Notice how succinct this function is. In fact, there are more lines of documentation
than there are lines of code. The entire search is contained in one while statement. With
this short code, you might be tempted to ask, "Why write the function at all? Why not
j
\ \l
'
just put the one line of code wherever it is needed?'' The answer lies in the structured
programming concepts that each function should do only one thing and in the concept
of reusability. By isolating the search process in its own function, we separate it from the
process that needs the search . This is better structured programming. This also makes
the code reusable in other parts of the program and portable to other programs that
need searching. S ’!
One final point: This function assumes that the list is not ordered. If the list were
ordered, we could improve the search slightly when the data we were looking for were
not in the list. We will leave this improvement for a problem at the end of the chapter.
Binary Search
The sequential search algorithm is very slow. If we have an array of 1 million
elements, we must do 1 million comparisons in the worst case. If the array is
not sorted, this is the only solution. But if the array is sorted, we can use a
more efficient algorithm called the binary search. Generally speaking, we
should use a binary search whenever the list starts to become large. The defi -
nition of large is vague. We suggest that you consider binary searches when -
ever the list contains more than 50 elements.
The binary search starts by testing the data in the element at the middle
of the array. This determines if the target is in the first half or the second half
of the list. If it is in the first half, we do not need to check the second half. If
it is in the second half, w'e don’t need to test the first half. In other words,
either way we eliminate half the list from further consideration. We repeat
this process until we find the target or satisfy ourselves that it is not in the list .
To find the middle of the list, we need three variables, one to identify the
beginning of the list, one to identify the middle of the list, and one to identify
the end of the list . We will analyze two cases: the target is in the list , and the
target is not in the list .
Target Found
Figure 8 - 51 shows how .
we find 22 in a sorted array We descriptively call our
I I we
three indexes first , mid, and last. Given first as 0 and last as
4
can calculate mid as follows :
integral value of
Since the index mid is an integer, the result will be the
the quotient; that is, it truncates rather than rounds the calculation. Given
the data in Figure -
8 3 I , mid becomes 5 as a result of the first calculation.
mid = (0 + 11) / 2 - 11 / 2 5
At index location 5 , we discover that the target is greater than the list
value ( 22 > 21 ) . We can therefore eliminate the array locations 0 through 5.
( Note that mid is automatically eliminated . ) To narrow our search , we assign
mid + 1 to first and repeat the search .
I he next loop calculates mid with the new value for first and deter -
mines that the midpoint is now 8.
mid = (6 + 1 1) / 2 = 17 / 2 = 8
Again we test the target to the value at mid , and this time we discover
that the target is less than the list value ( 22 < 62 ) . This time we adjust the
—
ends ol the list by setting last to mid 1 and recalculate mid . This elimi -
nates elements 8 through 1 1 from consideration . We have now arrived at
index location 6 , whose value matches our target . This stops the search ^ . Fig-
ure 8 - 31 traces the logic we have just described .
’ ?
!
only two condition s terminate the binary search algorithm: Either the target is
( ound or first becomes larger than last . For example , imagine we want to
lind I 1 in our binary search array. This situation is shown in Figure 8 - 32 .
In this example, the loop continues to narrow the range as we saw in the
successfu l search , until we are examining the data at index locations 3 and 4.
These settings of first and last set the mid index to 3.
mid = (3 + 4) / 2 = 7 / 2 3
I he test at index location 3 indicates that the target is greater than the
list value, so we set first to mid + 1 or 4. We now test the data at location 4
and discover that 1 1 < 1 4.
mid = (4 + 4 ) / 2 = 8/ 2 = 4
At this point , we have discovere d that the target should be between two
adjacent values; in other words, it is not in the list . We see this algorithm i-
cally because last is set to m i d - 1, which makes first greater than last ,
the signal that the value we are looking for is not in the list .
first~
~
mid~ last
0 11 5 l 111
a[0 ] a[ 1 ] a[ 2 ] a [3 ] a[ 4 ] a[5 ] a[ 6 ] a [ 7 ] a[ 8 ] a[9 ] a[ 10)a [ 11 ]
4 I 7 I 8 I 10 I 14 I 21 I 22 I 36 62 I 77 81 91
^ nnrinr
a[ 0 ] a[ 1 ] a[ 2 ] a[ 3] a[ 4) a [5 ] a [ 6) ' a[ 7 ] a [8 ] a [9 ] a[ 10)a[ 11 ]
4 I 7 I 8 I 10 I 14 I 21 22 36 I 62 ' 77 81 T 91
first mid last
[ ] [ 10 ]a [ 11 ]
a[ 0] a[ 11 a[ 2 ] a[ 3 ] a[ 4 ] a[ 5 ] a[ 6 ] a [ 7 ] a[ 8 ] a 9 a
4 7 8 I 10 14 I 21 I 22 I 36 I 62 I 77 | 81 | 91
first mid last
11 > 10
npnpr jj
T
a[ 3) a[ 4 ] a[ 5 ] a[ 6 ] a [ 7 ] a[ 8 ] a [9 ] a[ 10 ]a[ 11 ]
| 4 | 7 |8 to ] 14 [ 2?] 22 I 36 I 62 I 77 l 81 ] 91 |
first mid last
11 < 14
(2JCDC D
Function terminates
Once we fully understand the logic, we can design the program Figure 8- 33 .
contains the design in the form of a flowchart .
Binary
o
mid =
first = 0
last = end ( first + last) / 2
o target <
list [mid] first = mid + 1
* locn = mid
set found ?
( Return
^ 6
FIGURE 8 - 33 Design for Binary Search
c
.2
w>
c) ^
a </>
it
il
Second Dimension
v (columns) >
i
WBBKm
table [0][0] table [0][1] table [0 ][2] table [0][3]
table [0]
J I I
table [1][0] table [1][1] table [1][2] table [1][3]
table [1]
I I [
table [3][0] table [3](1] table [3][2] table [3][3]
table [3]
table
Declaration
Iwo - dimensional arrays, like one-dimensional arrays, must be declared
before be ing used. Declaration tells the compiler the name of the array, the
t \ pe < > l each element, and the
size of each dimension. Two-dimensional
arrays can he either fixed
length or variable length.
n
m
-
As we saw with the one dimensional array, the size of a fixed - length array
is a constant and must have a value at compilation time. For example , the
array shown in Figure 8 - 35 can he declared and defined as follows:
Remember, however, that the dimensions must be set before the declara -
tion or a compiler error occurs.
Initialization
As we noted before, the definition of a fixed - length array only reserves mem -
ory for the elements in the array. No values will he stored. If we don t initial -
'
ize the array, the contents are unpredictable. Generally speaking , fixed -length
arrays should be initialized .
Initialization of array elements can he done when the array is defined. As
previously noted , variable-length arrays may not he initialized when they are
defined. As with one dimensional arrays, the values must he enclosed in braces.
-
This time, however, there is a set of data for each dimension in the array. So for
table , we will need 20 values . One way to initialize it is shown below.
int table[5][ 4 ] =
{ 0 , 1 , 2 , 3, 10, 11 , 12 , 13 , 20, 21 ,
22 , 23 , 30, 31 , 32 , 33 , 40, 41 , 42 , 43};
Note that we use commas between the elements in the rows and also com-
mas between the rows.
When we discussed one- dimensional arrays , we said that if the array is
com pletely initialized with supplied values , we do not need to specify the size
of the array. This concept carries forward to multidimensional arrays, except
that only the first dimension can be omitted. All others must he specified.
The format is shown below.
To initialize the whole array to zeros , we need only specify the first value
as shown below.
Inputting Values
Another way to ( ill up the values is to read them from the keyboard . For a
two-dimensional array, this usually requires nested for loops. If the array is an
—
n by m array, the first loop varies the row from zero to n 1 . The second loop
varies the column from zero to m - 1 . The code to fill the array in Figure 8- 35
is shown below .
W lien the program runs, we enter the 20 values for the elements and
they are stored in the appropriate locations.
Outputting Values
We can also print the value of the elements one by one < , using two nested
loops. Again , the first loop controls the printing of the rows and the second
loop controls the printing ol the columns. To print the table in its table format ,
a newline is printed at the end of each row. The code to
print Figure 8- 35 is
shown below .
continual
\ 'TTl / l
n
Chapter 8 Arrays 513
table[ 2][0] = 23 ;
table[ 0 ][ 1 ] = table[ 3 ][ 2] + 15;
00 01 02 03
10 11 12 13
20 21 22 23
30 31 32 33
40 41 42 43
Memory Layout
As discussed earlier, the indexes in the definition of a two-dimensional array
.
represent rows and columns This format maps to the way the data are laid
out in memory. If we were to consider memory as a row of bytes with the low-
est address on the left and the highest address on the right , then an array
would be placed in memory with the first element to the left and the last ele -
ment to the right . Similarly, if the array is a two- dimensional array, then the
first dimension is a row of elements that are stored to the leit. This is known
as “row- major " storage and is seen in Figure 8 - 36.
00 01 02 03 04
10 11 12 13 14
User’s View
row 0 row 1
00 01 02 03 04 10 11 12 13 14
[ 0][ 0] 0][ 1] [0][2 0][ 3] 0][ 4] 1][ 0] 1][1] 1 ][ 2] 1][ 3] [ 1 ][ 4]
Memory View
continuei
.
6 At least one \language, FOR IRAN, reverses
the data values placement in memory. It stores
data by columns.
Chapter 8 Arrays 515
m1
PROGRAM 8 - 16 Convert Table to One-dimensional Array (continued )
4 Date:
5 */
6 # include <stdio.h>
7 #define ROWS 2
8 tdefine COLS 5
9
10 int main ( void )
11 {
12 // Local Declarations
13 int table [ ROWS ] [ COLS ] =
14 {
15 {00 , 01 , 02 , 03 , 04},
16 {10, 11 , 12, 13 , 14}
17 }; // table
18 int line [ROWS * COLS];
19
20 // Statements
21 for ( int row = 0; row < ROWS ; row++ )
22 for ( int column = 0; column < COLS ; column++ )
23 line[ row * COLS + column ] = table[ row ][ column ];
24
25 for ( int row = 0; row < ROWS * COLS; row++ )
26 printf( " %02d " , line[row]);
27
28 return 0 ;
29 } // main
Results:
00 01 02 03 04 10 11 12 13 14
Program 8- 16 Analysis In Program 8 - 16 we use nested for loops to make the conversion . The first loop con-
trols the table rows, and the second loop controls the columns within each row. Since
we know how many elements are in each row of the two dimension array
- al , we can
by simply multiplying the row index by the num -
map it to the one - dimensional array
each When we are in row zero , we multiply the number of
ber of elements in row .
defined , ) by the row number ( 0 )
elements in one row ( designed by the constant COLS
result the column This maps the elements in the two - dimensional array
and add the to .
row 1 , we add the number of
to the beginning of the receiving array. When we are in
times the row ( 1 ) to the current column , which maps the elements
elements in one row
we add the product of the row and
to the next set ( in this case, 5 .. . 9) . To generalize,
location.
the number of elements in a row to the column to determine the receiving the and col-
Study the technique we used to define the number of elements in rows
only define the arrays but also to control the loop
umns. Note how they are used not to
with different row - column sizes to help
execution. Copy this program, and then run it also . You might
will need to change the initializatio n
understand the differences. You (
want to consider initializing the values by assignment
s within for loops. )
516 Section 8.7 Two-Dimensional Arrays
Passing A Row
Passing a row of the array is rather interesting. We pass a whole row by
indexing the array name with only the row number. Referring to Figure 8- 37,
we see that a row is four integers. When we pass the row, therefore, the
receiving function receives a one-dimensional array of four integers. The for
_
loop in print square prints the square ol each ol the lour elements. After
printing all the values, the function advances to the next line on the console
_
and returns. The for loop in main calls print square five times so that the
final result is a table of the values squared shown on the monitor.
#d e f i n e MAXROWS 5
#d e f i n e M A X C O L S 4
/ / Function Declarations
k
void print square ( int [ ] ) ; table
i n t main ( void )
{ 0 1 2 3
i n t t a b l e ( MAXROWS ) ( M A X C O L S ) = 10 11 12 13
{
{ 0 , 1, 2 , 3 >, 22 23
{ 1 0 , 1 1, 1 2 , 13 } , fy 20 21
{
{
2 0 , 2 1 , 22 ,
3 0 , 3 1, 3 2 ,
23 } ,
33 },
\ 30 31 32 33
{
};
40 , 41, 42 ,
/* table */
43 >
_
p r i n t s q u a r e ( t a b l e [ row ] ) ;
return 0; address
) / / main
of a row
_
void print square ( int x ( ] )
{
f o r ( i n t c o l = 0 ; c o l < MAXCOLS ; c o l + + )
k X
p r i n t f ( " % 6d ” , x [ c o l ] * x [ c o l ] ) ; 0 14 9
100 121 144 169
printf ( "\ n" ); 400 441 484 529
900 961 1024 1089
return ; 1600 1681 1764 1849
} / / printsquare
_
// Function call
_
print square (MAX COLS, table);
// Function definition
_
void print square ( int size, x[ size])
> _
// print square
_
double average ( int table[][ MAX COLS ])
Note that again we do not need to specify the number of rows in a fixed -
length array. It is necessary, however, to specify the size of the second dimen -
sion. Thus, we specified the number of columns in the second dimension
_
( MAX COLS). In summary, to pass two-dimensional arrays to functions:
Figure 8 - 38.
518 Section 8.7 Two-Dimensional Arrays
__
#define MAX ROWS 5 table
#define MAX COLS 4 1 2 3
// Function Declarations
_
double average (int [ ][ MAX COLS ]); 11 12 13
int main (void ) 21 22 23
{
double ave;
int table[MAXROWS][ MAX
__COLS] = 31 32 33
{ 41 42 43
{ 0, 1, 2, 3 },
{ 10, 11, 12, 13 },
{ 20, 21 , 22 , 23 },
{ 30, 31, 32, 33 },
{ 40, 41 , 42 , 43 >
}; // table
return 0;
} // main
Address
{
double sum = 0;
for (int i = 0 ; i < MAXROWS; i++) X
for (int j = 0; j < MAX_COLS; j++)
sum += x [i] (j];
return (sum / ( MAX ROWS * MAXCOLS)); sum
} // average
Program 8 - 1 7 Analysis This iis a rather simple pattern problem, similar to many we saw in Chapter 6 . The
there
only difference is that now we are creating the pattern in array elements. Since the
, need loops to control the pattern . If the row equals
are two dimensions we two
, then we are
column, we assign 0 to the element. If the row is greater than the column
, assign - 1 . And if the row is less than the col-
in the lower half of the matrix so we
umn, then we are in the upper half of the matrix
and we assign + 1 .
the array. The hrst dimension is called a plane, which consists of rows and
columns. Arrays of four or more dimensions can be created and used, but
they are difficult to draw.
Second Dimension) 2
.
s ( rows)
3
2
0
0 1 2 3
&&
/" Third Dimension"/
x )
^ fcolumns ^ ^
FIGURE 8 - 40 A Three- dimensional Array ( 3 x 5 x 4)
.
Although a three-dimensional array is exactly what is shown in Figure 8- 40.
the C language looks at it in a different way. It takes the three-dimensional array
to be an array ol two-dimensional arrays. It considers the two - dimensional
array to he an array ol one-dimensional arrays . In other words, a three -
dimensional array in C is an array of arrays of arrays. This concept also holds
true lor arrays ol more than three dimensions. The C view of a three-
dimensional array is seen in Figure 8 - 41.
I he definition for the same table as a variable- length array requires three
variables, one for the rows, one for the columns, and one for the planes.
( » i \ c n these variables, it
would be defined as shown in the following example.
10 11 12 13
123
table [0][1][0] table [0][1][1] table [0][1 ][2] table [0][1][3] 233
[ 1 ][ 2 ][ 3 ]
table [01(1] [2](3][3]
20 21 22 23
133
table [0][2][0] table [0][ 2][1] table [0][ 2][2] table [0][2][3] 243
[ 1][3][3]
table (0][2) [2][4][3]
30 31 32 33 table[2]
143
table [0][3][0] table [0][3)[1] table [0][3][2] table [0][3][3]
MM3]
table [0][3]
table[1]
40 41 42 | 43
table [0][ 4][0] table [0][4][1] table [0) [4][ 2] table [0][ 4][3]
table [0][ 4] table[0]
Initialization
As we said before, declaration and definition only reserve space for the ele-
ments in the array. No values will he stored in the array. If we want to
store
,
values we must either initialize the elements , read values from the keyboard ,
or assign values to each individual element . Following the rules we discussed
for one- and two-dimensional arrays, only fixed - length multidimensional
arrays can be initialized when they are defined .
saw
Once more, initialization is simply an extension of the concept we )
Section 8 ./ .
for initializing a two-dimensional array ( see Initialization in
For the three-dimensional array, we nest each plane in a set of brackets . For
as we did for the two -dimensional array .
each plane, we bracket the rows
plane and row as we have done in the following
When we group the data by
code , the reader will be able to visualize the array with ease
. We have added
comments to make it even easier to read the values
.
As we saw previously, the planes size, and only the plane’s size, does not
need to he specified when we use explicit initialization . The size of all dimen-
sions after the first must he explicitly stated .
Ot course, if we want to initialize all the elements to zero, we can simply
initialize only the first element to zero and let the compiler generate the code
to initialize the rest of the array.
.^ ^ t 0 SO ve
the arra> structures and the r relationships is
problem. A picture of
shown in Figure 8- 42
T
i \
rowAve
columnAve
The program begins by requesting the user provide data for a two-
dimensional array. Once the array has been filled , the program calculates the
average of each row and places it in a parallel array of row averages. It then
calculates the average lor each column and places it in an array of column
averages. Although we have represented the column -average array horizon -
tally and the row average array vertically, they are both one-dimensional
-
arrays .
When all the calculations are complete, the program calls a I unction to
print the array with the row averages at the end of each row and the column
averages at the bottom of each column. The structure chart for the program
is seen in Figure 8 - 43.
calculate
Averages
and correct any coding errors found by the compiler. Note that we don’t run
the program because it produces no output at this stage. Program 8- 18 con-
tains the code.
3
-
contain row and column averages.
4 Written by:
5 Date:
6 */
7 # include <stdio.h>
8
9 #define MAX ROWS 5
continue
ft]if
Program 8 - 1 9 Analysis After running the program we carefully examine the results to make sure that the new
function worked correctly. Because we created our test data before we ran the pro-
gram, we knew what the results should be. A simple comparison of the test data and
the results verifies that the table was built correctly.
continued
Chapter 8 Arrays 527
**Row averages
15.0 25.5 30.0 44.0 56.0
continue
Chapter 8 Arrays 529
'
1 7
1
PROGRAM 8 -21 Calculate Row and Column Averages: Print Tables (continued )
3 contain row and column averages.
4 Written by:
5 Date:
6 */
7 # include <stdio.h >
8
9 _
# define MAX ROWS 5
10 #define MAX COLS 6
11
12 // Function Declaration
13 void getData ( int _
table[ ][ MAX COLS ]);
14 void rowAverage ( int _
table[ ][ MAX COLS],
15 float rowAvrg [ ]);
16 void colAverage ( int _
table[][ MAX COLS ],
17 float colAvrg [ ]);
18 void printTables ( int _
table[ ][ MAX COLS ],
19 float rowAvrg[],
20 float colAvrg[ ]);
21
22 int main ( void )
23 {
24 // Local Declarations
25 int _ _
table [ MAX ROWS ][ MAX COLS];
26
27 float rowAve [ MAX_ROWS] = {0};
28 float colAve [ MAX_COLS ] = {0 > ;
29
30 // Statements
31 getData ( table);
32 rowAverage (table, rowAve);
33 // colAverage ( table , colAve );
34
35 printf( " \n " );
36 printTables ( table, rowAve , colAve);
37 return 0;
38 > // main
39
40 ===== getData ====
- array.
41 Reads data and fills two dimensional
table is empty array to be filled
42 Pre
43 Post array filled
*/
44
45 void getData (int tablet ][ MAX
_
COLS])
46 {
continued
530 Section 8.9 Programming Example — Calculate Averages
continue
Chapter 8 Arrays 531
Testing Sorts
As our programs become more complex, we need to spend more time creating
test data that will completely validate them . In this section , we examine some
techniques for testing sorts.
In general , we should conduct four tests: ( 1 ) sort a list of random values,
( 2 ) sort a list that is already in sequence, (3) sort a list that is in reverse order,
( 4 ) sort a nearly ordered list , such as one in which every tenth item is one
position out of sequence . Table 8- 2 contains a summary of the tests that we
should conduct and some sample test data to show the points.
Nearly ordered 5 6 7 21 19 22 23 31 29 33 51 93
Ordered — ascending 5 6 7 11 19 22 23 31 33 51 78 93
Ordered — descending 93 78 51 33 31 23 22 19 11 7 6 5
Testing Searches
When we test the sequential search , we need only four tests. Three deal with
—
finding an element in the table find the first , last , and any element in the
middle. The last case deals with trying to find an element that is not in the
list ; look for any value that is not in the middle of the list. Whenever testing
an array, always he sure that the test data access the first and last elements of
the array. Many array processing errors occur at these locations.
When testing an array search, always access the first and last elements .
I he binary search requires the lour tests discussed
above, plus three
more. Since it uses an ordered list , we should try to find a target lower than
the first element in the list and another target greater than the last element.
Finally, we should find two elements that are in adjacent array locations,
such as l i s t [ 0 ] a n d l i s t [ l ] . The reason for this
test is that the binary
search includes logic that divides by two . An error
could result in being able
\ / \ ml
.
to find only even - or only odd - numbered elements Testing for adjacent loca -
tions ensures that such an error won ’t happen . These test cases are seen in
Table 8- 3.
Expected Results
found
Index
0
B
target = = list[0] all
Search
( n - l ) + ( n - 2 ) + . .. + 2 + 1 = n
534 Section 8.10 Software Engineering
-
(41) ' !<"• - >
Discarding the coefficient and selecting the larger factor, we see that the
-
dominant factor in the bubble sort is n 2 t which in big O notation would be
stated as 0( w 2 ).
continued
{
list[walker + 1 ]
walker ;
> // if
—
= list[ walker];
Chapter 8 Arrays 535
1
Ilf
Does the pattern look familiar? It should . Again we have the same basic
nested for loop logic that we saw in the bubble sort and the selection sort .
I he outer loop is executed n times, and the inner loop is executed ( n 1 ) / 2 —
times , giving us 0( w 2 ).
Comparing the sequential search and binary search, we see that, disre-
garding the time required to order the list , the binary' search is obviously bet -
ter for a list of any significant size ( see Table 8 - 4 ). We therefore recommend
the binary search for all but the smallest of lists—that is, lists with less than
50 elements.
16 4 8 16
50 6 25 50
256 8 128 256
1 ,000 10 500 1 ,000
10,000 14 5,000 10,000
100,000 17 50,000 100,000
1 ,000,000 20 500,000 1 ,000,000
TABLE 8 4 Comparison of
*
m
Chapter 8 Arrays 53 /
.
10. Another cause of invalid indexes is an uninitialized index. Make sure
your indexes are always properly initialized.
1 1 When initializing an array when it is defined , it is a compile error to pro-
.
vide more initializers than there are elements.
12 . It is a compile error to leave out the index operators in an assignment
statement, as shown below.
float costAry[20];
•••
costAry = quantity * price;
i a scanj
13. It is most likely a logic error to leave out the index
operators in
statement , as shown in the following example . In this case , costAry is
array and the input will be placed in the first element
the address of the
code it with
of the array. Even when this is the desired result you should
,
the index operators as shown.
538 Section 8.13 Summary
float costAry[20];
•••
scanf("%f", costAry ); // Poor Style
scanf( " %f " , ScostAry[ 0 ]); // Clear code
-
1 5. To pass a variable size array to a function , you need either to include the
variable defining the array size or use an asterisk .
8.13 Summary
-
A one dimensional array is a fixed sequence of elements of the same type.
We use indexes in C to show the position of the elements in an array.
J An array must be declared and defined before being used. Declaration and
definition tell the compiler the name of the array, the type of each ele-
ment , and the size of the array.
Initialization of all elements of a fixed length array can be done at the time
of the declaration and definition.
LI II a one - dimensional array is completely initialized when it is declared , it is
not necessary to specify the size , hut it is recommended
.
When an a i i a y is partially initialized , the rest of the elements are set
to zero.
1
iff
Chapter 8 Arrays 539
We can (ill the elements of an array by using a loop to read the values from
the keyboard or a file.
We can access the individual elements of an array using the array name
and the index.
We can read or write the values of an array using a loop.
ti
We can also pass the whole array to a function. In this case, only the
address ol the array will he passed. When this happens, the function has
access to all elements in the array.
A irequency array is an array whose elements show the number of occur-
rences ol data values in another array.
.
13 The search locates the target item by starting at the
beginning and moving toward the end of the list .
a. ascending
b. binary
c. bubble
d. selection
e. sequential
14. Which of the following statements about a sequential search is false?
a. Any array can be searched using the sequential search .
b. If the target is not found , every element in the list is tested .
c. The efficiency of the sequential search is O ( n ) .
d. The list must be ordered.
e. The sequential search is generally recommended only for small lists.
15. Which of the following statements about two-dimensional arrays is true?
a. A two - dimensional array can he thought of as an array of one-
dimensional arrays.
b. Only the size ol the second dimension needs to be declared when the
array is used as a parameter.
c. I wo different types can be stored in a two-dimensional array.
d. I he first dimension is known as the column dimension .
e. \\ hen passed to a function , the size of the second dimension must be
passed as a value parameter.
Exercises
16. What would be printed by the following program ?
#include <stdio.h>
int main (void )
{
// Local Declarations
int list [10] = {0};
// Statements
for (int i = 0; i < 5; i++ )
list [2 * i + l ] = i + 2 ;
for (int i = 0; i < 10 ; i++ )
printf( " %d\n " , list [i]);
return 0 ;
} // main
continued
V' 11 'iT,| ft1
iTy 1
Chapter 8 Arrays 543
/ / Local Declarations
int list [ 10 ] = {2, 1, 2, 1 , 1, 2, 3, 2 , 1, 2 > ;
/ / Statements
printf( " %d \ n ", list [ 2 ] );
printf( "% d \ n ", list [ list [ 2 ] ] ); M fl ,
printf( " % d \n ”, list [ list [ 2 ] + list [ 3 ] ] );
printf( " %d \ n", list [ list [ list [ 2 ] ]] );
return 0;
> / / main
19. An array contains the elements shown below. The first two elements
have been sorted using a selection sort . What would be the value of
the elements in the array after three more passes of the selection sort
algorithm ?
7 8 26 44 13 23 98 57
20 An array contains the elements shown below. The first two elements have
.
been sorted using a bubble sort . What would be the value of the elements
in the array after three more passes of the bubble sort algorithm ? Use the
version of bubble sort that starts from the end and bubbles the smallest
element.
7 8 26 44 13 23 57 98
47 3 21 32 56 92
After two passes of a sorting algorithm , the array has been rearranged as
shown below.
3 21 47 32 56 92
80 72 66 44 21 33
After two passes of a sorting algorithm , the array has been rearranged
as shown below .
21 33 80 72 66 44
47 3 66 32 56 92
After two passes of a sorting algorithm , the array has been rearranged
as shown below.
3 47 66 32 56 92
8 13 17 26 44 56 88 97
8 13 17 26 44 56 88 97
Chapter 8 Arrays 545
m1
2 / . Both the selection and bubble sorts exchange elements. The insertion
sort does not . Explain how the insertion sort rearranges the data without
exchanges.
Problems
28. We have two arrays A and B, each of 10 integers. Write a function that
tests if every element of array A is equal to its corresponding element in
array B. In other words, the function must check if A[0 ] is equal to B[ 0],
A[1 ] is equal to B[ 1 ] , and so forth. The function is to return true if all
elements are equal and false if at least one element is not equal.
29. Write a function that reverses the elements of an array so that the last
element becomes the first , the second from the last becomes the second ,
—
and so forth . The function is to reverse the elements in place that is ,
without using another array. ( It is permissible to use a variable to hold an
element temporarily.) Then write a test driver to test your function . Test
it twice , once with an even number of elements in the array and once
with an odd number of elements in the array.
.
30 The Pascal triangle can be used to compute the coefficients of the terms in
the expansion of ( a + b ) n. Write a function that creates a two-dimensional
matrix representing the Pascal triangle. In a Pascal triangle , each element is
the sum of the element directly above it and the element to the left ol the
element directly above it ( if any). A Pascal triangle ol size 7 is shown below.
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
6 15 20 15 6 1
1
In the above example , the first element ol row[0] and the first two -
ele
[ ] are set to 1 . Then each of the following rows up to the
ments of row l
maximum size , are set with a loop as
shown in the following pseudocode:
Algorithm PascalTriangle
1 pascal[0][0] = 1
2 pascal[l ][0] = 1
3 pascal[ l ][ l ] = 1
4 prevRow = 1
5 currRow = 2
6 loop ( currRow <= size)
1 pascal[ row ][0 ] = 1
2 col = 1
continued
546 Section 8.14 Practice Sets
Your program must IK* able to create the triangle of any size .
.
31 An international standard book number (ISBN) is used to uniquely iden-
tify a book. It is made of 10 digits, as shown in Figure 8 - 44. Write a func -
.
tion that tests an ISBN to see if it is valid For an ISBN number to be
valid, the weighted sum of the 10 digits must be evenly divisible by 11.
The tenth digit may hex, which indicates 10.
0-07-881809-5
5 1 5
Weighted Sum 220
0 0 0
0 0 0
7 7 7
8 15 22
8 23 45
1 24 69
8 32 101
0 32 133
9 41 174
46 220
5
.
34 Write a program that creates an array of 100 random integers in the
range 1 to 200 ( see Chapter 4 for a discussion of random numbers ) and
then , using the sequential search , searches the array 100 times using
randomly generated targets in the same range. At the end of the program,
display the following statistics:
.
a The number of searches completed .
b. The number of successful searches.
.
c The percentage of successful searches.
d . The average number of tests per search .
To determine the average tests per search , you will need to count the
number of tests for each search . 1 lint: Use the comma operator to count
the compares.
35. Repeat Problem 34 using the binary search.
36. The sequential search assumes that a list is unordered . 11 it is used when
the list is in fact ordered , the search can he terminated with the target
not found whenever the target is less than the current element . Modify
Program 8- 13 to incorporate this logic .
37. Repeat Problem 34 with the modified search you created in Problem 36.
38. I he binary search is a good algorithm to implement using recursion.
Rewrite the binary search function using recursion . Then repeat
Problem 35 using the recursive function.
39. Modify the bubble sort to stop as soon as the list is sorted . ( See discus-
sion in the analysis of Program 8- 10 . )
40. Modify the selection sort function to count the number of exchanges
needed to order an array ot 50 random numbers (see Chapter 4 for a dis-
cussion of random numbers). Display the array before and after the sort .
At the end of the program , display the total exchanges needed to sort
the array.
41 . Repeat Problem 40 using the bubble sort .
42. Program 8- builds a frequency array and its histogram. If there is an
/
invalid data point in the input, it is displayed and ignored. Modify' the
program to make the frequency array one element larger than the data
range, and use the last element of the array as a count of numbers not in
the specified range.
.
43 Write a program that fills the right - to left diagonal of a square matrix
-
-
with zeros, the lower right triangle with Is, and the upper- left triangle
—
with + Is. The output ol the program , assuming a six by-six matrix, is
shown in Figure 8- 45.
-
Chapter 8 Arrays 549
44. Given an array of 100 random numbers in the range 1 ...999, write a
function for each of the following processes. In building the array, il the
random number is evenly divided by 3 or 7 , store it as a negative number.
a . Print the array ten values to a line . Make sure that the values are
aligned in rows.
b. Print the odd values, ten to a line .
c. Print the values at the odd numbered index locations, ten to a line .
d . Return a count of the number of even values.
e. Return the sum of all values in the array.
f . Return the location of the smallest value in the array.
g. Return the location of the largest value in the array.
h . Copy all positive values to a new array. Then use process "a " above to
print the new array.
i. Copy all negative values to a new array. Then use process “ a ” above to
print the new array.
45. Modify the sort of your choice to sort two parallel arrays. Parallel arrays
are two or more arrays that contain related data . For example, one array
might contain identification numbers, and a related array might contain
corresponding scores in a tournament . The sort will sort only the array
containing the identifiers ; however, as each element in the sorted array is
moved, its corresponding element in the data array must also he moved .
After the arrays have been sorted , print out the data as shown below.
ID Score
1 72
2 89
3 98
4 105
—
Write a test driver to test your sort . Use the following data the first value
is an identifier and the second value is a score: { 18, 90}, { 237 , 47}, {35, 105},
}.
{ 5 , 25}, {76, 739}, { 103, 26}, { 189 , 38}, {22 , 110}, {156, 31}, {49 , 245
550 Section 8.14 Practice Sets
.
46 Write the incremental implementation to calculate the column averages
—
for Section 8.9, “ Programming Example Calculate Averages.” Before
testing the column average increment , determine the averages so that
you will be able to verify that the correct values were calculated.
Projects
47. Write a C program that simulates a guessing game. Each turn , you
choose among nine possible guesses. As many as five guesses may be
made in a turn . For each turn , the program will generate a random num -
ber between 1 and 36. Each correct guess will be rewarded with points
based on how many of your current points you risked .
A game board divides the numbers into rows and columns, as shown
in Figure 8- 46. This board provides the basis for your guesses.
01 02 03
04 05 06
LOW
07 08 09
10 11 12
13 14 15
16 17 18
MEDIUM
19 20 21
22 23 24
25 26 27
28 29 30
HIGH
31 32 33
34 35 36
You can guess whether the random number is even or odd. In this
case, you get I point for each point risked when you guess correctly. You
can guess whether the number is low ( 1 -12 ) , medium ( 1 3-24 ) , or high
( 23-36 ) . In this
case, you will get 2 points for each point risked . You can
TT
Chapter 8 Arrays 551
-
also guess left , center, or right , as shown in Figure 8 46. In this case, you
get 2 points lor each point risked when your guess is correct. Finally, you
can guess a specific number between 0 and 36. In ibis case you get
36 points for each point risked when your guess is correct .
Io make the game more interesting, each round allows up to five 4
guesses. None ol the five may he correct , or any number up to all five
may he correct. The program stops when the player quits or when the
player is out of points.
The program first asks the number of points the user wants to start
with , as shown below.
The program then prints the guess menu and allows up to five
guesses , as shown in the following example.
Guesses Choices
0 Odd
E Even
L Low
M Med
H High
F Left
C Center
R Right
N Number
Guess 2 :
Enter your choice? H
Points at risk? 15
Guess 3 :
Enter your choice? N
Enter your number: 18
Points at risk? 20
Guess 4 :
Enter your choice? 0
Points at risk? 120
Guess 5 :
Enter your choice? L
Points at risk? 0
552 Section 8.14 Practice Sets
After all guesses have been made, the program generates the random
number and displays the following message:
My number is: 31
Use one - and two- dimensional arrays only. Test your program with
the quiz data in Table 8- 7 .
1234 52 7 100 78 34
2134 90 36 90 77 30
3124 100 45 20 90 70
4532 11 17 81 32 77
5678 20 12 45 78 34
6134 34 80 55 78 45
7874 60 100 56 78 78
8026 70 10 66 78 56
9893 34 9 77 78 20
1947 45 40 88 78 55
2877 55 50 99 78 80
3189 22 70 100 78 77
4602 89 50 91 78 60
5405 11 11 0 78 10
6999 0 98 89 78 20
—
rows and then print the frequency diagram
hers as shown below.
with a heading of the nu ni -
— 0 1 - 2
—
- 3- 4- ... -18- -19- Invalid
554 Section 8.14 Practice Sets
51. Modify the program you wrote in Project 50 to include a histogram print-
out of the data. Its format should be similar to Figure 8- 15.
52. Using the data from Project 48, build a two-dimensional array of stu -
dents. Then write a search function that uses the sequential search to
find a student in the array and prints the students’ scores, average score,
and grade based on an absolute scale ( 90% is A, 80% is B, 70% is C, 60%
is D , less than 60% is F ). After each printout , give the user the opportu -
nity to continue or stop.
53. Modify the program you wrote in Project 52 to sort the two-dimensional
array of students. Then rewrite the search function to use a binary
search .
54. Write a program that sorts a 50-element array using the selection sort .
the bubble sort , and the insertion sort . Each sort is to be executed twice.
a . For the first sort , ( ill the array with random numbers between
1 and 1000.
b. For the second sort , ( ill the array with a nearly ordered list . Create the
nearly ordered list by filling the array with sequential numbers and
then subtracting 5 from every 10 th number in the array.
c. Each sort ( selection , bubble , insertion ) is to sort the same data . For
each sort , count the number of comparisons and moves necessary to
order this list .
d . After each sort execution , print the unsorted data followed by the sort
-
data in 5 - by 10 matrixes ( 5 rows of 10 numbers each ). After the sorted
data , print the number of comparisons and the number of moves
required to order the data. Provide appropriate headings for each
printout .
e. Io make sure your statistics are as accurate as possible , you must ana -
lyze each loop limit condition test and each selection statement in your
sort ( unctions. The best way to count them is with a comma expres -
sion , as shown below. Use similar code for the selection statements.
( . Analyze the heuristics you generated , and write a few lines concerning
what you discovered about these sorts. Put your comments in a box
( asterisks ) alter your program
documentation at the beginning o! your
program.
55 . \ \ iite a program to compute the arithmetic
mean ( average ) , median , and
-
mock loi up to 50 test scores. I he data are contained in a text file. The
program w ill also print a histogram of the scores.
I he piogram should start with a function to
read the data file and ( ill
the array. Note that there may he fewer than
50 scores. This will require
that the read ( unction return the index for
the last element in the array.
Chapter 8 Arrays 555
Iff
Io determine the average, write a function similar to Program 8 - 4 .
To determine the median , you must first sort the array. The median is the
score in the middle ol the range . This can be determined by selecting the
score at last / 2 il last is an even index and by averaging the scores at
the floor and ceiling ol last / 2 if last is odd . The mode is the score that
occurs the most often . It can be determined as a byproduct of building
the histogram. After building the frequency array, use it to determine
which score occurred most often . ( Note that two scores can occur the
same number of times.)
56. Write a menu - driven program that allows the user to fill an array of
50 integers with random numbers in the range 1 . . .999, sort it , and then
search it to determine if a given random number was generated . The
menu is shown below.
MENU
Select one of the following options:
F. Fill array with a random number series
P. Print the array
S. Sort the array
Q. Query the array
Z. Terminate the program
Each time the fill option is executed , it will fill the array with a new
random number series.
You may use any of the sorts discussed in the text .
If the query locates the number, it will print a message that the num -
ber was located and the index location where it was found.
If the query does not locate the number, it will print a message that
the number was not in the list and then print the value and index loca -
tion of the largest number less than the target and the smallest value
greater than the requested number. Note that the first and
last ele-
ments will not have both a smaller and larger value.
If the array has been sorted , the query will use a binary search . If it is
not sorted , it will use the sequential search .
To test the program
a . Fill ( F ) the array.
b. Print ( P ) the unsorted array.
c. Search ( Q ) the first , last , and a middle element.
d. Refill ( F ) the array.
e. Sort ( S ) the array.
f. Print ( P ) the sorted array.
g - Search ( Q ) the first , last , and a middle element
.
for a number less than the first , greater than the last , and
h . Search Q ( )
not found in the middle ol the list .
11
Pointers
Every computer has addressable memory locations. In previous chapters, all
of our data manipulations, whether for inspection or alteration, used variable
names to locate the data. In other words, we assigned identifiers to data and
then manipulated their contents through the identifiers.
Pointers have many uses in C. Besides being a very efficient method of
accessing data, they provide efficient techniques for manipulating data in
arrays, they are used in functions as pass- bv- address parameters, and they are
the basis for dynamic allocations of memory.
As shown in Figure 9- 1 , pointers are the third of the derived types.
r Derived
Types i
Function
Type
Array
Type
Chapter 8
Pointer
Type
Structure
Type
Chapter 12
1 Union
Type
Chapter 12
Enumerated
Type
Chapter 12
i
Chapter 4
Objectives
To understand the concept and use of pointers
To be able to declare, define, and initialize pointers
To write programs that access data through pointers
To use pointers as parameters and return types
To understand pointer compatibility, especially regardin
g pointers to pointers
To understand the role of quality in software engineer
ing
558 Section 9.1 Introduction
9.1 Introduction
A pointer is a constant or variable that contains an address that can be used
to access data. Pointers are built on the basic concept of pointer constants .
To understand and use pointers, we must first understand this concept .
Pointer Constants
First, let ’s compare character constants and pointer constants. We know that
we can have a character constant , such as any letter of the alphabet, that is
drawn from a universe of all characters. In most computers, this universe is
ASCII. A character constant is a value and can be stored in a variable.
Although the character constant is unnamed, the variable has a name that is
declared in the program. This concept is shown in Figure 9 - 2.
’\n’
address
value
character
constants ’ A’ 145600
’G’ aChar
’X’
variable
variable . name.
’c’
’k’
’x’
000000
V
variable
name
address
character 145595
constants ' A'
•G
X*
aChar
a
- 145600 145600
145603
pointer
'c' constants
k' 1048575
'x '
Memory
Pointer constants, drawn from the set of addresses for a computer, exist by
themselves We cannot change them; we can only use them.
.
Pointer Values
I laving defined a pointer constant as an address in memory, we now turn our
attention to saving this address. If we have a pointer constant , we should be
able to save its value if we can somehow identify it . Not only is this possible ,
but we have been doing it since we wrote our first scanf statement with an
address operator.
The address operator ( & ) extracts the address for a variable . The result is
a unary expression , which is also known as an address expression
. The Prece-
dence Table ( see inside front cover ) lists the address operator in the unary
category, precedence IS.
&variable name
rhe situation changes slightly when we talk about integers. In most com-
puters , the int type occupy 4 bytes . This means that each int variable occu
pies four memory locations. Which of these memory locations is used to find
-
the address of the variable? In most computers, the location of the first byte
is used as the memory address . For characters , there is only l byte , so its
location is the address. For integers, the address is the first byte. This is
shown in Figure 9- 5.
I he same design applies to real and other data types. The address of a
variable is the address ol the first byte occupied by that variable.
variable
name integer
value
integer \ a
constants ) j -123 -123 234560
b 145 234564
address of
.variable a ^
a -123 1234560
p 234560 |
name and location are constant , the value may change as the program exe -
cutes . This figure also has a pointer variable , p. The pointer has a name and a
location, both of which are constant. Its value at this point is the memory
location 234560. This means that p is pointing to a. In Figure 9 - 6, the physi-
cal representation shows how the data and pointer variables exist in memory.
The logical representation shows the relationship between them without the
physical details.
We can go even further and store a variable’s address in two or more dif
-
both of which are constant . Their value at this point is the memory location
234560. This means that both p and q are pointing to a.
There is no limit to
the number of pointer variables that can point to a variable.
value of p
( address of a)
&a
p | 234560 | ^ -123
234560
q 234560 |
*p
Let 's assume that we need to add 1 to the variable a . We can do this with
any ol the following statements, assuming that the pointer, p , is properly ini -
tialized ( p = & a ).
a ++ ; a = a + 1; *p = * p + 1; ( * p ) ++ ;
used in the right- hand side of the assignment operator, they can only inspect
( copy or compare ). When they are used in the left hand side of the assign -
-
ment operator, they change the value of x .
p
± ) x = x + 3; P 7 i
q X q X
P 7 i *p = 8; P 8 I
q x q x
P 8 I + * p; P i6 i
q x fmultiply N,
q X
Voperatory
P 16 I x = *p *q; P 256 \
q X q X
The indirection and address operators are the inverse of each other, and
when combined in an expression, such as * & x, they cancel each other. To see
this, let ’s break down the expression. These two unary operators are evaluated
from the right . T he first expression is therefore & x, the address of x , which as
we have seen, is a pointer value. The second expression, * ( & x ) , dereferences
the pointer constant, giving the variable ( x) itself. Therefore, the operators
effectively cancel each other ( Figure 9 -9). Of course, we would never code
the expression * & a in a program; we use it in Figure 9- 8 for illustration only.
&
J
inverse
>
•
J
FIGURE 9 - 9 Address and Indirection Operators
data declaration
type identifier
1
pointer declaration
type
D identifier ]
FIGURE 9 - 10 Pointer Variable Declaration
char a ; Z char* p
int n ;
GD int* q;
continue
Chapter 9 Pointers 565
^
Results:
14 00135760
00135760 14 14
Program 9- 1 Analysis Program 9- 1 requires a little explanation. First, we have defined an integer variable,
a, and a pointer to integer, p, to which we assign a's address. We then print twice .
The first print displays the contents of the variable a and its address (note the pointer
conversion code). The second print uses the pointer, p. It prints the pointer value con-
taining the address of a, followed by the contents of a, first referenced as a pointer
and then as a variable. This demonstrates two ways to access the data . We suggest
that you run this program for yourself. Of course, when you do, you will get a differ -
ent address . Also, some compilers display addresses as integers, and some use hexa -
decimal numbers.
int * pa ;
int * pb;
In this usage, the asterisk declares that the type of pa and pb is a pointer to
integer.
!
On the other hand, we also use the asterisk for redirection. When used
for redirection, the asterisk is an operator that redirects the operation from
the pointer variable to a data variable. For example, given two pointers to
integers, pa and pb, sum is computed as
sum = * pa + * pb;
The first expression, * pa, uses the pointer value stored in the pointer
variable, pa, to locate its value. The address in pa is not added; it is redirected
through the asterisk operator to the desired value. Likewise, the address
in
the second expression, pb, is not added but used to locate the second expres -
sion’s value. Once both of the expressions have been evaluated, they can be
added and the resulting expression value stored in sum.
566 Section 9.1 Introduction
unknown
value
int a; a ??? fc ?
int* p ; P ???
^ pointer to \
unknown location
int a ;
int* p = & a ; -
// int variable value unknown
// p has valid address
* p = 89 ; // a assigned value 89
int a ; int* p ;
declaration
int* p = &a;
P = &a ;
initialization
Note that in Figure 9- 13, the initialization involves two different steps.
First the variable is declared. Then the assignment statement to initialize the
variable is generated. Some style experts suggest that you should not use an
.
initializer in this way 2 Their argument is that it saves no code; that is, that
the initializer statement is required either as a part of the declaration and ini-
tialization or as a separately coded statement in the statement section of the
function. Putting the initialization in the declaration section tends to hide it
and make program maintenance more difficult . We tend to agree with them.
We can also set a pointer to NULL, either during definition or during exe-
cution. The following statement demonstrates how we could define a pointer
with an initial value of NULL.
int* p = NULL;
P qQrO^
22 q = p;
a [TJ b
23 r = &c; P
3
24
25 P = &a;
26 *q = 8;
27
28 *r = *p;
29
30 *r = a + *q + *&c;
31
32 printf( " %d %d %d \n" ,
33 ar b , c );
34 printf( " %d %d %d " ,
35 * P * q , * r );
'
36 return 0;
37 } // main
Results:
6 8 20
6 8 20
L
I
a b
'
pa pb Pr
Results:
Enter the first number : 15
Enter the second number: 51
15 + 51 is 66
Program 9 - 3 Analysis This program is rather straightforward except for one thing. Look at statements 20
and 22 carefully. What is missing? When we discussed pass-by address in
Chapter 4, we mentioned that when input areas are passed by address, we don't use
the address operator in the sconf statement . This is because the pointer already con-
tains an address. It would be an error to use the address operator on the pointers in
this program.
Results:
Enter three numbers and key return: 10 20 30
10
20
30
Results:
Enter a number: 15
15
15
15
n1
Chapter 9 Pointers 573
Passing Addresses
Figures 9 - 17 and 9 - 18 demonstrate the use of pointers. In Figure 9 - 17 , we
call the exchange function, passing it two variables whose values arc to be
exchanged . When we use downward communication , the data are exchanged
in the called function, hut nothing changes in the calling program .
Rather than passing data to the called function , we need to pass
addresses ( pointers). Once we have a pointer to a variable, it doesn t make
’
// Function Declarations
void exchange ( int x , int y );
K a b
5 I 7
int main ( void )
No Change
{
int a = 5;
int b = 7;
exchange (a , b);
printf( " %d %d\n\ a , b );
return 0;
} // main
{
void exchange (int x , int y )
int temp;
lk i
i
5
x
l 7
y i
i
As Passed
temp = x;
x = y? 7 i
y = temp ; I X y i
return ;
} // exchange When Exchanged
// Function Declaration
void exchange ( int* , int* ); tk a b
X5
int main ( void )
{
int a = 5;
int b = 7 ;
temp = * px ;
^ x y
* px = * py ; temp
* py = temp;
return ; 5
} // exchange
In summary, when we need to send more than one value back from a
-
function , we use pointers. Using either upward or bi directional communica -
tion . we can change any number of variables .
Every time we want a called function to have access to a variable in the calling
function, we pass the address of that variable to the called function and use
the indirection operator to access it .
// Prototype Declarations
int* smaller ( int* pi , int* p2 ) a b
notice the error because the space was not reused , a large program would
either get the wrong answer or fail when the memory being referenced by the
pointer was changed .
/ / Local Declarations
int a;
int * p;
int * * q;
*P
**q
MU
Figure 9- 20 also shows how we use these concepts in a C code fragment .
All three references in the printf statements refer to the variable a . The first
print statement prints the value of the variable a directly; the second uses the
pointer p; the third uses the pointer q . The result is the value 58 printed
three times, as shown below.
58 58 58
EXAMPLE 9- 6 Pointer-to-Pointer
The last example in this section shows how we can use different pointers with
pointers to pointers and pointers to pointers to pointers to read the value ot
——
the same variable. A graphic representation of the variables is shown in
Figure 9 - 2 1 . The code is shown in Program 9-6.
o a
r q p a
Results:
Enter a number: 1
The number is : 1
Enter a number: 2
The number is : 2
Enter a number: 3
The number is : 3
Enter a number: 4
The number is : 4
Program 9 - 6 Analysis In each successive read we use a higher level of indirection. In the print statements,
owever, we a ways use the integer variable, a, to
prove that the reads were successful.
9.4 Compatibility
It is important to recognize that pointers
have a type associated with them.
They are not just pointer types, hut rather are
pointers to a specific type, sue
as character. Each pointer
therefore takes on the attributes of the type to
which it refers in addition to its own
attributes.
Chapter 9 Pointers 5 / 9
Results:
sizeof(c): 1 sizeof(pc): 4|sizeof(*pc): 1
sizeof(a): 4 sizeof( pa ): 4 | sizeof( * pa ): 4
sizeof( x ): 8 | sizeof( px ): 4 | sizeof( * px ): 8
Program 9 -7 Analysis What is this code telling us? First, note that the variables a, c, and x are never
assigned values. This means that the sizes are independent of whatever value may be
in a variable. In other words, the sizes are dependent on the type and not its values.
Now look at the size of the pointers. It is 4 in all cases, which is the size of an
address in the computer on which this program was run. This makes sense: All com-
puters today have more than 65,535 bytes, which is the maximum address that could
be stored in 2 bytes.
But note what happens when we print the size of the type that the pointer is refer -
ring to: The size is the same as the data size! This means that in addition to the size of
the pointer, the system also knows the size of whatever the pointer is pointing to. To
confirm this, look at the size of the pointer, px, and what it is pointing to when deref -
erenced ( * px ) .
The first pair of assignments are valid . In the first assignment, we store the
address of a character variable in a pointer to character variable. In the second
assignment , we store the address of an integer ( int ) variable in a pointer to an
integer ( int ) variable. We create an error in the third assignment because we
try to store the address of a character variable into a pointer variable whose
type is pointer to integer ( int ) . We also get an error in the fourth assignment.
Pointer to Void
The exception to the reference type compatibility rule is the pointer to void .
As we discussed in Chapter 2 , one of the types defined in C is void . Although
the void type defines the lack of any other type when used with functions, its
use with pointers is different . A pointer to void is a generic type that is not
associated with a reference type; that is, it is not the address of a character,
an integer, a real , or any other type. However, it is compatible for assignment
purposes only with all other pointer types. Thus, a pointer of any reference
type can be assigned to a pointer to void type and a pointer to void type can be
assigned to a pointer of any reference type . There is, however, one restriction ;
since a void pointer has no o bject tvpe , it cannot be dereferenced unless it is
cast ( see next section ). The following shows how we can declare a variable of
pointer to void type.
void * pVoid ;
A
582 Section 9.4 Compatibility
Casting Pointers
The problem of type incompatibility can be solved if we use casting. We can
make an explicit assignment between incompatible pointer types by using a
cast , just as we can cast an int to a char . For example, if we decided that we
needed to use the char pointer, pc in the previous example, to point to an int
( a ) , we could cast it as shown below.
pc = (char* )&a ;
But in this case, user beware! Unless we cast all operations that use pc ,
we have a great chance ol creating serious errors. In fact , with the exception
ol the void pointer, we should never cast a pointer. The following assignments
are all valid , but they are extremely dangerous and must be used with a very
carclully thought -out design .
// Local Declarations
void * pVoid ;
char* pChar ;
int* pint;
// Statements
pVoid = pChar ;
pint = pVoid ;
pint = (int* ) pChar ;
Another use ol the cast is to provide a type for the void pointer. As noted
in the previous section , a void pointer cannot be dereferenced because it has
.
no object type \ \ hen we cast it , however, we provide
the type .
Chapter 9 Pointers 583
ppa
h-H pa a
int
int
a;
b;
//
//
type
type
int
int tv
int* pa ; // type pointer to int
int** ppa ; // type pointer to pointer to int
pa = &a ; // Valid: same level
ppa = & pa ; // Valid: same level
b = ** pa ; // Valid: same level
pa = a; // Invalid: different level
ppa = pa ; // Invalid: different level
b = * ppa ; // Invalid: different level
All expressions that are not lvalue expressions are rvalues. The following
show some rvalue expressions:
5 a + 2 a * 6 a[2 ] + 3 a++
Expression Problem
a + 2 = 6; a + 2 is an rvalue and cannot be the left operand in an
assignment; it is a temporary value that does not have an
address; no place to store 6.
& (a + 2) ; a + 2 is an rvalue, and the address operator needs an
lvalue; rvalues are temporary values and do not have
addresses.
& 4; Same as above (4 is an rvalue) .
( a + 2 ) ++ ; Postfix and prefix operators require lvalues; (a + 2 ) is an
++ ( a + 2 ) ; rvalue.
a = b
Pointer Examples
In this section we demonstrate two ways we can use pointers when calling
functions.
Program 9 - 8 Analysis The first question you might ask when reading this simple function is "Why define a
local variable for the time?" In this short function, it really wasn't necessary. However,
a good programmer does not change a value parameter
within the function, because
its original value may be needed later. We have seen
times when "later" turned out to
be a maintenance change and hours were spent debugging the error when the
wrong value was computed.
Varia e 3 w en 3 va ue
ti
6
^ ^ parameter will be changed within a func-
*
n 50 that the original value will always be
.
available for processing
When several values need to be sent back to the calling function, use address
.
parameters for all of them Do not return one value and use address parame -
ters for the others .
program
2
a\ + b\ +c = 0
Four possible situations can occur when you solve lor the roots in a qua -
dratic equation. First, it is an error if both a and b are 0: 1 here is no solution.
Second, if a is 0 and b is not 0, there is only one root:
-c
x
b
2
x =
J
- b± b — 4ac
2a
function , pass both values and pointers to a compute function, and finally
pass the values to a print function.
main
a b c rootl root2 numRoots
trSniJ
pa
getData
pb pc
a
quadratic
b c
#n
pRootl pRoot2
LJ
a b c rootl root2 numRoots
printResults
(c) Calling printResults
continued
Chapter 9 Pointers 589
Results:
Solve quadratic equations.
Thank you.
Program 9-9 Analysis This problem has many interesting points. The function main is a test driver — that is,
code that will not be put into production . Therefore, we code much of the test logic in
mam rather than providing separate functions for it.
The scanf function used in getData does not have the usual address operator ( & )
in front of variables a, b, and c. Why? Because they are already pointer values point-
ing to variables in main and therefore are already addresses.
The variables in these examples are either integers or pointers to integers. Parame-
ters that receive something from main are integers whose values will be
filled when the
call is made. Those that send data back to main are pointers to integers that will be
filled with the addresses of the corresponding variables in main. As a general rule, if a
value will be changed, it must be passed as a pointer. If it will not be changed, it should
be passed as a value. This protects data from accidental destruction.
In quadratic, note the extensive testing to make sure that the coefficients are
valid. To ensure valid code, they are all necessary. Look at how we calculated the square
root of the discriminant separately ( statement 89) .
Since square root is a complex func-
tion, it is more efficient to call it just once and save the
value for later use. Note also that
the function has only one return statement. This is proper
structured code, though many
professional programmers would simply return at statements 77,
82, 92, and 95.
Study our test data carefully. Note that this set of test data executes
every line of
code in the program. Designing test data that completely a function is not an
validates
easy task. Ensuring that all code has been executed is even
more difficult and tedious.
One way to make sure that all code has been
tested is to use your debugger to set a
break point at every statement and then clear them as the all
break points have been cleared, you know every instructionprogram executes. When
has been executed.
Executing every line of code does not ensure that the
ever. With large programs, it is virtually
function has no bugs, how-
impossible to test every possible combination
of data. One of the advantages of structured
gram down into separate functions we can
programming is that by breaking the pro-
test it better.
This program has a potential problem. Do
you see it? Hint: What if the user enters
invalid data? There is no error checking in getData
. If this were a production pro-
gram, it would contain code to check for
errors. It would then return a status flag to
indicate if getData was successful or not.
Chapter 9 Pointers 593
Quality Defined
Quality software is defined as
Software that satisfies the user’s explicit and implicit requirements, is well
documented, meets the operating standards of the organization, and runs
efficiently on the hardware for which it was developed.
Every one of these attributes of good software falls squarely on you , the sys -
tem designer and programmer. Note that we place on you the burden ol satis -
fying not only the users ' explicit requirements, but also their implicit needs.
Often , users don 't fully know what they need . When this happens, it is your
job to determine their implicit requirements, which arc hidden in the back -
ground . This is a formidable task indeed .
Of course, it is also your job to document the software. If you are lucky,
you will have a technical writer to help, and even if you do, the final product
is still your responsibility. And as an analyst and programmer, you are
expected to know the standards of your organization and to implement them
properly.
Finally, it is your program , so you are responsible for its efficiency. This
means that you are expected to use appropriate and efficient algorithms. I bis
was the focus of our discussions in Chapters 6 and 8 when we talked about
analyzing algorithms and the big-O theory.
But quality software is not just a vague concept. If we want to attain it
,
we have to be able to measure it . Whenever possible, these measurements
should be quantitative; they should he numerically measurable. For example
,
, should he able to tell you the
if an organization is serious about quality it
number of errors ( bugs ) per thousand lines of code and the mean
time
between failures lor every software system it maintains
. I hese are measur-
able statistics.
r ~
594 Section 9.6 Software Engineering
Quality Factors
Software quality can be divided into three broad measures: operability, main-
tainability, and transferability. These measures can be broken down as shown
in Figure 9-26.
Software
Quality
Operability
Operability refers to the basic operation of a system . The first thing a user
notices about a system is its “ look and feel . ” This means, especially for an
online , interactive system , how easy and intuitive it is to use. Does it fit well
into the operating system it is running under? For example , if
it is running in
a Windows environment , its pull -down and
pop - up menus should work the
same way the operating system ’s menus do. In short , operability
answers the
question , " How does it drive ?”
But these factors are subjective; they are not measurable.
So let’s look at
the factors that comprise operability. I hey are listed
alphabetically.
Accuracy
A system that is not accurate is worse
than no system at all. Most workers
would rather rely on intuition and experience
than a system that they know
gives false and misleading information .
Am s \ stem that you develop, therefore ,
must be thoroughly tested , both
b \ you ( whitebox ) and by a systems
test engineer and the user ( blackbox ). If
you get the opportunity, take a course on
software testing.
Chapter 9 Pointers 595
Efficiency
Efficiency is, by and large, a subjective term . In some cases the user will
specify a performance standard , such as that a real - time response must be
received within 1 second , 95 % of the time . This is certainly measurable.
Reliability
Reliability is really the sum of the other factors. If users count on the system
to gettheir job done and are confident in it , then it is most likely reliable. On
the other hand , some measures speak directly to a system’s reliability, most
notably, mean time between failures.
Security
The security of a system refers to how easy it is for unauthorized persons to
access the system ’s data . Although this is a subjective area , there are check-
lists that assist in assessing the system ’s security. For example, does the sys-
tem have and require passwords ?
Timeliness
Does the system deliver its output in a timely fashion ? For online systems ,
does the response time satisfy the users’ requirements? For batch systems,
are the reports delivered in a timely fashion ? It is also possible, il the system
has good auditability, to determine if the data in the system are timely ; that is,
are data recorded within a reasonable time after the activity that creates them
takes place ?
Usability
Usability is also highly subjective. The best measure of usability is to watch
the users and see if they are using the system . User interviews will often
revea 1 problems with the usability of a system .
Maintainability
Maintainability refers to keeping a system running correctly and up to date.
Many systems require regular changes, not because they were poorly imple-
mented but because of changes in external factors. For example, the payroll
system for a company must be changed yearly, il not more often
, to meet
changes in government laws and regulations .
Changeability
The ease of changing a system is a subjective factor. Experienced project
will take. If it
leaders , however, can estimate how long a requested change
596 Section 9.6 Software Engineering
takes too long, it may well be because the system is difficult to change. This is
especially true of older systems.
Current software measurement tools estimate a program ’s complexity
and structure . These tools should be used regularly, and if a program ’s com-
plexity is high , we should consider rewriting the program . Programs that have
been changed many times may have often lost their structured focus and
become difficult to change. They also should be rewritten .
Correctability
One measure of correctability is mean time to recovery— how long it takes to
get a program back in operation when it fails. Although this is a reactive defi-
nition , there are currently no predictors of how long it will take to correct a
program before it fails .
Flexibility
Users are constantly requesting changes in systems. This qualitative attribute
attempts to measure how easy it is to make these changes. If a program
needs to be completely rewritten to effect a change, it is not flexible. Fortu -
nately, this factor became less of a problem with the advent of structured
programming.
Testability
You might think that testability is a highly subjective area , but a test engineer
has a checklist ol factors that can be used to assess a program ’s testability.
Transferability
Transferability refers to the ability to move data and /or a system from one
platform to another and to reuse code. In many situations, it is not an impor-
tant factor. On the other hand , il you are writing generalized software, it can
be critical.
Code Reusability
II ( unctions are written so that they can be reused in
different programs and
on different projects, then they are highly reusable.
Good programmers build
libraries of reusable functions that they can use when they need to solve a
similar problem .
Interoperability
Interoperability addresses the capability of sending data to other systems. In
todays highly integrated systems, interoperability
( act , it has become so is a desirable attribute. In
important that operating systems now support the abil-
ity to move data between applications,
such as between a word processor and
a spreadsheet.
Chapter 9 Pointers 597
Portability
Portability addresses the ability to move software from one hardware platform
to another; for example , from a Macintosh to a Windows environment or
Irom an IBM mainframe to a VAX environment .
While no one can deny that quality begins with the software engineers
: igned to the team , they need quality tools to develop a quality product.
assi
Fortunately, today’s development tools are excellent . A whole suite of quality
tools known as computer-assisted software engineering ( CASE ) guides soft -
ware development through requirements, design , programming and testing,
and into production . Programmers can use workstations that not only assist
in writing the program but also in testing and debugging. For example , some
CASE tools track tests through a program and then determine which state-
ments were executed and which were not . Tools such as these are invaluable
for whitebox testing.
Another major step in quality software is the technical review. T hese
reviews should be conducted at every step in the development process includ -
ing requirements , design , programming, and testing. A typical program review
598 Section 9.6 Software Engineering
begins after the programmer has designed the data structures and structure
chart for a program . A design review hoard consisting of the systems analyst ,
test engineer, user representative, and one or two peers is then convened .
Note that no one from management is allowed to attend a technical review.
During the review, the programmer explains the approach and discusses
interfaces to other programs while the reviewers ask questions and make
suggestions.
Quality also requires formal testing. Formal testing ensures that the pro-
grams work together as a system and meet the defined requirements. After
the programmer has completed unit testing, the program is turned over to
another software engineer for integration and system testing. On a small
project, this is most likely the systems analyst and /or the user. A large project
will have a separate testing team.
Large systems take months and sometimes years to develop. It is only
natural that , over extended periods of time, changes to the requirements and
design become necessary. To ensure quality, each change should he reviewed
and approved by a change control board . The impact of a change on each
program needs to be assessed and properly planned . Uncontrolled change
causes schedule and budget overruns and poor- quality products.
Finally, a good -quality environment measures all aspects of quality and
regularly reports the results. Without measurement , you cannot tell if quality
is good or bad , improving or deteriorating. At the same time , published stan -
dards provide the yardstick for many of the quality measurements.
Conclusion
In this short discussion , we have only introduced the concept of software qual-
ity. Consider these points as you design and program systems in the future. For
a more complete discussion of the subject , we recommend Chapter 12 in
Roger Pressmans Software Engineering , A Practitioner’s App roac Ji. 3
int* p;
int a ;
P = a; // ERROR
.
5 You must not use a pointer variable before it is assigned the address of a
variable. In other words, the following lines create an error unless p is
assigned an address:
int* p ;
x *p; // ERROR
*p = x; // ERROR
.
6 A pointer variable cannot be used to refer to a nonexistent pointed vari-
able. For example, the following lines create a run- time error because p .
exists, hut * p does not exist until it is assigned a value.
int* p ;
*p = ..
.
7 You cannot dereference a pointer variable of type void * .
8. Remember that the definition for a pointer variable allocates memory
only for the pointer variable, not for the variable to which it is pointing.
9. The address operator ( & ) must be used only with an lvalue.
.
10 A function that uses addresses as parameters needs a pointer to data as a
formal parameter; the actual parameter in the function call must be a
pointer value ( address ).
11. If you want a called function to change the value of a variable in the calling
function, you must pass the address of that variable to the called function.
12 When using multiple definitions in one statement —a practice we do not
.
recommend—the pointer token is recognized only with one variable .
Therefore, in the following definition, only the first variable is a pointer
to an integer; the rest are integers.
A
ir~n
600 Section 9.9 Summary
9.9 Summary
Every computer has addressable memory locations.
J A pointer is a derived data type consisting of addresses available in the
computer.
J A pointer constant is an address that exists by itself . We cannot change it.
We can only use it .
J I he address operator ( & ) makes a pointer value f rom a .
pointer constant To
get the address ol a variable, we simply use this operator in front ol the
variable name.
Ll I he address operator can only be used in
front of an lvalue. The result is
an rvalue .
A pointer variable is a variable that can store an
address.
Chapter 9 Pointers 601
^
Review Questions
.
1 Pointer constants are drawn from the set of addresses for a computer.
a. True
b. False
2. Pointers are declared using the address operator as a token.
.
a True
.
b False
602 Section 9.10 Practice Sets
int x ;
int* P = &x ;
int** pp = &p;
int i;
float f ;
int* pd ;
float* pf ;
Exercises
16. Declare and define the following:
a . A pointer variable pi pointing to an integer
b . A pointer variable ppi pointing to a pointer to an integer
.
c A pointer variable pf pointing to a float
d . A pointer variable ppc pointing to a pointer to a char
17. If a is declared as integer, which of the following statements is true and
which is false?
a . The expression * & a and a are the same.
b. The expression * & a and & * a are the same.
18. Given the following declarations:
int x;
double d;
int* p;
double* q;
int a = 5;
int b 7;
int * p = &a ;
int * q = & b;
c. int a ;
int* p = &a ;
d. int a;
int** q = &a ;
21 . \\ hich
ot the following program segments is valid ? Describe each error in
the invalid statements. 1'
a. int* p ;
scant("%d " , &p);
b. int* p;
scanf( " %d " , &* p );
c. int* p ;
scant( "%d " , * p);
d . int a ;
int* p = &a;
scant( "%d " , p );
22. Which ol the following program segments has a logical error in it ?
a. int** p ;
int* q ?
q = &P;
b. int** p;
int* q ;
p = &q ;
c. int** p;
int** q ;
p = &q ;
d. char c = ' A ’ ;
char** p ;
char* q ;
q = & c;
.
printf("%c " , * p);
23. Given the following declaration :
int*** p;
.
c &p
d, * p + 2
. i an array, which of the foi -
25 If p and q are pointer variable names and a ls
lowing expressions are not syntactically correct , because they violate the
rules concerning lvalues and rvalues?
a. *p = *p + 2 ;
b. &p = &a [ 0 ] ;
.
c q = &(p + 2);
.
d a [ 5 ] = 5;
26. Write a function prototype statement for a function named calc that
returns void and contains a reference parameter to iwt , x , and a reference
parameter to a lotto double , y .
27. Write a function prototype statement for a function named spin that
returns a pointer to an integer and contains a reference parameter to an
int , x , and a pointer parameter to the address of a long double , py .
28. Assuming all variables are integers, and all pointers are typed appropri-
ately, show the final values of the variables in Figure 9- 28 after the fol -
lowing assignments.
a p;
s * * p;
t p;
b = ** r ;
* *q = b;
HJ- R
a —a Q5
«
——
sQ
t
a
•
22
32
61
|
FIGURE 9- 28 Exercise 28
t = **p;
b = ***q;
*t * c ;
v = r;
w = *s;
a **v
*u = *w
PQ—
—
a 40
b 5 r
E3 Q s
c 30 t
1
191 18
[T
y
20
> I
VU w
FIGURE 9 - 29 Exercise 29
.
30 In the following program , show the configuration of all the variables and
the output.
# include <stdio.h>
int main (void )
{
// Local Declarations
int a;
int* p;
int** q ;
// Statements
a = 14 ;
p = &a ;
q = & p;
printf( " %d\n" , a);
printf( " %d\n " , * p);
printf( " %d\n ", * * q ) ;
printf( " % p\t" , p);
printfC % pM , q ) ;
return 0;
} // main
Problems
31. Write a function that converts a Julian date to a month
and day. A Julian
relative January 1 . For
date consists of a year and the day of the year to
608 Section 9.10 Practice Sets
example, day 41 is February' 10. The month and day are to he stored in
integer variables whose addresses are passed as parameters. The function
is to handle leap years. ( For a discussion of the calculation of a leap year,
see Chapter 5 , Project 60.) If there is an error in a parameter, such as a
day greater than 366, the function will return zero. Otherwise, it returns
a positive number.
.
32 Modify' Program 9 - 3 to include subtraction, multiplication, division, and
remainder.
.
33 Write a function that receives a floating-point number representing the
.
change from a purchase The function will pass hack the breakdown of the
change in dollar hills, half-dollars, quarters, dimes, nickels, and pennies.
.
34 Write a function that receives a floating-point number and sends hack
the integer and fraction parts.
35. Write a function that receives two integers and passes back the greatest
common divisor and the least common multiplier. The calculation of the
greatest common divisor can he done using Euclid’s method of repetitively
dividing one number by the other and using the remainder (modulo).
When the remainder is zero, the greatest common divisor has been
found. For example, the greatest common divisor of 247 and 39 is 13, as
.
in Table 9 - 4
Once you know the greatest common divisor ( ged), the least corn-
mon multiplier (1cm ) is determined as shown below.
Projects
.
36 \ \ iite a program that creates the structure
shown in Figure 9- 30 and
then reads an integer into variable a and
prints it using each pointer in
turn. I hat is, the program must
read an integer into variable a and print
it using p, q, r, s, t, u, and v.
.
37 Write a program that creates the
structure shown in Figure 9 - 31. It then
.
re ids data into a, b, and c
using the pointers p, q, and r .
Chapter 9 Pointers 609
P q
5)
a b
a c
After the data have been read, the program reassigns the pointers so
thatp points to c, q points to a, and r points to b. After making the reas-
signments, the program prints the variables using the pointers. For each
variable, print both its contents and its address.
38. Write a program that creates the structure shown in Figure 9 - 32 and
reads data into a and b using the pointers x and y.
The program then multiplies the value of a by b and stores the result
in c using the pointers x , y , and z . finally, it prints all three variables
using the pointers x , y , and z .
39. Write an exploratory program to study pointers. In this program , prompt
the user to enter a character, an integer, a long integer, and a floating-
point number. The program then calls a print function that accepts a void
pointer as its only parameter and prints the address of the pointer fol-
lowed by the data , first as a character, then as an integer, a long integer,
and a floating- point number. Since void pointers have no type , you will
need to cast the pointer appropriately in each of your print statements.
A proposed output format is shown below’. It is the result of calling
the print function w ith a pointer to integer. You will most likely get differ-
ent results. Call the print function for each of the user inputs, each time
printing the results shown below. (1 he first line may be printed before
calling the print function .)
Using the addresses that you printed , build a memory map of the
variables as they existed in your program. Then , by analyzing the results
of the printout , write a short explanation of what happened when your
program ran . To fully understand the results , you may have to refer to
your system documentation to determine the physical memory sizes of
the various types.
40. Assume an environment in which transactions and responses are being
sent across a network. When a transaction is sent , a time stamp is cre-
ated and attached to the transaction. When the response is received , the
time is noted . Using an integral time stamp, as shown in Program 9-8,
write a function to determine the elapsed time in hours, minutes, and
seconds between the transaction submittal and its response. Assume that
no transaction takes longer than 24 hours. Create a test driver to validate
your I unction . lest the function with at least five test cases, including
one that spans midnight and one that is longer
than 24 hours.
Chapter 10
Pointer Applications
In this chapter we explore two basic uses of pointers: with arrays and in
dynamic memory.
We begin with the use of pointers in implementing arrays. This discus -
sion includes the important topic of pointer arithmetic, which allows us to
process data in an array by adding or subtracting from pointer addresses.
Following a discussion of passing arrays to functions, we discuss one of
the most powerful aspects of C, dynamic memory. We examine two different
approaches to dynamic memory, static allocation and dynamic allocation.
We conclude with two applications, sorting with pointers and ragged
arrays.
Objectives
To understand the relationship between arrays and pointers
To understand the design and concepts behind pointer arithmetic
To write programs using arrays and pointer arithmetic
J To better understand the design behind passing arrays to functions
To understand the C implementation of dynamic memory
To write programs using static and dynamic memory allocation
To understand and implement ragged arrays ( arrays of pointers)
611
612 Section 10.1 Arrays and Pointers
a[ 0 ] [ a
a[ 1 ] [
a[ 2 ] [
The name of an array is a
a[ 3 ] [ pointer constant to its first
V element v
a[ 4 ] [
a
Since the array name is a pointer constant to the first element , the
address of the first element and the name of the array both represent the
same location in memory. We can , therefore, use the array name anywhere
we can use a pointer, as long as it is being used as an rvalue. Specifically, this
means that we can use it with the indirection operator. When we dereference
an array name, we are dereferencing the first element of the array; that is, we
are referring to array [ 01 . However, when the array name is dereferenced , it
refers only to the first element , not the whole array.
same
a - &3 [ 0 ] .
Prove this to yourself by writing a program with the code shown below.
1 he code prints the address of the first element of the array ( & a [ 0 ] ) and the
array name , which is a pointer constant . Note that we have used the format
conversion code for a pointer ( % p ).
We cannot tell you the values that this code will print , hut they will be
addresses in your computer. Furthermore, the first printed address ( the first
element in the array ) and the second printed address ( the array pointer) will
be the same , proving our point.
A simple variation on this code is to print the value in the first element
ol the array using both a pointer and an index. This code is demonstrated in
1 igure 10- 2 . Note that the same value, 2 , is printed in both cases , again prov-
ing our point that the array name is a pointer constant to the beginning ol
the array.
^
a[0] or * a ^x ^intinclude
{
main ( void )
int a[ 5 ] = {2 ,4,6 ,8 , 22};
a[ 0 ] 2 a printf( " %d %d " , *a, a[ 0]);
a[ 1 ] 4
return 0;
a[ 2 ] 6 } // main
a[ 3 ] 8
a[ 4 ] 22
2 2
a
#include <stdio.h>
int main ( void )
a[ 0 ] {
int a[5] = {2, 4, 6 , 8, 22 ) 2 2
a[ 1 ] int* p a; ^
a[ 2 ]
printf( "%d %d \n " , a[0 ), * p);
a[ 3 ]
a[ 4 ] return 0;
} // main
Right after we define and initialize the array, we define a pointer and ini -
tialize it to point to the first element of the array by assigning the array name.
Note especially that the array name is unqualified ; that is, there is no address
operator or index specification . We then print the first element in the array,
first using an index notation and then pointer notation .
Let’s look at another example that explores the close relationship
between an array and a pointer. We store the address of the second element
of the array in a pointer variable . Now we can use two dilferent names to
614 Section 10.2 Pointer Arithmetic and Arroys
accesseach element. This does not mean that we have two arrays; rather it .
shows that a single array can be accessed through different pointers.
include <stdio.h>
^
int main ( void )
{
int a[5 ] = {2, 4 , 6, 8 , 22};
a[ 0 ] int* p;
a[ 1 1
a[ 2 ] P = &a[ 1 ];
a[ 3 ] This is p[0] printf(" %d % d " , a [ 0 ] , p[-1 ]);
a[ 4 ] printf( " \ n " );
a printf( "%d %d " , a [ l ) , p [ 0 ] ) ;
} // main
Figure 10- 4 demonstrates the use of multiple names for an array to refer-
ence different locations at the same time. First, we have the array name. We
then create a pointer to integer and set it to the second element of the array
( a [ 1 ] ). Now, even though it is a pointer, we can use it as an array name and
index it to point to different elements in the array. We demonstrate this by
printing the first two elements using first the array name and then the
pointer. Note especially that , when a pointer is not referencing the first ele-
ment ol an array, the index can he negative [ - 1 ] . This is shown in the refer-
ence to p [ -1 ] .
To access an array, any pointer to the first element can be used instead of
the name of the array.
Irom a , and a + 3 is the address three elements from a . We can generalize the
notation as follows:
It does not matter how a and p are defined or initialized ; as long as they
are pointing to one of the elements of the array, we can add or subtract to get
the address ol the other elements of the array. This concept is portrayed in
Figure 10- 5 .
a 2 P “ 1
a + 1 4 P
a + 2 6 P + 1
a + 3 8 p + 2
a + 4 22 «
* p + 3
a
a+n
<
a 100 b 100
a +1 101
a+2 102
b+1 104
memory
addresses
b+2 108
char a[3];
int b[3];
fV
float c[ 3];
We ’ve seen how to get the address of an array element using a pointer
and an offset , now let ’s sec how we can use that value . We have two choices:
First , we can assign it to another pointer. This is a rather elementary opera -
tion that uses the assignment operator as shown below.
p = aryName + 5 ;
Second , we can use it with the indirection operator to access or change the
value ol the element we are pointing to. This possibility is seen in Figure 10-7.
a[0J or * ( a + 0 ) a
a[1 ] or *(a + 1 ) a + 1
a[2l or * ( a + 2 ) a + 2
a [ 3] or *(a + 3) a + 3
a[4] or *(a + 4 ) a + 4
lor practice, lets use pointers to find the smallest number among
integers stored in an array. Figure 10- 8 tracks the code as it works its way
through the array.
Chapter 10 Pointer Applications 617
vm
The following expressions are identical .
* (a + n) and a [ n ]
We start with the smallest pointer (pSm) set to the first element of the
array. T he functions job is to see if any of the remaining elements are smaller.
Since we know that the first element is not smaller than itself , we set the
working pointer ( pWalk) to the second element. The working pointer
advances through the remaining elements, each time checking the element it
is currently addressing against the smallest to that point ( pSm). If the current
element is smaller, its location is assigned to pSm.
p+ 5 5 + p P - 5 pl - p2 p++ —p
When one pointer is subtracted from another, the result is an index rep-
resenting the number of elements between the two pointers. Note, however,
that the result is meaningful only if the two pointers are associated with the
same array structure.
The relational operators ( such as less than and equal ) are allowed only il
both operands are pointers of the same tvpe . Two pointer relational expres-
sions are shown below.
p i >= p2 p i != P2
~~
22
^ pLast
After initialization
ary 3? ary-
PSm pSm
^QpWalk u ^ rjPWalk
22 22
20 20
r* pLast - Fj PLast
After first iteration After second iteration
TT-
22
pWalk
nz / m
22
pWalk
20 pLast
PLast
After third iteration After fourth iteration
pLast = ary + arySize - 1?
for ( pSm = ary , pWalk = ary + 1 ;
pWalk <= pLast ;
pWalk+t)
if ( * pWalk < *pSm)
pSm = pWalk ;
f
Using Pointer Arithmetic
In this section, we write two small programs that demonstrate moving
through arrays using pointers .
Printing an Array
Program 10- 1 prints an array, first by adding I to advance through the array
and then subtracting 1 to print it backward.
Results:
Array forward : 1 2 3 4 5 6 7 8 9 1 0
Array backward: 1 0 9 8 7 6 5 4 3 2 1
Program 10 - 1 Analysis While this program is quite simple, it does demonstrate one efficiency point. Note
that before we printed the forward loop, we set pEnd . Many programmers would
use the following test in the limit condition:
While this logic works, some compilers will calculate the end address in
each loop, an obvious inefficiency. We recommend, therefore, that whenever
possible you calculate the end pointer in the loop initialization.
620 Section 10.2 Pointer Arithmetic and Arrays
Since we cannot use addition with two pointers, this formula will not
work. We need to come up with the pointer arithmetic equivalent . Another
formula determines the midpoint in an array by calculating the number of
elements from the beginning of the array. I bis method , known as the offset
method , is shown below.
continuei
Program 10 - 2 Analysis Although the code in this function is relatively simple, the coding for locnPtr merits
some discussion. In the calling function, locnPtr is a pointer to the found location.
To store the pointer in locnPtr, therefore, we need to pass a pointer to a pointer to
an integer ( see statement 12). To create a pointer to pointer, the calling function must
pass the address of its location pointer.
In addition to demonstrating the subtraction of two pointers, we also see the use of
a relational operator with two pointers in statement 22.
*(*( table) )
table ] [
table [0] or * ( table + 0 )
3
table+1 ( [
table [ 1 ] or * ( table + 1 )
table+2
](
table[2 ] or * ( table + 2 )
int table[ 3 ][ 4 ] ;
Print Table
*(*( table + i ) + j)
u
Chapter 10 Pointer Applications 623
dolt (aryName );
The called program can declare the array in one of two ways. I irst , it can
use the traditional array notation . This format has the advantage of telling the
user very clearly that we are dealing with an array rather than a single pointer.
This is an advantage from a structured programming and human engineering
point of view.
We can also declare the array in the function header as a simple pointer.
The disadvantage to this format is that , while it is technically correct , it actu -
ally masks the data structure ( array ). For one-dimensional arrays , it is the
code of choice among professional programmers.
If you choose to code this way, use a good descriptive name for the
parameter to minimize any reader confusion. The function documentation
should also indicate clearly that an array is being passed.
Note, however, that when we pass a multidimensional array, we must use
the array" syntax in the header declaration and definition . The compiler needs
the offset for
to know the size of the dimensions after the first to calculate
pointer arithmetic . Thus , to receive a three- dimensional array, we would use
the following declaration in the function’s header:
To see how passing array pointers works, let ’s write a program that calls
a function to multiply each element of a one-dimensional array by 2. The
program s variables are shown in Figure 10- 10 , and the code is shown in
Program 10 - 3.
main
multiply
5 |
pAry pWalk pLast size
1
25
26 multiply ( ary , SIZE);
27
28 printf ( "Doubled value is: \n " );
29 for ( pWalk = ary ; pWalk < = pLast ; pWalk++ )
30 printf ( " % 3d " , *pWalk );
31
32 return 0;
33 > // main
34
35
36 Multiply elements in an array by 2
37 Pre array has been filled
38 size indicates number of elements in array
39 Post values in array doubled
40 */
41 void multiply ( int* pAry , int size )
42 {
43 // Local Declarations
44 int* pWalk ;
45 int * pLast ;
46
47 // Statements
48 pLast = pAry + size 1; -
49 for ( pWalk = pAry ; pWalk <= pLast ; pWalk++ )
50 *pWalk = *pWalk * 2;
51 return ;
52 } // multiply
53 ===== End of Program ====
Results:
Please enter an integer: 1
Please enter an integer: 2
Please enter an integer: 3
Please enter an integer: 4
Please enter an integer: 5
Doubled value is:
2 4 6 8 10
Program 10 - 3 Analysis This program contains several points of interest. First, we have declared the array in
have
the function declaration using the more common pointer notation, but we
given
( ).
it a name that indicates it is a pointer to an array pAry
626 Section 10.3 Passing an Array to a Function
In the multiply function, we use a separate pointer ( pWalk) to walk through the list.
We could have used pAry, since it was not being used other than to identify the begin-
ning of the array. All too often, however, this type of " shortcut" saves a line or two of
code only to create hours of debugging when the program is changed later. As we
have pointed out before, do not use formal parameters as variables unless their intent is
to change a value in the calling program. This rule is especially important when the
parameter is a pointer, as in this case.
Finally, note that we have passed the size of the array to the multiply function. We
still need to know how much data we need to process, and we use the size to calculate
the address of the last element in the list. As a variation on the limit test, however, we
could have passed a pointer to the last element of the array, &ary [ SIZE - 1 ] . This
would save the calculation of pLast. From a style and efficiency point of view, neither
method has an advantage over the other. The structure and needs of other parts of the
program usually dictate which method is used.
identifier
t t t t t t3 t
6 4 2 start here 1 5
int ;
T t T T
4 2 0 1 3
Note that we keep going right even when there
is nothing there until all the entities on
_
the left have been exhausted. For a more
complete discussion of complex declara-
tions, see Appendix L.
L
Chapter 10 Pointer Applications 627
^
Memory
Allocation
Static Dynamic
Memory Usage
To understand how dynamic memory allocation works, we must study how
memory is used. Conceptually, memory is divided into program memory and
.
data memory Program memory consists of the memory used for main and all
.
called functions Data memory consists of permanent definitions, such as glo -
bal data and constants, local declarations, and dynamic data memory Exactly .
how C handles these different needs is a function of the operating system and
the compiler writer ’s skills. We can, however, generalize the concepts.
.
Obviously, main must he in memory at all times Beyond main, each
called function must he in memory only while it or any ol its called functions
are active. As a practical matter, most systems keep all functions in memory
while the program is running.
Although the program code for a function may he in memory at all
times, the local variables for the function are available only when it is active .
Furthermore, more than one version of the function can he active at a time.
( See the discussion of recursion in Chapter 6.) In this case, multiple copies
of the local variables arc allocated, although only one copy of the function is
.
present The memory facility for these capabilities is known as
the stack
memory.
main function
Program Memory
Data Memory
Memory
pointer.
We can refer to memory allocated in the heap only through a
// Local Declarations
ary
// Local Declarations
int x; x int ary[ 3 );
Stack Stack
(a) Static Memory Allocation
tv
// Local Declarations // Local Declarations
int* x ; int* ary ;
x = malloc(...); ary = calloc(...);
X
I
Stack Stack
(b) Dynamic Memory Allocation
Memory
Management
As mentioned above, malloc returns the address ol the first byte in the
memory space allocated. However, if it is not successful , malloc returns a
NULL pointer. An attempt to allocate memory from the heap when memory
is insufficient is known as overflow. It is up to the program to check for mem-
ory overflow. If it doesn’t , the program produces invalid results or aborts with
an invalid address the first time the pointer is used .
Exactly what action should he taken when memory overflow is encoun -
tered is application dependent . If memory might he released by another por-
tion of the program , the memory request can be held. Generally, however, the
programmer must terminate the program and allocate more memory' to
the heap.
The malloc function has one more potential error. If we call malloc with
a zero size, the results are unpredictable. It may return a N U L L pointer, or it
may return some other implementation -dependent value . Never call malloc
with a zero size .
Figure 10- 1 5 shows a typical malloc call. In this example, we are allocat -
ing one integer object. II the memory is allocated successfully, ptr containsa
value. II it doesn t , there is no memory and we exit the program with error
'
code 100.
Stack
__
void *calloc ( size t element-count ,
size t element size ); _
I he result is the same for both mailoc and calloc when overflow occurs
and when a zero size is given.
A sample calloc call is shown in Figure 10- 16. In this example, we allo-
cate memory for an array of 200 integers.
Ptr
P GE
if (!( ptr = ( int*)calloc ( 200, sizeof( int))))
// No memory available
exit ( 100) ;
_
void* realloc ( void * ptr , size t newSize);
Before
GL
ptr
1181551331121|64 | 1 |90 | 3l [ 5 177
10 integers
new elements
.
.not initialized
ptr
18155133|l 21 641 1 190| 31 | 5 771 ? ? ? ? | ?|
15 integers
After
FIGURE 10 - 17 realloc
Figure 10- 18 shows two examples. The first one releases a single element, allo-
cated with a malloc , hack to the heap. In the second example, the 200 elements
were allocated with calloc . When we free the pointer in this case, all 200 elements
arc returned to the heap. You should note two things in this figure. First , it is not
the pointers that are being released hut rather what they point to. Second, to
release an array of memory that was allocated by calloc , you need only release the
I
pointer once. It is an error to attempt to release each element individually.
BEFORE AFTER
ptr
a-ptr
free ( p t r ) ;
BEFORE AFTER
QH i i ITH T \wmm\
~
... mi
ptr 200 integers ptr 200 integers
free ( p t r ) ;
Releasing memory does not change the value iin a pointer. It still contains
the address in the heap. It is a look error to use
the pointer after memory has
'1
Chapter 10 Pointer Applications 633
been released . The program may continue to run , but the data may be
destroyed it the memory area is allocated for another use. This logic error is
very difficult to trace . We suggest that immediately after you free memory you
also clear the pointer by setting it to NULL .
Using a pointer after its memory has been released is a common program -
.
ming error Guard against it by clearing the pointer.
The pointer used to free memory must be of the same type as the pointer
used to allocate the memory .
32 18 12 24
13 11 16 12 42 19 14
22
13 13 14
11 18
If we use a two dimensional array for storing these numbers, we are wast -
-
ing a lot of memory. The solution in this case -
is to create five one dimensional
arrays that are joined through an array of pointers. One implementation
of
this concept is seen in Figure 10- 19 along with the statements needed to allo -
heap. Note that table is a pointer to a pointer to an
cate the arrays in the
integer and must be declared as shown below, not as an
array.
int * * table
634 Section 10.6 Programming Applications
table [ 0 ]
table [ 1 ]
table [ 2 ]
table [ 3 ]
table [ 4 ]
table [ 5 ]
Selection Sort
(pointers)
smallest exchange
Program 10- 4 Analysis Here are a few points you should note as you study this program. Note that we have
used pointers and pointer arithmetic in all functions.
The getData function fills the array. Since the pointer, pFill , is always one
ahead of the read, when we reach the end of file, it is pointing to an empty element.
Therefore, when we return it we subtract 1.
Sf ^ , it selects
^*
< rt nc on advances through the array using a for statement. For
eachL iteration eCfS
? the smallest
' element in the unsorted portion of the array and
,
exchanges t with the first element in the unsorted portion of the . Each loop, there-
array
hpfnrp . ?m[ nes ° Tecousnum^erGr unordered elements. We stop at the element just
- - *• -
® smaHest always
tests the first element and at least one
±TotlXS
element
1
*
Finally, the style used to code the for statement in both smallest and
note
,
separate tine ^ StaterTlen s are more readable if you put each expression on a
V
T1J
Chapter 10 Pointer Applications 639
Dynamic Array
I his program creates a dynamic table that can store a ragged array. The col -
umn and the width of the dynamic array are tailored to the needs of the user.
1 he program starts by asking the user for the number of rows that must he
stored. After allocating the row pointers (using culloc ) the program asks for
y
the number ol entries in each row. The table is then filled with data supplied
.
by the user from the keyboard To demonstrate the applications that could be
used with this type of structure, we then determine the minimum, maximum,
and average ol each row oi data. The design is shown in Figure 10-2 I .
Dynamic
Arrays i
build
Table i fill
Table i process
Table
row
Mimimum i row
Maximum
row
Average i
smaller j larger
The data structure is shown in Figure 10 - 22. The table pointer points to
the first pointer in an array of pointers. Each array pointer points to a second
array of integers, the first element of which is the number of elements in the
list . All arrays arc allocated out of the heap, giving us a structure that is lim-
ited only by the computer s memory.
3 12 3 45
table
5 10 6 21 78 61
K 4 2 31 40 70
The complete set of programs to build and fill the table and some sample
applications are shown in Programs 10- 5 through 10 - 13. To
compile these
need he put in one source file or combined using include
programs , they to
statements .
640 Section 10.6 Programming Applications
L
4 Pre nothing
continue1
Chapter 10 Pointer Applications 641
We begin Program 10- 6 by asking the user how many rows of data must be entered
.
Program 10 - 6 Analysis plus
Using the calloc function, we then allocate the memory for an array of pointers
one extra pointer at the end. Each entry in the allocated table is
used to point to an
array of integers, also stored in the heap .
Program 10 - 7 Analysis Filling the rows, Program 10 - 7 requires a while statement to loop through the
array
pointers and a for statement to enter the data. We use the while statement because
the pointer array is designed with a null pointer at the end, and we use it to tell we
are at the end of the array. We use the for statement for filling the row because the
user has already told us how many elements are in each row.
L continue
Chapter 10 Pointer Applications 643
Program 10 - 8 Analysis Processing the table in Program 10- 8 calls three functions to show how you could use
a dynamic structure such as this . Obviously, many more applications could be used.
Remember the structure of this example. You will be able to use this structure in future
applications. Programs 10- 9 through 10- 13 continue the code for the functions.
*( *( ary + i) + j) or a [i1 [ j 1
a + i i + a
a[ i] is identical to i[ a ]
Program 10- 14 Analysis First look at the logic of this simple program. It loops five times, each time allocating
16 bytes and immediately freeing them . The program was run on two different sys-
tems, a personal computer and a UNIX network.
If the memory management is doing its job, the same 16 bytes would be allocated
each time. What we see, however, is that for the personal computer, each address is
different. It is also interesting to note that the difference between all the allocations
except the first was 18 bytes, not 16.
The larger UNIX network system is more sophisticated, and it appears to reuse
dynamic memory efficiently.
Chapter 10 Pointer Applications 649
.
3 Remember that we usually pass the name of an array as a pointer value to
a function that needs to access the elements of the array.
4 . Remember that
.
5 Similar to array indexes, the most common pointer error is referencing a
nonexistent element in an array. This is especially easy to do with pointer
arithmetic.
6. It is a compile error to use pointer arithmetic with a pointer that does not
reference an array.
.
7 It is a logic error to subtract two pointers that are referencing dillerent
arrays.
8. It is a compile error to subtract a pointer from an index.
9. It is a compile error to attempt to modify the name of an array using
pointer arithmetic , such as
10. The header file stdlib.h is required when using memory allocation functions.
11 . It is a compile error to assign the return value from malloc or calloc
to
anything other than a pointer.
12. It is a logic error to set a pointer to the heap to NULL before the memory
has been released.
13. It is a compile error to use pointer arithmetic with multiply
, divide , or
modulo operators.
650 Section 10.10 Summary
10.10 Summary
Arrays and pointers have a close relationship. The name of an array is a
pointer constant to the first clement of the array.
The name of an array and the address of the first element in the array rep-
resent the same thing: an rvalue pointer.
The name of an array is a pointer only to the first element , not the
whole array.
A pointer variable to the first element of an array can be used anywhere
the name of the array is permitted , such as with an index.
In pointer arithmetic, if ptr is pointing to a specific element in an array,
ptr + n is the pointer value n elements away.
LI I he name of a two-dimensional array is a pointer to a one-dimensional
—
array the first row.
-J In a multidimensional array, the following two expressions arc equivalent .
*( *( a + i ) + j ) a[ i][ j]
J We can pass an array to a function in many ways. One way is to pass the
name of the array as a pointer.
J A
— —
ragged array that is, an array of pointers can be used to save space
when not all rows of the array are full .
I he memory in a computer can be divided into program memory and data
memory. Data memory can be partitioned into global area , heap, and stack.
Q Static allocation ol memory
requires that the declaration and definition of
memory be I ully specified at compilation time.
J Dynamic allocation of memory is done during
run time through the use of
predefined functions.
J C has lour predefined memory allocation functions: malloc , calloc , realloc ,
an d free .
J lo read and interpret a complex declaration , we can use the right-left rule.
10.11 Practice Sets
Chapter 10 Pointer Applications 651
^
T |
Review Questions
1. Given a pointer to an array element , ptr, ptr - 5 is a pointer to the
value 5 elements toward the beginning of the array.
a . True
.
b False
2. Adding 1 to a pointer increases the address stored in it by 1 byte.
a . True
b. False
3. 1 he parameter declaration int * a can be used to declare an array of
integers passed to a function.
a . True
b. False
4 . Dynamically allocated memory can only be referred to through pointers.
a. True
.
b False
5. Which of the following statements about pointers and arrays is true?
.
a The following expressions are identical when ary is an array: * ary
and &ary[ 0 ] .
b. The following expressions are identical when ary is an array: *ary
and *ary[0].
c The name of an array can be used with the indirection operator to ref -
.
erence data .
.
d The name of the array is a pointer variable.
e. The only way to reference data in an array is with the index operator.
6. Which of the following statements about pointer arithmetic is true ?
a Any arithmetic operator can be used to change the value ol a pointer.
.
b Given a pointer ptr, ptr + n is a pointer to the value u elements away.
.
-
c. Pointer arithmetic is a short hand notation that changes the value that
a pointer is referencing.
d Pointer arithmetic is valid only with pointers to arithmetic variables,
.
such as pointer to integer.
e Pointer arithmetic is valid only with the name of the array.
.
?
7. Which of the following parameters is a two-dimensional array of integers
.
a int ary
.
b int ary[ ][SIZE2 ]
c. int* ary
d . int* ary[ ][SIZE2]
.
e int* ary [ SIZE2 ]
652 SectionlO . il Practice Sets
Exercises
13. Rewrite each ot the following expressions by replacing the index operator
( [ . . . ] ) with the indirection operator ( * ) .
a. tax [ 6 ]
b. score [ 7 ]
.
c num [ 4 ]
d . prices [ 9 ]
14. Rewrite each ot the following expressions by replacing the indirection
operator ( * ) with the index operator ( [ . . . ] ). Each identifier refers to
an array.
.
a * ( tax + 4 )
b. * ( score + 2 )
.
c * ( number + 0 )
d. * prices
1 5 . Imagine you have the following declarations:
int ary[10];
int* p = &ary[3 ];
Show how you can access the sixth element of ary using the pointer p.
.
16 Given the following declarations:
float ary[200];
Write the function declaration for a function named fun that can manip-
ulate a one-dimensional array of floating- point numbers, ary . Provide an
additional parameter, a pointer to the last element in the array. Code a
call to the function .
.
1 7 Show what would be printed from the following block:
{
int num[ 5] = { 3, 4, 6, 2 , 1 > ;
int* p = num ;
int* q = num +2 ;
int* r = &num[ 1 ];
printf( " \n%d %d ", num[ 2], *( num + 2 ));
printf( "\n%d %d " , *p, *(P + 1 ));
printf( "\n%d %d " , *q , *( q + 1 )),*
printf( "\n %d %d " , *r, *(r + 1 ));
} // Block
654 Section 10.11 Practice Sets
// Function Declaration
void printOne ( int* );
void printTwo ( int* );
void printThree (int* );
{
// Local Declarations
int num [ 5 ] = {3 , 4 , 6 , 2 , 1};
// Statements
printOne ( num );
printTwo ( num + 2 );
printThree (&num [2 ]);
return 0 ;
> // Block
void printOne ( int* x )
{
printf( " \n%d " , x[ 2]);
return ;
} // printOne
void printTwo ( int* x )
{
printf( " \n %d " , x[2 ]);
return ;
> // printTwo
void printThree ( int* x )
{
printf( " \n %d " , * x ) ;
return ;
} // printThree
write the function declaration for a function named fun that can accept the
whole array using a pointer. Write the statement that calls this function.
20. Given the following definition:
{
// Local Declarations
int x [ 2 ][ 3 ] =
{
{ 4 , 5 , 2 > ,
{ 7 , 6 , 9 }
>;
int (*p) [3] = &x [1];
int (*q ) [3] = x;
// Statements
printf( "\n%d %d %d " , ( * P )[ 0 ],( * p )[ l ],( * p )[ 2 ] );
printf( " \n%d %d " , * q [ o ], * q [ i 1 );
} // Block
{
// Local Declarations
int x [2][3] = {
{ 4 , 5 , 2 } ,
{ 7 , 6 , 9 }
>;
// Statements
fun (x);
fun (x + 1 );
return 0;
} // Block
void fun ( int ( * p )[ 3 J )
{
printf( " \n%d %d %d " , ( * P )[ 0 ],( * p )[ l ] ( * p )[ 2 ] );
/
return ;
>
24. Given the following definitions:
.
26 Given the following definition:
i n t d a t a [ 15 ] = { 5, 2 , 3 , 4 , 1, 3, 7 , 2 , 4 , 3, 2 , 9 , 1 2 };
int i = 2;
int j = 4;
int* pi = & i;
i n t* pj = & j ;
write pointer expressions that evaluate to the same value as each of the
following:
a. a [ 0 ]
b. a [ 5 ]
c . The address ol the element just
before a [ 0 ] .
mm
'
int j = 4 ;
write index expressions that evaluate to the same value as each oi the
following:
a. *( num + 2)
b. *(num + i + j)
c. *( num + *( num + 1 ))
d. * ( num + j)
e. *( num + i) + *( num + j)
.
30 Given the following definition :
.
33 Given the following function for mushem and the definitions shown below
.
34 What is the output from the following program?
#include <stdio.h>
int fun ( int* , int , int* );
int main ( void )
{
// Local Declarations
int a = 4 ;
int b = 17 ;
int c[5] = {9 , 14 , 3, 15, 6 > ;
// Statements
a = fun(&a , b, c );
printf( " 2. %d %d %d %d %d %d %d \n" ,
a , b, c[0], c[ l ], c[2], c(3], c[4]);
return 0;
> // main
int fun (int* px , int y , int* pz )
{
// Local Declarations
int a = 5;
int* p;
// Statements
printf( " 1. %d %d %d \n" , * px, y , * pz ;
)
for ( p = pz; P < pz + 5. ++p )
*p = a + *p;
return (* px + * pz + y );
> // fun
.
35 \ \ hat is the output Irom the
following program?
# include <stdio.h>
int sun ( int* , int , int* );
continued
Til
Chapter 10 Pointer Applications 659
Problems
36. We have two arrays, A and B, each containing 10 integers. Write a Function
that checks if every element of array A is equal to its corresponding element
in array B. In other words, the function must check if A[0 ] is equal to B[ 0 ] ,
A[ 1 ] is equal to B[ 1 ] , and so on. The function must accept only two pointer
values and return a Boolean, true for equal and false for unequal.
.
37 Generalize the function in Problem 36 to include the number of ele -
ments to be compared as a parameter.
,
wr 660 SectionlO. il Practice Sets
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
*
Your function must be able to create the triangle of any size . The func-
tion should accept an integer representing the size of the triangle and
return a pointer to the array it created .
0-07-881809- :
i
Country Book Number
;
I he function must accept a pointer value ( the name of the array ) and
return a Boolean , true for valid and false for invalid .
— —
If j and k are not factors of n that is , if n j * k the function returns a
*
null pointer. Otherwise , it returns the pointer to the two-dimensional
i array. I he input array and j, k , and n will he passed as parameters.
i i r
n
. .
42 C iven the following definition:
int num[20];
Chapter 10 Pointer Applications 661
1
and using only pointer notation, write a for loop to read integer values
from the keyboard to fill the array.
.
43 Given the following definition
char a[ 40];
and using only pointer notation, write a for loop to read characters from
the keyboard to fill the array.
.
44 Given the following declaration and definition:
char a[6] = {'z', ' x ' , 'm', 's', e ' , ' h' };
and using only pointer notation, write a loop to rotate all values in a to
the right ( toward the end) by one element.
.
43 Repeat Problem 44, with the rotation one element to the left.
. -
46 Write a function named addem with two call-by address integer parame -
ters. The function will add 2 to the first parameter and 5 to the second
parameter. Test the function by calling it with the values of I 7 and 23 . i
Projects
.
47 Write a program that reads integers from the keyboard and place them in
.
an array The program then will sort the array into ascending and
descending order and print the sorted lists. The program must not
change the original array or create any other integer arrays.
The solution to this problem requires two pointer arrays as shown in
the example in Figure 10- 24. The first pointer array is rearranged so that
it points to the data in ascending sequence. The second pointer array is
rearranged so that it points to the data in descending sequence.
26
14 14
57
#
-
33
41
C
-/ >
^
—
41
Ascending Descending
Before Sorting After Sorting
26 14 41
33 57 33
41 33 26
57 41 14
.
48 Write a program that creates a two-dimensional array in the heap and
then analyzes it to determine the minimum, maximum, and average of
each column.
The data are to he read from a file. The first two elements are the
number of rows in the array and the number ol columns in each row. The
file data for a 1 2 x 8 array are shown below.
12 8
838 758 113 515 51 627 10 419
49 Rewrite the straight insertion sort from Chapter 8 using pointer arith -
.
.
metic The data to he sorted are to he read from a file. I he array is
Chapter 10 Pointer Applications 663
dynamically allocated in the heap after reading the file to determine the
number of elements. While reading the data to determine the size of
array you will require, print them 10 integers to a line. Use the test data
shown below.
The data will he sorted as they are read into the array. Do not fill the array
and then sort the data . After the array has been sorted , print the data
again using the same format you used for the unsorted data .
50. Write a program to answer inquiries about student data . Using a menu -
driven user interface, provide the capability to print out the scores, aver-
age, or grade lor a student . A fourth menu option is to provide all infor-
mation about a given student . All array functions are to receive the array
as a pointer and use pointer arithmetic.
The data in Table 10- 4 will he stored in a two-dimensional array.
1234 52 7 100 78 34
1947 45 40 88 78 55
2134 90 36 90 77 30
2877 55 50 99 78 80
3124 100 45 20 90 70
3189 22 70 100 78 77
4532 11 17 81 32 77
4602 89 50 91 78 60
continued
TABLE 10 - 4 Student Data for Project 50
664 SectionlO.il Practice Sets
.
51 Contract bridge is a popular card game played by millions of people
throughout the world. It began in the 1920 s as a variation of an old
.
English card game, whist In bridge, the entire deck is dealt to four play-
ers named North, South, East, and West. In tournament bridge, teams of
players (North- South versus East - West ) compete with other players using
.
the same hands ( sets of 13 cards) Today it is common for large tourna-
ments to use computer-generated hands. Write a program to shuffle and
deal the hands for one game.
To simulate the bridge deck, use an array of 52 integers initialized
from 1 to 52. To shuffle the deck, loop through the array, exchanging the
current card element with a random element ( use the random number
generator discussed in Chapter 4 ) . After the deck has been shuffled,
print the hands in four columns using the player ’s position as a heading.
Use the following interpretation for the cards suits:
1 to 13 Clubs
14 to 26 Diamonds
27 to 39 Hearts
40 to 52 Spades
To determine the rank of the card, use its number (modulo 13 ) + l. The
interpretation of the rank follows: 1 is an ace, 2 through 10 have their
value, 1 1 is a jack, 12 is a queen, and 13 is a king.
.
the h in M
n turn
p
^*K
’ ^^
13 cards tIle or North, the next 13 cards
*
Then use a Unction to sort each hand
II
'
Chapter 11
Strings
II is impossible to write a well- structured and human-engineered program
without using strings. Although you probably weren’t aware of it, you have
been using strings ever since you wrote your first C program .
Some languages, such as Pascal, Ada, and C++ provide intrinsic string
types, hut C does not In C, the programmer implements strings. Because
.
strings are so important, however, functions to manipulate them have been
defined in an ad hoc standard library.
In this chapter we first consider how strings are defined and stored, and
then we explore the standard string f unctions that are available in C.
Objectives
To understand design concepts for fixed -length and variable- length strings
To understand the design implementation for C - language delimited strings
To write programs that read, write, and manipulate strings
To write programs that use the string functions in the string library
IJ To write programs that use arrays of strings
To write programs that parse a string into separate variables
To understand the software engineering concepts of information hiding and
cohesion
665
666 Section 11.1 String Concepts
string
fixed
length
i variable
length
length delimited
controlled
C Strings
.
I The shortest name that we are aware of is (). To accommodate the computers of credit card
and other companies, however, Mr. () was Forced to .
legally change his name to Oh
Chapter 11 Strings 667
Length-Controlled Strings
Length-controlled strings add a count that specifies the number ol charac -
ters in the string. I his count is then used by the string manipulation functions
to determine the actual length of the data.
Delimited Strings
Another technique used to identify the end of the string is the delimiter at
the ends of delimited strings. You are already familiar with the concept of
delimiters, although you probably don’t recognize them as such. In English,
each sentence, which is a variable-length string, ends with a delimiter, the
period. Commas, semicolons, colons, and dashes are other common delimit -
ers found in English.
I he major disadvantage of the delimiter is that it eliminates one charac -
ter from being used for data. The most common delimiter is the ASCII null
character, which is the first character in the ASCII character sequence ( \ 0 ) .
This is the technique used by C.
Figure 1 1 - 2 shows length-controlled and delimited strings in memory.
length delimiter
9 C O N F U C I U S B 0 O L E \0
11.2 C Strings
is delimited by the null
A C string is a variable-length array of characters that
character. Generally, string character s are selected only from the printable
, any character , other than the
character set . Nothing in C however , prevents
fact quite common to use
null delimiter, from being used in a string. In is
, it
formatting characters, such as tabs , in strings.
Storing Strings
In C , a string is stored in an array of characters. It is terminated by the null
character ( ' \ 0 • ) • Figure 1 1 -3 shows how a string is stored in memory. What
precedes the string and what follows it is not important. What is important is
that the string is stored in an array of characters that ends with a null delim-
iter. Because a string is stored in an array, the name of the string is a pointer
to the beginning of the string.
H e I I o \0
H H \0 \0
the end lets us treat the string as a sequence of objects (characters ) with a
defined object at the end that can be used as a sentinel. Figure 1 1 - 5 shows
the difference between an array of characters and a string.
H e I I o \0 H e l l o
String Literals
— —
A string literal or as it is also known , string constant is a sequence of char-
acters enclosed in double quotes. For example , each of the following
is a
string literal :
nII
a "a "
an empty
a character a string
string
Declaring Strings
As we said, C has no string type. To declare a string, therefore, we must use one
of the other available structures. Since strings are a sequence of characters, it is
only natural that the structure used to store string variables is the character array.
In defining the array to store a string, we must provide enough room for
the data and the delimiter. The storage structure, therefore, must he one byte
larger than the maximum data si/e. A string declaration for an eight -character
string, including its delimiter, is shown below.
char s t r [ 9 ] ;
stant . We don’t need to worry about allocating memory because the declara -
tion is for an array and memory allocation is automatic . We can read data into
the string and change data in the string as necessary.
// Local Declarations
char* pStr ;
pStr Q >
Memory for strings must be allocated before the string can be used.
Initializing Strings
We can initialize a string the same way that we initialize any storage structure
by assigning a value to it when it is defined. In this case, the value is a string
literal . For example, to assign " Good Day to a string, we would code
In this case, the compiler will create an array of 8 bytes and initialize it
with January and a null character. We must he careful, however, because
month is an array. II we now tried to store " December " in it , we would over-
run the array and destroy whatever came after the array. This example points
out one ol the dangers of strings: We must make them large enough to hold
the longest value we will place in the variable.
C provides two more ways to initialize strings. A common method is to
assign a string literal to a character pointer, as shown below. This creates a
string for the literal and then stores its address in the string pointer variable,
pStr. To clearly see the structure , refer to Figure 11 10.
-
char* pStr = "Good Day" ;
char str[9] =
i|
-
{’ G' ,'o' ,'o' , d ' , ' f ' D ' ' a ' ' y ' , ' \ 0 ' };
j j
str — ! BHHEMBBnEl
flag
Like all inputs, the only flag that can he used is the asterisk ( * ), which dis-
cards the data read.
Size
.
II no si / e option is specified, we read normal characters To read wide charac-
ters, we use size 1 ( ell ).
Examples
For example, to read a string, such as month, from the keyboard, we could
simply write the statement shown below.
char month[ 10 ];
we can protect against the user entering too much data by using a width in
the field specification . ( Recall that the width specifics the maximum number
.
ol characters to be read ) The modified scanf statement is shown below.
Note that we set the maximum number of characters at nine while the
array size is ten . This is because scanf will read up to nine characters and
then insert the null character. Now, if the user accidentally enters more than
nine characters, the extra characters will he left in the input stream . But this
can cause a problem. Assuming that the data are being entered as a separate
—
line that is , that there is only one piece of data on the line we use the —
—
preprocessor-defined statement, FLUSH see Chapter 7 , to eliminate any
extra characters that were entered . I bis function also flushes the newline
that is left in the input stream by scanf when the user correctly enters data.
The complete block of code to read a month is shown in Program 11 - 1 .
character remains in the input stream for the next read operation If the first .
character read is not in the scan set , the scanf / fscanf terminates and a null
string is returned .
A major difference between the scan set and the string conversion codes
is that the scan set does not skip leading whitespace. Leading whitespace is
either put into the string being read when the scan set contains the corre-
sponding whitespace character, or stops the conversion when it is not .
In addition to reading a character that is not in the scan set , there are
two other terminating conditions. First , the read will stop if an end -of -file is
.
detected Second , the read will stop if a field width specification is included
and the maximum number of characters has been read .
For example, lets assume we have an application that requires we read a
string containing only digits, commas, periods , the minus sign , and a dollar
sign ; in other words, we want to read a dollar value as a string. No other char-
acters are allowed . Let 's also assume that the maximum number of characters
in the resulting string is 10. The format string for this operation would be3
line . We can do this by stating that all characters except the newline ( \ n ) are
valid . To specify invalid characters, we start the scan set with the caret ( ) A
symbol . I he caret is the negation symbol and in effect says that the following
characters are not allowed in the string. ( If you know UNIX, this should
sound lamiliar. ) Io read a line , we would code the scanf as shown below.
In this example, scanf reads until it finds the newline and then stops.
Note that we have again set the width ol the data to prevent our string, line,
Irom being overrun . We would never use this code, however. As we see in the
next section , an intrinsic string function does it for us.
I or the last example, let ’s read a 1 5-character string that can have any
character except the special characters on the top of the keyboard . In this
case , we again specify w hat is not valid . This conversion code is shown in the
following example.
s c a n f ( " %1 5 [ v ~ ! @ # $ % *&* ( ) + j
/
. f str) ;
i
r
^l
Chapter 11 Strings 677
Note that the caret can he included in the scan set , as long as it is not the
first character. Similarly, the closing bracket is treated as text as long as it is
the first character after the opening bracket. However, if it follows the nega -
tion symbol ( ) , then it ends the scan set . This means that the close bracket
A
cannot he used when the scan set is being negated . The following example
reads a string containing brackets and digits only.
Justification Flog
The justification flag ( - ) is used to left justify the output . It has meaning only
when a width is also specified , and then only if the length of the string is less
than the format width . Using the justification flag results in the output being
left justified , as shown below. If no flag is used , the justification is right .
Minimum Width
The width sets the minimum size of the string in the output . If it is used with-
out a flag, the string is printed right justified as shown below
.
Precision
is the minimum number of
Strings can get quite long. Because the width
characters that can be printed , a long string can easily exceed the width spec-
ified , which destroys column formatting . Therefore , C also uses the precision
that will be written. In the
option to set the maximum number of characters
678 Section 11.3 String Input/Output Functions
following typical example we set the maximum characters to one less than
the width to ensure a space between the string and the next column .
Size
If no size option is specified , we write normal characters. To write wide char-
acters , we use size 1 ( ell ) .
Input:
123 456 7.89
987 654 3.21
Results:
Second integer: 456
Second integer: 654
End of program
Program 1 1 - 2 Analysis As long as two numbers are at the beginning of each line, this program works just
fine. It skips the first number, reads the number into amount, and then discards the
rest of the line. Study the flags contained in the format string in statement 22 carefully.
It consists of only three field specifications. The first one reads and discards an integer.
The second one reads amount. The third one considers the rest of the line as a long
string; fscanf reads until it finds a newline. Because we are not interested in these data
for the first and third parts of the line, we used the suppress flag to discard them.
19
20 // Discard newline and set line to null string
21 fgetc (stdin);
22 *( line) = '\0' ;
23 printf( " Enter data: ");
24 } // while
25
26 printf( " \nThank you\n " );
27 return 0;
28 I > // main
Results:
Enter data: No whitespace here.
You entered: No whitespace here.
Enter data: Only one whitespace character.
You entered: Only one whitespace character.
Enter data: Tabs and spaces here.
You entered: Tabs and spaces here.
Enter data: Next line is only one space.
You entered: Next line is only one space.
Enter data:
You entered:
Enter data: Ad
Thank you
.
In Program 1 1 - 4 we read a file of student names and scores As each
name is read, it is printed. We read the first and last names using the string
.
conversion code. We read the students score as an integer As an alternative,
we could have used the scan- set conversion code, but it is simpler to use the
basic string code ( remember KISS).
Results:
George Washington 95
Benedict Arnold 53
-
Mary Todd Lincoln 91
End of Student List
682 Section 11.3 String Input/Output Functions
Program 1 1 - 4 Analysis There is only one significant point to note in this program how we handle end of
data. As long as we read three pieces of data, we continue looping. When the last
data have been read, the scan returns 0 and the while statement becomes false.
String Input/Output
In addition to the formatted string functions , C has two sets of string func -
tions that read and write strings without reformatting any data. These func-
tions convert text -file lines to strings and strings to text -file lines. A line
consists of a string of characters terminated by a newline character.
C provides two parallel sets of functions, one for characters and one for
wide characters. They are virtually identical except for the type. Because wide
characters are not commonly used and because the functions operate identi -
cally, we limit our discussion to the character type. The wide - character func-
tions are listed in Appendix F.
Line to String
The gets and fgets functions take a line ( terminated by a newline ) from the
input stream and make a null - terminated string out of it . They are therefore
sometimes called line- to-string input functions.
The function declarations for get string are shown below.
Figure 11 - 11 shows the concept . As you can see, gets and fgets do not
work the same. The gets function converts the return ( newline character ) to
-
the end of -string character ( \ 0 ), while fgets puts it in the string and appends
an end - of -string delimiter.
\n changed
to null
HelloNn
..
gets( .) H e I I o \0
Keyboard
Memory
\n kept
and null added .
Hello\n
fgets(...) I I o \n \0
* He
Memory
1
Chapter 11 Strings 683
The source o( data for the gets is standard input ; the source of data lor
fgets can he a file or standard input . Both accept a string pointer and return
the same pointer if the input is successful. If any input problems occur, such
as detecting end -of - fde before reading any data , they return NULL . If no data
were read , the input area is unchanged. If an error occurs after some data * ,
have been read , the contents of the read - in area cannot be determined . The
current string may or may not have a valid null character.
Note that since no size is specified in gets, it reads data until it finds a
-
newline or until the end -of file. If a newline character is read , it is discarded
and replaced with a null character.
The fgets function requires two additional parameters: one specifying the
array size that is available to receive the data , and the other a stream . It can
be used with the keyboard by specifying the stdin . In addition to newline and
end of file, the reading will stop when size - 1 characters have been read.
Since no length checking is possible with gets , we recommend that you
never use it. Should a user enter too much data , you will destroy the data
after the string input area , and your program will not run correctly. Rather,
use fgetSy and specify the standard input file pointer ( stdin ) .
Results:
the time for all students
Please enter a string: Now is
continuec
684 Section 11.3 String Input/ Output Functions
String to Line
The puts/ fputs functions take a null-terminated string from memory and write
it to a file or the keyboard. Thus, they are sometimes called string- to- line out -
put functions.
Figure 11 - 12 shows how these functions work. All change the string to a
line . The null character is replaced with a newline in puts; it is dropped in
fputs. Because puts is writing to the standard output unit , usually a display,
this is entirely logical. On the other hand , fputs is assumed to be writing toa
file where newlines are not necessarily required . It is the programmer’s
responsibility to make sure the newline is present at the appropriate place.
Note how the newline is handled in these functions. I hen compare their
use of the newline to the gets and /gets functions. While the output functions
treat the newline the opposite of the input functions, they are compatible. As
long as we are reading from a file and writing to a file, the newlines will be
handled automatically. Care must he taken , however, when we read from the
keyboard and write to a file .
The declarations for these functions are shown below.
r Hello
Hello\n
puts( ... )
NULL is changed
v to newline y
H e I I o \0
Memory
k.
Hello NULL is dropped
fputs( ... )
No newline
(unless in H e I I o \0
string data)
Memory
EXAMPLE 11 - 5 I he following program demonstrates the use of / puts with stdout . It calls / puts
three times. The first time, we pass a pointer to the beginning of an array. The
third time, we pass a pointer that is at the middle of the array. If you were to
run this code, you would see that it does not matter where a string pointer
starts , fhe function starts at the address in the pointer and writes until
it
finds a null characte r. Note that we had to include a newline put because
/ puts does not insert a newline. The output is shown in Program 1 1 -6.4
PROGRAM 1 1 - 6 Demonstration of Put String
1 /* Demonstrate fput string
2 Written by:
3 Date:
4 */
5 # include <stdio.h >
6
7 int main ( void )
8 {
9 // Local Definitions
on." ;
10 char str[ ] = " Necessity Is the Mother of Inventi
11 char* pStr = str ;
12
13 // Statements
14 fputs( pStr , stdout );
15 fputs( "\n", stdout );
16 fputs( pStr + 13 , stdout);
17 return 0;
18 > // main
Results:
Necessity Is the Mother of Invention
the Mother of Invention.
Program 1 1 - 7 Analysis Note that we are reading the keyboard [ stdin ] using /gets ( see statement 20). This
requires that we specify the file name, even though we do not have to declare it or
open it. By using fgets we are able to ensure that the user will not overrun our string
variable area (str). But even if the user enters too much data, nothing is lost. The
data are left in the input stream buffer and are read in the next loop iteration.
This little program has two problems, one stylistic and one technical. The style prob-
lem is that it contains no instructions to the user, who must therefore guess what is to be
done. This leads directly to the second problem. Some systems have a problem if the
user should end the program with an end - of - file but no return. Depending on the system,
the last line is lost or the system may just wait for an end- of - file at the beginning of the
line. This problem can be prevented by adding good user instructions and a prompt.
I
m
Chapter 11 Strings 687
1
PROGRAM 1 1 - 8 Print Selected Sentences
1 /* Echo keyboard input that begins with capital letter.
2 Written by:
3 Date written:
4 */
5 # include <ctype.h>
6 # include <stdio.h>
7
8 int main ( void )
9 {
10 / / Local Declarations
11 char strng[81];
12
13 // Statements
14 while ( fgets ( strng , sizeof( strng ), stdin ))
15 if ( isupper(*strng ))
16 fputs(strng, stdout );
17 return 0;
18 > // main
Results:
Now is the time
Now is the time
for all good students
to come to the aid
of their school .
Amen
Amen
Program 1 1 - 9 Analysis Because we are reading data from a text file, we use the fgets function. This function
guarantees that we will not overrun our input string variable. Note how we used the
sizeof operator to set the maximum number of characters to be read. Since fgets
adds a newline character at the end of the data being read, we don' t need to worry
about adding one when we write. After writing the data string, we use a putchar to
write the blank line.
is a pointer constant,
Program 11 - 1 0 Analysis To print the days of the week, we use a for looparithmetic . The pointer variable must
. Since pDays
and that the calculation needs to be done only once, but we can't be sure that such effi-
cient code would in fact be generated.) Therefore, we calculate the ending address just
once, before the while loop, and then we can be sure that the limit test will be efficient.
Study this code carefully. Note first that pWalker is a pointer to a pointer to a
character. Then notice how it is used in the for statement. It is initialized to the first ele-
ment in pDays, then it is incremented until it is no longer less than or equal to pLast.
Finally, note how it is used in the printf statement. The printf syntax requires that the
variable list contain the address of the string to be printed. But pWalker is a pointer to
an address that in turn points to the string ( a pointer to a pointer ). Therefore, when we
dereference pWalker, we get the pointer to the string, which is what printf requires.
This example is diagrammed in Figure 11 - 13.
pWalker
4 S u n d a y \0
M | o n d a y \0
\\ \ 'x
X'\ v
V
T | u | e | s | d | a | y \0
*v W | e | d | n | e | s | d | a | y \0
T | h | u | r | s | d a y \0
\
pLast F r i | d a y \0
pDays
S | a | t | u | r | d | a | y | \0
String Length
I he string length function, strlen, returns the length of a string, specified as
the number of characters in the string excluding the null character. If the
string is empty, it returns zero The function declaration is shown below.
.
int strlen (const char* string );
18 {
19 printf( " \aCould not open output file Xn )
" ; .
20 exit ( 100 );
21 > // if
22
, stdin ))
23 while ( fgets(strng , sizeof( strng )
24 {
continued
692 Section 11.5 String Manipulation Functions
Program 1 1 - 1 1 Analysis To ensure that the user doesn't overrun the input area, we use the fgets function to
read the keyboard. Some data may be left in the buffer, but we don' t flush the buffer,
because we want to write them to the file on the next line.
Also, since fputs does not add a newline when it writes the file, we need to ensure
that there will be a new line at the end of each line. Therefore, in statement 28 we test
the last character of the input string and if it isn' t a newline, we write one.
Because we want to add characters to the beginning of the line, we must use a
character operation. The function fputc writes one character to a designated file. To
insert two characters, therefore, we use it twice, and then use fputs to write the line
read by fgets .
String Copy
C has two string copy functions. The first , strepy, copies the contents of one
string to another. I he second , strnepy , also copies the contents of one string
to another, hut it sets a maximum number of characters that can be moved.
I herd ore, strnepy , is a safer function .
II fromStr is longer than toStr , the data in memory after toStr are
destroyed . It is our responsibility to ensure that the destination string array is
large enough to hold the sending string. This should not be a problem, since
we control the definition oi both string variables. The address of toStr is
returned , which allows string functions to he used as arguments inside other
string functions. We will demonstrate the use of these returned pointers later
in the chapter.
Figure 11 - 14 shows two exam pies ol string copy. In the first example, the
source string is shorter than the destination variable. The result is that , after
the stling has been copied , the contents of
the last three bytes of si are
unchanged ; si is a valid string, however.
7
Chapter 11 Strings 693
G o o d D a | y \Q
s1- before s2 - before
(a ) strcpy( s1, s2 ) ;
Copying Strings
S h o r t \0 0 t h e r \0 G o o d D | a l y |\Q |
s1- before s3 - before s 2 - before
(b) strcpy( s 1, s2 ) ;
moved . If the from string is smaller than size , the entire string is copied and
then null characters are inserted into the destination string until exactly size
characters have been copied. Thus, it is more correct to think of size as the
destination characters that must be filled .
If the sending string is longer than size , the copy stops after size bytes
have been copied . In this case, the destination variable may not be a valid
string; that is , it may not have a delimiter. The string number copy functions
do not insert a delimiter if the from string is longer than size . On the other
hand , the data following the destination variable will be intact , assuming the
size was properly specified . Figure 11 - IS shows the operation of strncpj
G o o d D a y \0
s1 - before s2 - before
G| o | o | d | |D 0| 11 h | e | r \0 |G| o | o | d | |D | a | y |\0
s1 - after s3- after s 2 - after
We recommend that you always use strncpy ; do not use strcpy. To prevent
invalid strings , we also recommend that you move one fewer character than
the maximum and then automatically pi ace a null character in the last posi-
tion . The code for this technique is shown below.
strncpy ( si , s 2 , s i z e o f ( s l ) -1 ) ;
* ( s l + ( sizeof ( s i ) -
1 ) ) = ' \o ' ;
Since the strncpy places null characters in all unfilled characters, we are
guaranteed that the last character in the string array is a null character. If it is
not , then the copy was short . By executing the above statements, we are assured
that si will be a valid string, even if it doesn ’t have the desired contents.
A closing note: If size is zero or negative , nothing is copied. The destina-
tion string is unchanged .
1
Chapter 11 Strings 695
Results:
How many names do you plan to input? 3
Enter names:
Tom
Rico
Huang
Program 11 - 12 Analysis This little 50- line program contains some rather difficult code. To understand it better,
let's look at the array structure, shown in Figure 11 - 16.
pNames
fB *
>
-
*
Last pointei
.always null
(heap)
String Compare
As with the string copy functions, C has two string compare functions.^ The
first, strcmp, compares two strings until unequal characters are found or until
.
the end of the strings is reached The second, strncmp, compares until
unequal characters are found, a specified number of characters have been
tested, or until the end of a string is reached.
Roth functions return an integer to indicate the results of the compare.
Unfortunately, the results returned do not map well to the true-false logical
values that we see in the if .. .else statement (see Chapter 5 ), so you will need
to memorize a new set of rules:
. .
1 If the two strings are equal, the return value is zero Two strings are con -
sidered equal if they are the same length and all characters in the same
relative positions are equal.
2. If the first parameter is less than the second parameter, the return value
is less than zero . A string, si, is less than another string, s 2 , if starting
from the first character, we can find a character in si that is less than the
character in s 2 . Note that when the end of either string is reached, the
null character is compared with the corresponding character in the other
string.
.
3 If the first parameter is greater than the second parameter
, the return
. A string si , is greater than string, s 2 , if start -
value is greater than zero ,
first character, we can find a character in s 2 that is greater
ing from the
than the corresponding character in si. Note that when the
end of either
compared with the correspond ing
string is reached, the null character is
character in the other string .
strcmp
The function declaration for the string compare function is shown below.
While the string compare functions return integer values, ( < = l , o, >= i )
we can code a logical expression that returns true or false by using the com-
pare function and relational operators. For example, to compare two strings
for equal, we must write the statement as shown below.
if (strcmp(strl , str2 ) == 0)
// strings are equal
else
// strings are not equal
Stops at end
of string -
Stops at Stops at
not equal not equal
strcmp ( s1 , s2 )
I he I allowing statement tests whether the first string is less than the sec-
ond string.
99
Chapter 11 Strings
^
Io test for s t r i n g l greater than s t r i n g 2 , use the following code:
i f ( strcmp ( s t r i n g l , s t r i n g 2 ) > 0 )
/ / s t r i n g l i s g r e a t e r than s t r i n g 2
We can also test for greater than or equal to with the following statement :
i f ( strcmp ( s t r i n g l , s t r i n g 2 ) > = 0 )
/ / stringl is greater than or equal to string2
strncmp
I he string number compare function , strncmp, tests two strings for a speci-
fied maximum number of characters ( s i z e ). The function declaration s are
shown below.
i n t strncmp ( const c h a r * s t r l , c o n s t c h a r * s t r 2 ,
int size ) ;
stringl
"ABC123"
string2
" ABC123"
1 8 equal
Results Returns
0
" ABC 123" "ABC456 " 3 equal 0
"ABC123" "ABC456" 4 stringl < string2 < 0
" ABC 123" " ABC" 3 equal 0
" ABC 123 " " ABC" 4 stringl > string 2 >0
String Concatenate
to the end of another.
The string concatenate functions append one string
They return the address pointers to the destination string . The size of the des -
is assumed to he large enough to hold the resulting
tination string array
the data at the end of the string array are destroyed . As we
string. If it isn ’t ,
ble if the strings overlap .
saw with string copy, the results are unpredicta
700 Section 11.5 String Manipulation Functions
The function copies str 2 to the end of strl, beginning with strl's
delimiter. That is, the delimiter is replaced with the first character of str 2 .
The delimiter from str 2 is copied to the resulting siring to ensure that a
valid string results. The length of the resulting string is the sum of the length
of strl plus the length of str 2. Figure 1 1 - 18a shows the operation.
ClOlN \C C A T E N A T I O N \0
s 1- before s 2 - before
C |Q | N | C | A | T | E | N | A | T | I IOTNI 'O
| C | A | T | E | N | A | T | I | O | N | \0
s1 - after s2 - after
String Concatenate
C | Q | N \C C A T E N A T I O N \0
s 1 - before s2 - before
(b ) strncat ( s 1 , s2 , 3 ) ;
C|O|N|CTATT W C A T E N A T I O N S
s1 - after s 2 - after
String N Concatenate
Note that , as often is the case in the library functions, the character in the
ASC 11 functions is typed as an integer. We don ’t need to worry about this.
I he compiler will implicitly cast any character types to integer types before the
call, figure 1 1 - 19 shows three examples of character- in -string calls.
si |C|O| N C A | T | E N | A | T | I Q]N \Q
1 1 1 1 1 1
P1
a* p1 = strchr ( s1, ’N’ ) ;
si C O N C A T E N A T I O N S
P1
v P2
^ ^
[ p2
Desired
Substrinc
s1 q O N C A T E j N A T I O N \0
t j p1 = strstr ( s1, "CAT" ) ;
FIGURE 11 - 20 String in String
IcioNC lAlTlE N A T I O N S
FIGURE 11 - 2 1 String Span
String Token
, called tokens ,
The string token function , strtoky is used to locate substrings
string into tokens , much as a
in a string. Its most common use is to parse a locates
how it is called it either
com piler parses lines of code. Depending on
,
The first parameter is the string that is being parsed ; the second parame-
ter is a set of delimiters that will he used to parse the first string. If the first
parameter contains an address , then strtok starts at that address, which is
assumed to be the beginning of the string. It first skips over all leading delim -
iter characters. If all the characters in the string are delimiters, then it termi-
nates and returns a null pointer. When it finds a nondelimiter character, it
changes its search and skips over all characters that are not in the set ; that is
it searches until it finds a delimiter. When a delimiter is found , it is changed
to a null character ( ' \ 0 • ) , which turns the token just parsed into a string.
If the first parameter is not a string, strtok assumes that it has already
parsed part of the string and begins looking at the end of the previous string
token for the next delimiter. When a delimiter is located, it again changes the
delimiter to a null character, marking the end of the token , and returns a
pointer to the new token string.
Let ’s look at a simple example of a string containing words separated by
spaces. We begin hv calling string token with the address of the f ull string. It
returns the address of the first character ol the first string that was just
parsed . The second execution of the string token function parses the second
string and returns its address, and so on until the complete string has been
parsed. I bis design is shown in Figure 1 1 - 22 .
( a ) Before Parsing
strng
^
( b ) After First Parsing
pToken = strtok (NULL, " "); pToken = strtok (NULL, " ");
pToken Q pToken ££)
strng | Q 1 N | E | \0| T |W|0|\0| T | H | R | E | E |\0' strng OlN E 0 Tlwlo! 0|T|H|R|E| EbQ
( c ) After Second Parsing ( d ) After Last Parsing
String to Number
I he standard library ( stcllib.lt ) library provides several functions that convert
a string to anumber. I he two most important are string to long and string to
double . A complete list is found at the end of this section .
String to Long
I he string- to- long function , strtol , converts a string to a long integer. It skips
leading whitespace characters and stops with the first non - numeric character,
Results:
Decimal constant 12345
Binary constant 25
Octal constant 6010
Hex constant 31420
Base 0-Decimal 11001
Base 0-Octal 577
-
Base 0 Hex 31420
Invalid input 0
j Program 11 - 1 3 Analysis The first thing to note in this program is that the address of
the character pointer must
be passed to strtol. If this surprises you, go back and look at the strtol function decla -
ration statement again. Note that the declaration of ptr has
two asterisks.
Observe that the pointer is set to the first character after the last valid digit. In our
example, this character was always a space. If there were no characters after the last
digit, the pointer would point to the string delimiter, resulting iin a null string.
String to Double
I he string- to- double functions—strtocl and wcstod —
are similar to the string-
to- long functions, except that they do not have a hase. The function declara-
tion is seen below.
and minus signs, and the letter e or E in the exponent . After using the longest
possible sequence ol digits and characters to convert the string to a number,
strtod sets the pointer to the address of the next character in the string. If no
characters are present , the pointer is set to the string delimiter. As with
string- to- Iong, if the number is invalid , zero is returned and the pointer is set
to the beginning of the string.
String Examples
In this section , we discuss two functions that use string functions.
The first
. The second uses string input /
uses strtok to parse an algebraic expression
and several string manipulatio n functions to compare
output functions
strings.
sum = sum + 10 ;
by whitespace. Our
Each token in this simple expression is separated
it out . The step- by-step parsing is
program will identify each token and print
Program 11 - 14 .
shown in Figure 1 1 -23. The code is shown
in
708 Section 11.5 String Manipulation Functions
^
strng | S |U|M|\0|= |\0| S | U|M| | -t | 1110 ; |\0
pToken ( FourthToken)
contin net
'
II
Results:
Token 1 contains sum
Token 2 contains =
Token 3 contains sum
Token 4 contains +
Token 5 contains 10
End of tokens
Program 11 - 1 4 Analysis Since the first call to strtok must contain the address of the string, it is coded before
the loop. If the string contains at least one token, the first call will return a valid
address and the while loop will print it and parse out the remaining tokens. Note that
the delimiter set includes the semicolon as well as the blank between tokens. The
semicolon serves as the last token in the string.
31 cmpResult = strCmpPk ( si , s2 );
32 if ( cmpResult < 0 )
33 printf("stringl < string2\n " );
34 else if (cmpResult > 0 )
35 printf( "stringl > string2 \n " );
li 36 else
37 printf( "stringl == string2\n " );
38
39 return 0;
40 } // main
41
42 ===== strCmpPk =
43 Packs two strings and then compares them.
44 Pre si and s2 contain strings
45 Post returns result of strcmp of packed strings
46 */
47 int strCmpPk ( char* si , char* s2)
48 {
49 // Local Declarations
50 char slln [ 80);
continue
Chapter 11 Strings 711
'
\ n
PROGRAM 11 - 1 5 Compare Packed String Function ( continued )
51 char slOut[ 81 ];
52 char s2In [ 80 ];
53 char s20ut[81];
54
55 // Statements
56 strncpy (slln , si , sizeof(slln ) i );
57 strncpy (s2In , s2 , sizeof(s2In ) 1 );
58 strPk ( slln , slOut);
59 strPk (s2In , s20ut );
60 return (strcmp ( slOut , s20ut ));
61 > // strCmpPk
62
63 :
=====
64 -
Deletes all non alpha characters from si and
65 copies to s2.
66 Pre si is a string
67 Post packed string in s2
68 si destroyed
69 */
70 void strPk (char* si , char* s2)
71 {
72 // Local Declarations
73 int strSize;
74
75 // Statements
76 *s2 = '\01 ;
77 while ( *sl != \0 * )
78 {
79 // Find non-alpha character & replace
80 strSize = strspn( sl , ALPHA );
81 si[strSize ] = ’ \01 ;
82 strncat (s2 , si , 79 -
strlen( s2 ));
83 si += strSize + 1 ;
84 } // while
85 return ;
86 > // strPk
Results:
Please enter first string:
a b!c 234d
Please enter second string:
abed
stringl == string 2
c continued
712 Section 11.6 String/Dato Conversion
Program 1 1 - 1 5 Analysis To test the string compare, we need to write a test driver. Since the test driver is throw -
away code, we have coded the test logic in main.
Statement 8 contains a C language construct that we have not used before: the
statement continuation. When a statement does not fit on a line, it can be continued by
putting an escape character ( \ ) immediately before the end of the line. This is consistent
with the meaning of the escape character; in this case, it means escape the end of line
that follows; it is not really an end of line. This allows us to continue the define statement
on the next line.
Since we are passing addresses to strings, we need to be careful that we don't
destroy the original data. The first thing we do in strCmpPk, therefore, is to use
strnepy to copy the input string to a work area . Once we have protected the original
data, we pack the two strings to be compared and then use the stremp function to
determine if the reformatted data are equal.
The strPk function merits a little discussion. It uses a while loop to process the
input string. Each iteration of the loop scans the valid characters, as defined in ALPHA,
looking for invalid data. When strPk finds an invalid character — and there will
always be at least one, the input string delimiter — it ( 1 ) replaces that character with a
string delimiter, ( 2 ) concatenates the input string to the output string, and then
( 3 ) adjusts the input string pointer to one past the last scanned character. This is neces-
sary to point it to the data beyond the string just copied.
But what if the pointer adjustment puts it beyond the end of the input data? In that
case, we will have a delimiter, because we used strnepy to copy the data from the orig-
.
inal input, and it pads the output string with delimiters. Therefore, the character after
the original string delimiter is another delimiter. Note that we made the output area one
character larger than the input area to provide for this extra delimiter even with a
I full string.
At least three test cases are required to test the program. We show two of them in
the result. In addition to an equal and greater - than result, you should conduct at least
one less - than test case.
sscanf
string variables
r "%49[
A
;] %*c %4s %d %*[" ABCDF] %c"
l
s Let ’s examine each format code individually
.
First, we are expecting a
string of up to 49 characters terminated
by a semicolon. Note that we use the
scan set with a terminating token
of a semicolon .
Following the scan set that
14 Section 11.6 Stiing/Data Conversion
reads to a semicolon is a format code that reads and discards one character.
The asterisk is a flag indicating that the character is not to be stored. This
code is necessary because the terminating semicolon I mm the string remains
in the input string. We need to read and discard it .
The third format code is a simple four-character string. Any leading
whitespace is discarded with the string token ( ). Likewise, the third field,
the score, is converted to an integer format after the leading whitespace is
discarded.
Parsing the grade is a little more difficult. We can have one or more
whitespaces between the numeric grade and the alphabetic grade, but we
must dispose of them ourselves, because the character token ( % c ) does not
discard whitespace. Our solution is to use a scan set with the suppress (lag
( * ) to discard all characters up to the grade. Again, we use the negation to set
the terminating character. Having discarded all the leading whitespace, we
can now format the grade with a character token.
Using our example above, we can code the sscanj call as shown below.
Note that this code has six format codes hut only four variables because
the second and fifth fields have their formatting suppressed.
sending the data to a file, however, it simply ‘‘ writes” them to a string. When
all data have been formatted to the string, a terminating null character is
added to make the result a valid string. If an error is detected, sprintf returns
any negative value, traditionally EOF. If the formatting is successful, it returns
the number of characters formatted, not counting the terminating null char-
acter. The string print operation is shown in Figure 1 1 - 25 .
sprintf
string variables
The first parameter is a pointer to a string that will contain the formatted
.
output The format string is the same as printf and follows all of the same
rules. The ellipse (...) contains the fields that correspond to the format codes
in the format string.
It is our responsibility to ensure that the output string is large enough to
hold all the formatted data. If it is not large enough, sprint / destroys memory
.
contents after the output string variable The wide-character function,
swprintf , on the other hand limits the output. The additional parameter,
max _out , specifies the maximum wide characters.
Results:
String contains: "Einstein , Albert; 1234 97 A"
Reformatted data:
Name: "Einstein , Albert "
id: " 1234"
score: 97 I
grade: A
New string: " Einstein , Albert 1234 97 A"
Chapter 11 Strings 71 /
Results:
Enter first address: 23.56.34.0
Enter second address: 23.56.32.255
Pointers to Pointers to
characters Morse code
A
[ A \O # \0
A
rBW # \0
A
z \o # \0
A
\0 $ | $ 1 # | \0
.
Each column has 27 pointers Each pointer in the first column points to
a string ol length one, which contains one English letter ( uppercase) Each .
pointer in the second column points to a string of varying size, a ragged array,
which contains the corresponding Morse code for the English letter.
The program is menu driven. The menu, which is shown in Figure 1 1 - 27,
has three options: encode English to Morse code, decode Morse code to
English, and quit.
MENU
E encode
D decode
Q quit
Enter option: press return key:
Morse
code
(+)
print getlnput
menu getlnput encode decode
Output/]
convert convert
A
FIGURE 11 - 28 Morse Code Program Design
38 { "E", >,
39 { "F" , >r
40 { "G" , —.# "
41 { " H", # " },
42 { II T II
± 9 }r
43 { "J ", . #" >,
44 { "K " , >,
45 { "L" , >,
46 { "M " ,
47 { "N " ,
48 { "0" , #”
49 { "P" ,
50 { "Q " f
51 { "R " , .-.# M
52 { "S",
53 { "T",
54 { "U" ,
55 { " V" , > #
60 { $$# M >,
continuec
722 Section 11.7 A Programming Example — Morse Code
Program 1 H 8 Anolysu
6
Ptl
^ ^' * ’
|
f °is neither'
n Can
menu
one or
^ assume quit. We can do this
Program 11 - 191 At this point in the
,ly three values . We test for two and
default the third ^ ° °
T u
Program 11 - 19 Analysis The menu function in Program 11 - 19 displays the options and reads the user's
choice. It then validates the choice and if invalid, displays an error message and asks
for the option again. Although it is always good design to validate the user input, it
must be validated in the correct place. Since menu is communicating with the user,
this is the logical place to do the validation. One more point: As the option is read, it
is converted to uppercase. Not only does this make the validation simpler, but it also
simplifies the switch statement in main. Whenever you have a single character code,
convert it to uppercase or lowercase for processing in the program .
Program 1 1 -20 Analysis Note that to prevent a runaway string in getlnput we use the fgets function and
specify the maximum number of characters in the string area. This function creates a
minor problem, however; it places the newline in the input string . We must therefore
overlay it with a null character.
The code to print the output of this program is shown in Program 11- 21.
Reading this declaration, at 0 we have encDec . Moving to the right, we find the
1 , which is ignored. Moving left, at 2 we find a pointer. We therefore have encDec as
a pointer. Moving right to 3, we see that it is a pointer to
an array of two . Moving to the
left to 4, we see that it is an array of pointers. PI
At this point, we see that encDec is a pointer to an array of two pointers. Now,
moving back to the right, we see that 5 is empty, so we go to the left and find char at 6.
The final type is therefore a pointer to an array of two pointers to char. Referring back
to Figure 11 - 26, we see that this is exactly what we have.
rogram 1 1 - 24 Analysis The convert function does the actual conversion. Note, however, how it is designed
to handle the conversion both from English to Morse and Morse to English. The only dif -
ference between the two conversions is which pointer we want to use. We pass the col-
umn to be used for the search as a parameter. Once we have located the correct string,
we use the formula shown below to pick up the matching string ( see statement 22) .
(col + 1) % 2
If we are searching on the English letters in column 0, then the modulus of the column
(0 + 1 ) is column 1, which contains the pointer to the matching Morse code string. Con-
versely, if we are searching Morse code using the pointers in column 1, then the modulus
of the column ( 1 + 1 ) is column 0, which contains the pointer to the English letter.
728 Section 11.8 Software Engineering
Information Hiding
Information hiding is the principle of program design in which the data
structure and functional implementation are screened from the user. Mod -
ules are independent when their communication is only through well-defined
parameters and their implementation is hidden from the user. The purpose of
the function should be defined in its inputs and outputs, and the user should
not need to know how it is implemented or how its data are structured . Press-
man states it well when he says:
Cohesion
The most common weakness of function design is combining related pro-
cesses into one primitive function. In Chapter 4 we discussed the concept
- Hill
8. Roger S . Pressman , Software Engineering : A Practitioner’s Approach , 2 nd ed , McGraw
Series in Software Engineering and Technology ( New York: McGraw- Hill, 1982 ), p. 228.
Chapter 11 Strings 729
that each module ( function ) should do only one thing. This principle ol
structured programming is known as cohesion . Cohesion , first discussed by
Larry Constantine in the 1960s,9 is a measure of how closely the processes in
a function are related .
We are concerned with cohesion for three primary reasons. The first and
most important is accuracy. The more cohesive a function is, the simpler it is.
I he simpler it is, the easier it is to write , and the fewer errors it will have.
This is closely related to the second reason for high cohesion, maintainability.
II a function is easy to understand , it is easy to change . This means that we
will get the job done faster and with fewer errors.
Finally, cohesive modules are more reusable . Reusability is also closely
related to the concepts of accuracy and ease of use. Existing functions have
stood the test of time and have been tempered in the heat of use . They are
more likely to be error free , and they certainly arc easier and faster to develop.
Cohesion is most applicable to the primitive functions in a program ,
those that are at the bottom of the structure chart and less applicable to the
controlling functions that appear above the lowest level. This does not mean ,
however, that cohesion can be ignored at the higher levels. To make the point
with an absurd example, you wouldn ’t write a program to manage your check -
book and maintain your daily calendar. Even though both of these processes
are related to things you do, they are so unrelated that you wouldn ’t put them
in the same program . The same concept applies in a program . For example , at
the lower levels of your program , you shouldn ’t combine functions that read
data with functions that print a report.
The seven levels of cohesion are shown in Figure 1 1 - 29 .
Functional Cohesion
Functional modules that contain only one process demonstrate functional
cohesion. This is the highest level of cohesion and the level that we should
hold up as a model. Using the example of printing a report , the report func-
tion should call three lower-level functions , one to get the data , one to format
9 . E. N . Yourdon and L. L. Constantine, Structured Design ( Upper Saddle River, N .J.: Prentice
Hall , 1978 ).
730 Section 11.8 Software Engineering
an d print the report header, and one to format and print the data . This design
.
is shown in Figure 11 -30 The print report heading function is optional
because it is called only when a page is lull .
Sequential Cohesion
A sequential module contains two or more related tasks that are closely tied
together, usually with the output of one flowing as input to the other. An
exa mple of sequential cohesion is shown in the calculations for a sale. The
design for this function might well he
1 . Extend item prices
2. Sum items
.
3 Calculate sales tax
4. Calculate total
print
report
o
getData
I print report
heading
printLine
In this example , the first process multiplies the quantity purchased by the
price. I he extended prices are used by the process that calculates the sum of
the purchased items. This sum is then used to calculate the sales tax, which
is finally added to the sum to get the sale total. In each case , the output of
one process was used as the input of the next process.
Although it is quite common to find the detail code for these processes
combined into a single function , it does make the f unction more complex and
less reusable. On the other hand , reusability would he a concern if the same
or similar calculations were being made in different parts of one program .
Communicational Cohesion
Communicational cohesion combines processes that work on the same
data. It is natural to have communicational cohesion in the higher modules
in a program , but you should never find it at the primitive level . For example,
consider a function that reads an inventory file, prints the current status of
the parts , and then checks to see if any parts need to he ordered . The
pseudocode for this process is shown in Algorithm 11 - 1.
V
Chapter 11 Strings / 31
All three ol these processes use the same data. II they are calls to lower -
level iunctions, they are acceptable. If the detail code is found in the func -
tion, however, the design is unacceptable.
The first three levels of cohesion are all considered to be good structured
programming principles. Beyond this point, however, ease of understanding
and implementation, maintainability, and accuracy begin to drop off rapidly.
The next two levels should be used only at the higher levels of a structure
chart, and then only rarely.
Procedural Cohesion
The fourth level of cohesion, procedural cohesion, combines unrelated pro-
cesses that arc linked hv a control flow. (This differs from sequential cohe-
sion, where data flows from one process to the next.) As an example, consider
the high- level logic main line of a program that builds and processes a list. A
procedural How could look like Algorithm 1 1 - 2.
Temporal Cohesion
The fifth level, temporal cohesion , is acceptable only over a limited range of
processes. It combines unrelated processes that always occur together. Two
temporally cohesive functions are initialization and end of job. They are
acceptable because they are used only once in the program and because thcv
are never portable . Recognize , however, that they should still contain calls to
functionally cohesive primitive functions whenever practical.
Summary
We have discussed two design concepts in this chapter: information hiding
and cohesion . Cohesion describes the relationship among processes within a
function . Keep functions as highly cohesive as possible. When designing a
program, pay attention to the levels of cohesion . It is much easier to design
high cohesion into a program than it is to program it in . For more information
about these concepts, refer to The Practical Guide to Structured Systems
Design by Meilir Page -Jones. 10
.
10. Meilir Page -Jones The Practical Guide to Structured Systems Design , Yourdon Press Com -
puting Series ( Upper Saddle River, N .J .: Prentice- Hall , 1988 ).
1
char str[20 ];
strcpy ( str, " Now is the time" );
char* str;
str = " Now is the time";
.
7 Passing a character to a function when a string is required is another
common error. This is most likely to occur with the formatted input/
output functions, in which case, it is a logic error. Passing a character in
place of a string when a function header is declared is a compile error .
.
8 Using the address operator for a parameter in the scanJ function with a
string is a coding error. The following is an invalid call and will most
likely cause the program to fail:
.
9 It is a compile error to assign a string to a character, even when the char -
acter is a part of a string.
10. Using the assignment operator with strings instead of a function call iis a
compile error.
I 1 Since strings are built in an array structure, they may he accessed with
.
indexes and pointers. When accessing individual bytes, it is a logic error
to access beyond the end of the data structure array .
( )
II Summary
11.11 Summary
Strings can be fixed length or variable length .
A variable- length string can he controlled by a length or a delimiter.
The C language uses a null - terminated ( delimited ) variable - length string.
A string constant in C is a sequence of characters enclosed in double
quotes. A character constant is enclosed in single quotes.
When string constants are used in a program , C automatically creates an
array of characters, initializes it to a null - terminated string, and stores it,
remembering its address.
To store a string, we need an array of characters whose size is one more
than the length of the string.
There is a difference between an array of characters and a string. An array
of characters is a derived data type ( built on the type character ) and is an
intrinsic structure in C . A string is a data structure that uses the array as
its basic type. It requires a delimiter for all of its operations.
We can initialize a string:
With an initializer when we define it .
By using a string copy function
By reading characters into it .
A string identifier is a pointer constant to the first element in the string.
An array of strings is a very efficient way of storing strings of different sizes.
The sccmf and printf functions can he used to read and write strings using
the % s format .
The scan set format , %[ . . . ], can also be used in the sccinf function to
read a siring.
The functions gets , fgets , puts , and / puts are used for reading and writing
strings. I hey transform a line to a string or a string to a line.
m
Chapter 11 Strings / 35
J The functions sscanf and sprintf are used for reading and writing strings
—
Irom memory that is, memory- to- memory formatting.
J I he functions strcpy and strncpy are used to copy one string into another.
a . True
.
b False
3. Both the gets and fgets functions change the newline to a delimiter.
a . True
b. False
4. The function that can be used to format several variables into
one string
in memory is string token ( strtok ) .
a . True
b. False
12 Practice Sets
.
a array
.
b character
c. field
d. record
e. string
6. The delimiter in a C string is
a . newline
b. defined by the programmer
c. a del character
d . a null character
e. not specified in ANSI C
7. Which of the following statements about string variables is false?
a. The assignment operator copies the value of one string variable to
another.
b. The array bracket operator is not needed if a string is defined as a
pointer to character.
c. The string name is a pointer.
d . When a string is initialized at definition time, C automatically inserts
the delimiter.
e. When a string is read with scarify it automatically inserts the delimiter.
8. Which of the following printf statements will not print a string? ( Assume
that name is properly defined and formatted . )
.
a printf ( " % s " , name ) ;
b. printf ( " % 30 s " , name ) ;
c . printf ( " % - 30 s " , name ) ;
d . printf ( " % 30 [ \ 0 ] " , name ) ;
A
.
12 The string manipulati on function adds a string to
the end of another string.
.
a stradd
b. strcat
c . strcmp
d . strepy
.
e strtok
.
13 The string manipulat ion function determines il a
character is contained in a string.
a . strehr
b. strehar
c. strcmp
d. strepy
e. strtok
.
14 The function is used to format a string into variables
i n memorv
*
.
a . scanf
b. sscanf
c. strfrmt
d . strscan
.
e strtok
15. The best level of cohesion is
.
a coinciden tal
b. communi cational
.
c procedura l
d . sequentia l
e. functiona l
Exercises
), a n d * ( x + 4 ) for the following declaration
:
16. Find the value of *x, * ( x + l
";
char* x = "The life is beautiful
Practice Sets
. block?
18 What is the error in the following program
{
char* x;
scanf( " %s" , x);
>
19. What would be printed from the following program
block?
{
char si[50 ] = "xyzt" ;
char* s2 = " xyAt" ;
int dif ;
dif = strcmp (si , s2 );
printf( " \n%d " , dif );
>
20 . What would be printed from the following program block?
{
char si[ 50] = " xyzt" ;
char* s2 = " uabefgnpanm" ;
char* s3;
char* s4 ;
char* s5 ;
char* s6;
s3 = si ;
s4 = s2 ;
strcat (si , s2 );
s5 = strchr( si , 'y ' );
s6 = strrchr(s2 , 'n ' );
printf ( " \n%s" , s3 );
printf ("\n%s" , s4);
printf ( "\n%s" , s5);
printf ( "\n%s" , s6 );
>
.
21 W hat would be printed from the following program block?
{
char si[50] = " uabefgnpanm" ;
char* s2 = "ab" ;
continued
Chapter 11 Strings 739
{
char* si = "abefgnpanm" ;
char* s2 = "ab";
char* s3 = " pan";
char* s4 = " bef";
char* s5 = " panam " ;
int dl;
int d 2 ;
int d 3;
int d4 ;
dl = strspn (si , s2);
d 2 = strspn (si , s3);
d3 = strcspn ( si , s4);
d4 = strcspn (si , s5 );
printf ("\n%d " , dl );
printf ("\n%d " , d 2);
printf ("\n %d " , d 3);
printf ("\n%d " , d4);
}
{
char* w = " BOOBOO" ;
printf ( "%s\n " , "DOO" );
printf ( "%s\n " , "DICK " + 2 );
printf ( "%s\n", "DOOBOO" +3 );
printf ("%c\n " , w( 4]);
printf ( "%s\n" , w+4);
continued
10 Section 11.12 Practice Sets
w++;
w++;
printf ( " %s\n " ,w );
printf ("%c\n" , *(w+l));
>
24. What would be printed from the following program
block?
{
char* a[ 5 ] =
{"GOOD" , " BAD " , " UGLY " , " WICKED" , "NICE"};
printf ("%s\n" , a[0]);
printf ( " %s\n " , *(a + 2 ));
printf (" %c\n" , *(a[ 2] + 2));
printf ( " %s\n" , a[3]);
printf ( "%s\n" , a[ 2 ]);
printf ("%s\n" , a[4]);
printf ( "%c\n " , *( a[ 3] +2));
printf ( "%c\n " , *( *( a+4)+3 ));
>
25. What would he printed from the following program block?
{
char c[ ] = " programming " ;
char* p ;
int i?
for ( p = &c[5]; p > &c [0]; P )
printf("%c" , * p);
= —
printf ( "\n");
for ( p = c+5 , i=0 ; p >= c ; p ,i++ )
printf( " %c " , *( p i )); - —
>
Problems
a unction t iat accepts a string ( a pointer to a character) and
^
^
1
I - h, . 1 C 3St 1 araCttr ky moving the null character one position to
the left
27’
deletes the
'
^ l
°“ ^
a character) and
^
1
Chapter 11 Strings / 41
.
30 Write a ( unction that returns the number of times the character is found
in a string. I he ( unction has two parameters . The first parameter is a
pointer to a string. The second parameter is the character to he counted .
.
32 Write a function that , given a string, a width , and an empty string for
output , centers the string in the output area . The function is to return 1
il the formatting is successful and 0 if any errors, such as string length
greater than width , are found .
33. Write a ( unction called newStrCpy that does the same job as strep} . The
declaration ( or your function is to be the same as the library function .
34 . Write a function called newStrCat that does the same job as strccit . The
declaration for your function is to be the same as the library' function .
35. Write a function called newStrCmp that does the same job as stremp . The
declaration for your function is to he the same as the library function .
36. A string is a palindrome if it can be read forward and backward with the
same meaning. Capitalization and spacing are ignored , l or example,
anna and go clog are palindromes. Write a function that accepts a string
and returns true if the string is a palindrome and false il it is not. Test
your function with the following two palindromes and at least one case
that is not a palindrome:
Madam, I ’ m Adam
Able was I ere I saw Elba
.
37 Todays spelling checkers do much more than simply test for correctly
spelled words. They also verify common punctuation . For example, a
period must be followed by only one space. W rite a program that reads a
text file and removes any extra spaces after a period , comma, semicolon ,
or colon . Write the corrected text to a new file.
Your program is to read the data using fgets and parse the strings
using strtok . Print your test data before and after they are run through
your program .
Practice Sets
Projects
.
38 Write a C program that converts
a string representing a number in
Roman numeral form to decimal form . The
symbols used in the Roman
numeral system and their equivalent s are given below.
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
For example , the following are Roman numbers: XII 12 CII 102 ; XL
( ) ( )
( 40 ). The rules for converting a Roman number to a decimal number are
as follows:
a . Set the value of the decimal number to zero.
b. Scan the string containing the Roman character from left to right. If
the character is not one of the symbols in the numeral symbol set , the
.
program must print an error message and terminate Otherwise, con-
tinue with the following steps. ( Note that there is no equivalent
to
zero in the Roman numerals.)
If the next character is a null character ( if the current character is
the last character ), add the value of the current character to the
decimal value.
II the value of the current character is greater than or equal to the
value of the next character, add the value of the current character
to the decimal value.
,
II the value ol the current character is less than the next character
decimal value .
subtract the value ol the current character from the
Solve this project using parallel arrays. Do not solve it using a switch
statement.
39. Rework Project 38 to convert a decimal number to a Roman numeral.
the search-and-replace operation in a text
ftC
e» l i or.
S
e program is to
^atesonly
' muhave three function calls in main . The first
unction prompts the user to type a string of less than 80 characters
. It
un prompts t e user to type the search substring of 10 or fewer charac -
ters Finally, it prompts the user to type the replace substring ot
. 10 or
fewer characters.
° ° .'
ttUrrtnLes are found t returns the
original
characters long
string. Theoreticall y
y’ » h ' new stnng could be 800
Chapter 11 Strings 743
( 80
.
identical characters replaced by 10 characters each ) Your function
must he able to handle overflow by using the realloc function to extend
the new string when necessary. ( Start with an output string of 80 charac-
ters and extend by 80 as necessary. ) The search -and - replace function
returns the address ot the new string.
Alter the search -and - replace function returns, a print function prints
the resulting string as a series of 80 character lines. It performs word -
wrap. I hat is , a line can end only at a space. If there is no space in 80
characters, then print 79 characters and a hyphen , and continue on the
next line.
Write each called function using good structured programming tech -
niques. It is expected to call subfunctions as necessary.
Run the program at least three times:
a . First , run it with no substitutions in the original input.
b. Second , run it with two or more substitutions.
c. Finally, run it with substitutions that cause the output to be at least
three lines, one of which requires a hyphen .
41 . Write a program that "speaks " pig- latin . Words in pig- latin are taken
from English. To form a word in pig- latin , the first letter of the English
word beginning with a consonant is removed and added at the end of the
word , adding the letters ay after the moved consonant . Words that begin
with a vowel are simply appended with ay. Thus, in pig- latin , pig - latin is
igpay-atinlay.
Your program will read a sentence at a time using fgets. It is to parse
the words into strings. As words are parsed, they are to be converted to
pig- latin and printed .
42. Write a program that provides antonyms to common words. An antonym
is a word with the opposite meaning. For example, the antonym of happy
is sad .
The data structure is to he patterned after the Morse code problem
in Section 1 1.7. Given a word , the program is to look for the word on
both sides of the structure and , if found , report the antonym . If the word
is not found , the program is to display an appropriate message.
The program is to use an interactive user interface. The words are to
he read from a dictionary file. Use your dictionary to provide at least 30
antonyms, including at least one from each letter in the alphabet.
Test your program by finding the antonyms for the first word , last
word , and a word somewhere in the middle on both sides of the struc-
ture. Include in your test data at least three words that are not found ,
one less than the first word on the left side , one in the middle some-
where, and one greater than the last word on the right side.
43. The Morse code program in this chapter is a public example of a crypto -
graphic system. We can apply the same techniques to encoding and
decoding any message. For example, we can substitute the letter Z for the
744 Section 11.12 Practice Sets
NZWZN , R ZN ZWZN
MADAM , I AM ADAM
Write a program that encodes and decodes messages using any user-
, include spaces and the
su pnlied code . To make it more difficult to read
common punctuation characters in the code . The code is to be read from
a text file to build the encode/decode array. The user is then given a menu
of choices to encode, decode , or enter a new code from the keyboard .
Test your program with the following code and message and with the
com plete alphabet entered in its encoded sequence so that it prints out
in alphabetical order:
ABCDEFGHIJKLMNOPQRSTUVWXYZ .,?!;
?Q.W ,EMRNTBXYUV!ICO PZA ;SDLFKGJH
Message
WNWLSVPLM , LN A
44. Write a program that parses a text file into words and counts the
number
of occurrences of each word . Allow for up to 100 different words . .After
the list has been built , sort and print it .
45 Modify the program you wrote in Project 44 to eliminate common words
.
such as the , a , an , and , for, and of . You may add other common words to
the list .
Chapter 12
Enumerated, Structure,
and Union Types
We have already discussed three of the six derived types: functions in
Chapter 4, arrays in Chapter 8, and pointers in Chapter 9. In this chapter, we
discuss the three remaining derived types: enumerated, structure, and union.
We also discuss a very useful construct, the type definition. The derived types
are shown in Figure 12 - 1.
Derived
Types i
Function Array Pointer Structure
Type
Chapter 4
Type
Chapter 8
Type
Chapter 9
Type i Union
Type i Type i
Enumerated
Objectives
To introduce the structure, union, and enumerated types
To use the type definition statement in programs
To use enumerated types, including anonymous types, in programs
To create and use structures in programs
(J To be able to use unions in programs
To understand the software engineering concept of coupling and to be able
to evaluate coupling between functions.
746 Section 12.2 Enumerated Types
keyword
One of the more common uses of the type definition is with complex d
Iarations. To demonstrate the concept , let ’s see how we can declare a rat
simple construct, an array of pointers to strings ( a ragged array ). Withoi
type definition , we must define the array as shown below.
char* stringPtrAry[ 20 ];
ec- The color type has four and only four possible values. The range ol the val -
ler ues is 0 . . 3, with the identifier red representing the value 0 , blue the
ta value 1 , green the value 2 , and white the value 3.
Once we have declared an enumerated type, we can create variables from
it just as we can create variables from the standard types. In fact , C allows
the enumerated constants, or variables that hold enumerated constants, to he
used anywhere that integers can he used . The following example defines
ea three variables for our color type.
/.
enum color productColor ;
enum color skyColor ;
enum color flagColor ;
eger 1 . When the defined type directly translates into a standard type as
, with the enumerated type,
died the standard types may be automatically defined .
8 Section 12.2 Enumerated Types
enum color x ;
enum color y ;
enum color z;
x = BLUE ;
y = WHITE;
/ / Error. There is no purple.
z = PURPLE;
Similarly, once a variable has been defined and assigned a value, we can
store its value in another variable of the same type. Given
the previous exam-
ple , the following statements are valid .
x = y;
z y;
if (colorl == BLUE )
Because enumerated types are derived from integer types, they may be used
the
in the case expressions. For example, consider an enumerated type lor
months of the year.
enum months
{JAN ,FEB,MAR ,APR ,MAY,JUN,JUL,AUG,SEP,OCT,NOV ,DEC}
;
switch (dateMonth )
{
case JAN: .
break ;
case FEB: .
break ;
> // switch
int x;
enum color y ;
x = BLUE; // Valid , x contains 1
y = 2; // Compiler warning
enum color y ;
y = ( enum color)2 ; // Valid , y contains blue
enum months
{JAN ,FEB , MAR , APR ,MAY ,JUN ,JUL ,AUG ,SEP ,OCT,NOV,DEC};
Note that we don ’t have to assign initializers to every value. If we omit the
initializers, the compiler assigns the next value by adding 1. To initialize the
months, therefore, we simply assign the value for JAN . The rest will be auto-
matically assigned by the compiler.
C also allows us to assign duplicate values to identifiers. For example, we
could assign similar colors identical values as shown in the next example.
To emphasize the point , even though RED in Color ( see page 747) has
the same value as RED in myColor, the two types are different and cannot be
used together unless one of them is cast.
wrote a program that prints out the cable television channels for our local
cable provider. Since the channels are changing continually, using their
names in an enumerated type makes it easy to change them and to follow the
code. The implementation is seen in Program 12- 1 .
Results:
Here are my favorite cable stations:
ABC: 11
CBS: 5
CNN: 51
ESPN: 39
Fox: 2
HBO: 15
Max: 31
NBC: 4
Show: 17
End of my favorite stations.
752 Section 12.3 Structure
Input/Output Operations
Because enumerated types are derived types, they cannot be read and written
using the formatted input/output functions. When we read an enumerated
type , we must read it as an integer. When we write it , it displays as an integer.
When necessary, C implicitly casts variables as described in " Enumeration
Type Conversion" earlier in the chapter.
The following example shows how we would read and write two months.
12.3 Structure
Today’s modern applications require complex data structures to support
them. For example, the structures needed to support the graphical user inter-
lace found in computer workstation windows obviously are very complex. So
complex, in fact, that they could not be built using the relatively primitive
data types we have seen so far. A much more powerful capability is needed.
I his is the role of the structure.
A structure is a collection of related elements, possibly of different types,
having a single name. For example, consider the file structure you have been
using ever since you wrote your first program that used files . The file table is
a type-defined structure, FILE.
Each element in a structure is called a field . A field is the smallest ele-
ment ol named data that has meaning. It has many of the characteristics of
the variables you have been using in your programs . It has a type , and it exists
in memory. It can he assigned values, which in turn can he accessed lor selec -
tion or manipulation . A field clillers Irom a variable primarily in that it is part
ol a structure.
We have studied another derived data type that can hold multiple pieces
ol data , the array. I he
difference between an array and a structure is that all
Chapter 12 Enumerated, Structure, and Union Types 753
elements in an array must be of the same type, while the elements in a struc-
ture can be of the same or different types .
Figure 12 - 3 contains two examples of structures. T he first example,
fraction, has two fields, both of which are integers . The second example,
student , has three fields, made up of two different types.
numerator denominator
fraction
structure structure
id name gradePoints
student
Format Example
A tagged structure starts with the keyword struct. The second element in
the declaration is the tag. The tag is the identifier for the structure, and it
allows us to use it for other purposes, such as variables and parameters. If we
conclude the structure with a semicolon after the closing brace, no variables
are defined. In this case, the structure is simply a type template with no asso-
ciated storage.
Format Example
Variable Declaration
After a structure has been declared, vve can declare variables using it. Gener-
ally, we declare the type in the global area of a program to make it visible to all
functions. The variables, on the other hand, are usually declared in the tunc *
lions, either in the header or in the local declarations section. Figure 12-6
demonstrates the declaration ot a structure type and variables that use it.
Chapter 12 Enumerated, Structure, and Union Types 755
Initialization
We can initialize a structure. The rules for structure initialization are similar
to the rules for array initialization. The initializers are enclosed in braces and
separated by commas. They must match their corresponding types in the
structure definition. Finally, when we use a nested structure ( see " Nested
Structures" later in this section), the nested initializers must be enclosed in
their own set ol braces.
Figure 12 - 7 shows two examples of structure initialization. The first
example shows an initializer for each field. Note how they are mapped to the
structure in sequence. The second example demonstrates what happens
when not all fields are initialized. As we saw with arrays, when one or more
initializers are missing, the structure elements will be assigned null values,
zero for integers and floating-point numbers, and null ( ' \ 0 ' ) for characters
and strings.
typedef struct
{
int x;
int y;
float t;
char u;
} SAMPLE;
2 5 3.2 im 7
t
0.0 \0 |
u
X y t u X y
filled with
float zero
Accessing Structures
Now that wc have discussed how to declare and initialize structures, it’s time
to see how we can use them in our programs. We will first describe how to
access individual components of a structure and then examine the assign-
ment of whole structures . After looking at how pointers are used with struc-
tures , we conclude with a program that uses structures.
aStudent.id
aStudent.name
aStudent.gradePoints
We can also read data into and write data from structure members just as
we can from individual variables. For exam pie , the value for the fields of tin
Chapter 12 Enumerated, Structure, and Union Types 757
sample structure can be read from the keyboard and placed in saml using the
following scanf statement. Note that the address operator is at the beginning
of the variable structure identifier.
typedef struct
{
int total;
int ary[ 10];
} DATA ;
DATA sales;
sales.total++; // interpreted as (sales.total)++
fun( sales.total ); // sales.total passed to fun
fun( sales.ary[3 )); // ( sales.ary )[ 3 ] passed to fun
Now let 's examine cases in which the direct selection operator is used
.
with an operator of lesser precedence In these cases, the direct selection is
applied first.
++sales.total ; // interpreted as ++(sales.total )
fun(&sales.total ); // &( sales.total ) passed to fun
Results:
Key first fraction in the form of x/y: 2/6
Key second fraction in the form of x/y: 7/4
Program 1 2 - 2 Analysis Although this iis a very simple program, several points are of interest.
.
1 Note that we have coded the typedef in the global area before main. This is
the first time that
— we have included statements here other than preprocessor
'
the typed definition statements there so that they are in scope for the entire
compilation unit.
2. Note that the name of the typed definition is all UPPERCASE. This is another
C style tradition. It warns the reader that there is something special about this
type, in this case that it is a typed definition.
3. Now examine the scanf statements. Since we need a pointer for scanf, we must
use the address operator as well as the direct selection operator. As you can see,
we have not used the parentheses around the field name. The reason is that the
direct selection operator ( . ) has a higher priority ( 16) than the address operator
( 1 5) . In other words, the expression
&frl.numerator
&(fr.numerator)
Operations on Structures
I he entity that can be treated as a whole. However, only one
structure is an
operation, assignment is allowed on the structure itself. That is, a structure
can only he copied to another structure of the same type using the assign-
ment operator.
Rather than assign individual members when we want to copy one
structure to another, as we did earlier, we can simply assign one to the other.
Figure 12- 9 copies saml to sam2
Before
| A
7 3 MU [El t
3 -2
u
X y t u
sam2 saml
sam2 saml ; j
2 5 32
~
|[ Al 2 5 3.2 im
y t u X y t u
X
sam2 saml
After
Slack Bytes
It is interesting to examine why we cannot compare two structures. Sometimes hard-
ware requires that certain data, such as integers and floating- point numbers, be
aligned on a word boundary in memory. When we group data in a structure, the
arrangement of the data may require that slack bytes be inserted to maintain these
boundary requirements. For example, consider the structure shown below. In this .
Since these extra bytes are beyond the control of the program, we cannot guarantee
what their values will be. Therefore, if we were to compare two structures and their
first components were equal, the inserted slack bytes could cause an erroneous com-
pare result, either high or low, if they weren' t equal. C prevents this problem by not
allowing selection statements with structures. Of course, if we really need to compare
structures, we can simply write a structure compare function of our own that would
compare the individual fields in the structure.
Pointer To Structures
Structures, like other types, can also be accessed through pointers. In fact,
this is one of the most common methods used to reference structures. For
example, lets use our SAMPLE structure with pointers (Figure 12 - 10).
( *ptr) . x j ( * ptr).u
typedef struct sam 1
{
int x;
V
int y; X t U
float t; ( * ptr). )
t
char u; ( *ptr ).y
} SAMPLE;
SAMPLE saml ;
SAMPLE* ptr ;
u
ptr
ptr = fitsaml ;
saml . x
SAMPLE* ptr ;
We now assign the address of saml to the pointer using the address oper-
would with any other pointer.
ator ( & ) as we
ptr = &saml ;
Now we can access the structure itself and all the members using the
pointer, ptr. I he structure itself can be accessed like any object using the
indirection operator ( * ) .
Since the pointer contains the address ol the beginning of the structure,
we no longer need to use the structure name with the direct selection opera-
tor. I he pointer takes its place. Phe reference to each of the sample mem-
hers is shown below and in Figure 12 - 10.
.
Note the parentheses in the above expressions They are absolutely
necessary', and to omit them is a very common mistake. The reason they are
needed is that the precedence priority of the direct selection operator ( . )
is higher than the indirection operator ( * ) . If we forget to put the paren-
theses, C applies the dot operator first and the asterisk operator next . In
other words,
(*pointerName).fieldName -
pointerName >fie1dName
762 Section 12.3 Structure
t u
The token for the indirect selection operator is an arrow formed by the minus
.
sign and the greater than symbol (->) It is placed immediately after the pointer
identifier and before the member to he referenced. We use this operator to refer
to the members of our previously defined structure, saml, in Figure 12 - 12.
-
typedef struct saml
int x;
int y; L y t u
float t; ptr
char u;
} SAMPLE;
SAMPLE saml; saml .X ptr -> x
SAMPLE* ptr ;
Direct Selection Indirection Indirect Selection
ptr = Srsaml ;
Three Ways to Reference the Field x
Results:
14:38:57
14:38:58
14:38:59
14:39:00
14:39:01
14:39:02
Complex Structures
As mentioned earlier, structures were designed to deal with complex problems.
I he limitations on structures are not on the structures themselves but on the
imagination ol the soitware engineers who solve the problems. Structures
within structures (nested structures ) , arrays within structures, and arrays ol
structures are all common . We deal with the first two here and arrays of struc-
tures in the next section.
Chapter 12 Enumerated, Structure, and Union Types 765
Nested Structures
We can have structures as members of a structure. When a structure includes
another structure, it is a nested structure . There is no limit to the number of
structures that can be nested , but we seldom go beyond three.
For example , we can have a structure called stamp that stores the date
and the time . The date is in turn a structure that stores the month , day, and
year. I he time is also a structure that stores the hour, minute, and second .
This structure design is shown in Figure 12 - 13.
stamp.date.month stamp.time.sec
stamp
date time
stamp.date stamp.time
There are two concerns with nested structures: declaring them and refer-
encing them .
// Type Declaration
typedef struct
{
int month;
int day ;
int year ;
> DATE;
typedef struct
{
int hour ;
int min;
int sec ;
> TIME;
typedef struct
{
DATE date ;
TIME time;
> STAMP;
// Variable Declaration
STAMP stamp;
It is possible to nest the same structure type more than once in a dec-
laration . For example, consider a structure that contains start and end
.
times for a job Using the STAMP structure, we create a new declaration as
shown below.
// Type Declaration
typedef struct
{
STAMP startTime;
STAMP endTime;
> JOB;
// Variable Declaration
JOB job ;
^^ nL 7dWtr
!„felC olZ7ructuZ
^ ytZl
a
Jtif ’ T ,,
se
* flcxil lty > n working with them . For example, with DATE
.
)
declared St' l> tlrate tyPe definition , it is possible to pass the date structure
to a fun c ' w thout having to pass the rest of the stamp structure.
funetton
? ” “*
of r e f r e n c
'
Chapter 12 Enumerated, Structure, and Union Types 767
stamp
stamp.date
stamp.date.month
stamp.date.day
stamp.date.year
stamp.time
stamp.time.hour
stamp.time.min
stamp.time.sec
job.startTime.time.hour
job.endTime.time.hour
// Global Declarations
student.name[ student.midterm
^])
typedef struct
{
char name[ 26 ];
int midterm[3 ]; I II 111 -..
name midterm final
int final ;
} STUDENT ; student.final
student
// Local Declarations
STUDENT student ;
student
student.name
student.name[ i ]
student.midterm
student.midterm[ j]
student.final
We have already seen how to refer to fields in a structure using the indi-
rect .
selection operator (->) When one structure contains an array, we can
.
use a pointer to refer directly to the array elements For example, given a
pointer to integer, pScores, we could refer to the scores in student as
shown below.
pScores = student.midterm ;
totalScores = * pScores + *( pScores + 1 ) + *( pScores + 2);
Note that the name is initialized as a string and the midterm scores art
simply enclosed in a set of braces.
Chapter 12 Enumerated, Structure, and Union Types 769
lo assign the month May to the structure, we would use the following
statement. Note that we are copying a pointer to a string, not the string
itself .
stamp.date.month = may ;
typedef struct
{
char* month;
int day ;
int year ;
} DATE;
rrrrrrrrrrrrrrrrrq
i i i i i i i i i i i i i i i
i t
• ' I
i i i i i i i i
stamp
Array of Structures
In many situations, we need to create an array of structures. To name just
one example, we would use an array of students to work w ith a group of stu -
dents stored in a structure. By putting the data in an array, we can quickly
and easily work with the data, to calculate averages , lor instance.
Let ’s create an array to handle the scores for up to 50 students in a class.
Figure 12 - 16 shows how such an array might look.
stuAry [ 0 ]
stuAry [ 1 ]
stuAry [ 2 ]
stuAry [ 49 ]
stuAry
Since a structure is a type, we can create the array just as we would cre-
ate an array of integers. The code is shown below .
STUDENT stuAry[ 50];
Study this array carefully. Note that it is an array that contains two other
arrays , name and midterm. 1 his is not a multidimensional array. To be a mul-
tidimensional array, each level must have the same data type. In this case,
each type is different: the stuAry is STUDENT , while name is character, and
midterm is integer.
lo access the data lor one student , we refer only to the structure name
with an index or a pointer as shown below.
stuAry[i] *pStu
lor example, lets write a short segment of code to compute the average
loi the final exam . We use a for loop since wre know the number of students in
the array.
int totScore = 0;
float average ;
STUDENT* pStu ;
STUDENT* pLastStu ;
continued
Chapter 12 Enumerated, Structure, and Union Types 77 j
average = totScore
- 50.0;
totScore += pStu >final;
/
stuAry[4 ].midterm[ 1 ]
STUDENT* pLastStu ;
pLastStu = stuAry + 49 ;
for ( int i = 0; i < 3; i++ )
{
sum = 0;
for ( pStu stuAry ; pStu <= pLastStu; pStu++)
=
-
sum += pStu >midterm[ i ];
midTermAvrg[i] = sum / 50.0;
> // for i
index oper-
The Precedence Table ( see inside front cover ) shows that the
and the indirect selection operator all have
ator, the direct selection operator,
y is from left to right . So we do
the same precedence and that the associativit
not need parentheses to total the scores for a midterm .
Ik
'i
772 Section 12.3 Structure
41
-
pStuPtr >name,
42
-
pStuPtr > midterm[0],
-
pStuPtr >midterm[ 1 ],
43
44
-
pStuPtr >midterm[ 2],
-
pStuPtr >final);
continue
Chapter 12 Enumerated, Structure, and Union Types 113
Sorted data:
Adams, Karin 75 91 89 89
Charles, George 85 94 79 93
Chavez , Maria 83 79 93 91
Nguyen , Tuan 87 88 89 90
Oh , Bill 78 96 88 91
Program 1 2 4 Analysis This program follows the insertion sort design we studied in Chapter 8 (Program 8- 11).
-
There are only two significant changes: First, all the array handling is done with
pointers, and second, the compare logic was changed to use the strcmp function.
To test the sort, we wrote a small driver that uses an initialized array for its data. To
verify that the sort works, we printed the data before and after the sort process.
.
3 I he address of a structure or member can be passed, and the function
can access the members through indirection and indirect selection
operators.
res.numerator =
k fr 1
numerator denominator
multiply( frl.numerator , fr2.numerator ) fr2
res.denominator =
multiply( frl .denominator , fr2.denominator ); numerator denominator
res
numerator denominator
// ============= = =
multiply =====
int multiply ( int x, int y )
{
return x * y ; X y
> // multiply
As you can sec from Figure 12 - 17, the only difference is that we must
for the
use the direct selection operator to refer to the individual members
actual parameters The .
called program doesn ’t know if the two integers are
Let us rework the multiply fractions program to pass structures In this case, .
we have written the complete program. The design is shown in Figure 12- 18,
and the code is shown in Program 12 - 5.
main
fr 1 res
i
numerator denominator numerator denominator
i
A
fr 2
numerator denominator —i
i
I ( returned)
r
i
multFr (copied) i
i
i
frl I
I res
numerator denominator numerator denominator
fr2
numerator denominator
continued
Chapter 12 Enumerated, Structure, and Union Types 111
n
778 Section 12.3 Structure
Program 1 2 - 5 Analysis There are five points you should study in this program.
1. The fraction structure is declared in global memory before main. This makes it
visible to all the functions.
2. In getFr, the data are read by using the address operator and the direct selec -
tion operator. Since the direct selection operator (. ) has a higher priority, they
can be coded without parentheses.
.
3 In getFr we pass back two values in the structure without using pointers. This is
another advantage of structures: We can return more than one piece of data
when we put the data in a structure.
.
4 The structure type is passed using the FRACTION type definition.
5. The references to the data in multFr and printFr must use the direct selec -
tion operator to get to the individual members.
programming while at the same time being efficient ( see “Software Engi -
neering" at the end of the chapter) .
W hen the structure is very large, however, efficiency can suffer, espe-
.
cially with a heavily used function I herefore, we often use pointers to pass
.
structures It is also common to pass structures through pointers when the
structure is in dynamic memory. In these cases, all we have to pass is the
pointer.
We modify the multiply fractions program once more to pass the struc -
tures using pointers. The memory flow is shown in Figure 12 - 19 and the cod-
ing in Program 12 -6.
fr1 res
numerator denominator numerator denominator
fr2
numerator denominator
-
pRes >numerator = pFrl->numerator * pFr2->numerator;
-
pRes >denominator = pFrl >denominator * pFr2 >denominator ;
return ;
- -
} // multFr
. Note
Program 1 2 -6 Analysis In this version of the program, the structure is passed and returned asWe reproduce it
a pointer
the syntactical notation for reading the data in the getFr function.
below for your convenience.
.denominator );
scanf( " %d/%d " , & pFr->numerator , &(*pFr )
12.4 Unions
The union is a construct that allows memory to he shared by different types
of data. This redefinition can be as simple as redeclaring an integer as four
characters or as complex as redeclaring an entire structure. For example, we know
that a short integer is 2 bytes and that each hvte is a character. Therefore, we could
process a short integer as a number or as two characters.
The union follows the same format syntax as the structure In fact, with .
the exception of the keywords struct and union , the formats are the same.
Figure 12 - 20 shows how we declare a union that can he used as either a
short integer or as two characters.
»
i chAry [ 0] chAry[1]
union shareData
i
'A'
i
I B‘ '
i
I
i
{
char chAry[ 2 ];
short num; l
16706 l
i i
}; i num i
Both num and chAry start at the same memory address. chAry[0]
occupies the same memory as the most significant byte of num.
FIGURE 12 - 20 Unions
Referencing Unions
I he rules for referencing a union are identical to those for structures. To ref *
erence individual fields within the union, we use the direct selection ( dot
operator. Each reference must he fully qualified from the beginning of the
'
structure to the element being referenced. This includes the name of the
union itself. When a union is being referenced through a pointer, the selec *
lion operator ( arrow ) can be used. The following are both valid references to
the union shown in Figure 12- 20.
shareData.num
shareData.chAry[ 0 ]
Initializers
\ \ bile C permits a union to be initialized, only
the first type declared in UH
union can be initialized when the variable is defined. The other types can
only be initialized by assigning values or reading values into the union. When
initializing a union, we must enclose the values iin a set of braces, even if
there is only one value.
Program 12- 7 demonstrates unions in a program. It uses the structure in
Figure 12 - 20 to print a variable, first as a number and then as two characters.
Chapter 12 Enumerated, Structure, and Union Types 783
Results:
Short: 16706
Ch[0]: A
Ch[1]: B
field . On the other hand , when we store an individuals name, the name has
at least —
three parts first name, middle initial , and last name. If we want to
have only one name field in our address hook, we need to use a union to store
the name. This design is shown in Figure 12 - 21 . Note that in the figure, unis
the member name to access the union structure.
typedef struct
{ NAME
char first[ 20]; type un
char init;
char last[ 30 ]; company
} PERSON ;
typedef struct
{
char type ; person
union first init last
{
char company[40];
PERSON person ;
} un ;
> NAME ;
Because two different types of data can he stored in the same union, we
need to know which type currently is stored . This is done with a type flag that
is created when the data are stored. If the name is in the company format , the
flag is C, and if it is in the person format it is a P.
\ \ hen a union is defined , C reserves enough room to store the largest
data object in the construct . In Figure 12- 21 , the size of the company name
is 40 bytes and the size of the person name is 5 1 bytes. The size of the union
structure is therefore 52 bytes ( the size of the person name plus the type !
regardless of what type of data are being stored. When a company name is
stored , the last 1 1 bytes are present hut unused .
Program 12-8 demonstrates the use of a union in a structure by printing
each format .
Results:
Company: ABC Company
Friend: Martha C Washington
Program 1 2 - 8 Analysis We begin by creating two union structures, one for a business and one for a friend.
To initialize the business union, we used initializers ( see below ). To initialize the friend
union, we used string copy and assignment. Once the unions were created, we cop-
ied them to an array to demonstrate one common processing concept.
Unions are often processed with a switch statement that uses the type to determine
what data format is currently being processed. Note that it is the programmer's respon-
sibility to process the data correctly. If a business identifier is used while person data
are present, the program will run but garbage will be produced.
Once the correct union type has been determined, the rest of the processing is rou-
tine . We simply print the data in the union using the appropriate identifiers for its con-
tent. Note that we provide a default statement even though there is no possibility of an
error in this simple program.
short 16706
0100 0001 0100 0010
data[0] data[0]
( A)1
St ,gn,bcant
-
e is stored on the right. This exam
pie is seen on theTiqhMn the L °
-
6
^ timempUter
This |
, data [ 0 ] contains ' '
B and
' °^
]
checkV iWslltT^ ° is b 9 r
^
C
then run Program 12-7 and t
Chapter 12 Enumerated, Structure, and Union Types 787
Internet Addresses
As a final example of union processing, let ’s consider how Internet addresses
are lormatted and processed. We are most familiar with the Internet domain
names such as www.deanza.edu. These names are easy to remember and
use. I he real Internet Protocol (IP) address, however, is defined as dotted-
decimal notation, as shown below.
153.18.8.105
Results:
IP decimal dot : 153.18.8.105
IP binary address: 2568095849
loor buttons
(IN / O U T ) ,
currentFloor
The elevator design is shown two different ways . Figure 12- 23 is a struc -
ture chart for the program .
Figure 12 - 24 is a state diagram for the elevator. A state diagram
is a
design technique that is often used with realtime systems to show how a sys -
state to another . For an elevator , it can be in one of three
tern moves from one
states: moving up, moving down , or stopped .
Each of these states is repre -
sented by a circle in the diagram. To move from one state to another, a
change must occur in the elevator environment . For example, to change from
the stop state to the up state , a button must be pressed . This is reflected on
the line between stop and up as anyUp.
790 Section 12.5 Programming Application
elevator
run
initialize Elevator terminate
move
I
anyllp anyDown
request request
moveUp move
Down
timePass j timePass j
FIGURE 1 2 - 23 Elevator Structure Chart
up down
lanyDown & & anyllp
stop
Program 1 2-12 Analysis The simulation is controlled by runElevator. The technique we use to prevent
invalid user input is interesting . After reading the input stream , we use a pointer to
parse the string after skipping whitespace . If the current character is a digit, we use
sscanf to format the digit(s) as an integer. If it is not a digit we print an error mes-
sage and skip it. (Of course, in a real elevator, it is impossible to get an invalid floor.)
Then , after converting the digit string to an integer, we synchronize the buffer and our
pointer with a small while loop .
As we parse the integers in the input stream , we " push" their corresponding but-
tons in our data structure. This code is shown at statement 45 in Program 12-12.
Program 12- 13 moves the elevator to a requested floor.
Program 1 2- 1 3 Analysis The move function is one of the more interesting functions in the simulation. At any
given point in the operation, the elevator can be in one of three states: moving up,
moving down, or stopped. We use an enumerated type to track the state.
If the elevator is moving up, we continue up as long as there are requests for
higher floors. If there are none, we test for down requests. If there are any down
requests, we change the state to down and proceed. If there are no up requests
and
no down requests , we change the state to stopped. This logic is seen in statements 21
through 47 of Program 12- 13. Note the use of the blocks to ensure that the if and
else statements are properly paired.
Program 12- 14 simulates the elevator movement as it moves up and
down.
27 elev->currentFloor );
28 printf("\n " );
29 timePass(4 );
30 return;
31 > // moveUp
32
33 /* ===== =============== moveDown
34 This function simulates the movement of the elevator
35 when it is going down.
36 Pre given moving down elevator
37 Post down simulation is displayed on the screen
38 */
39 void moveDown (ELEVATOR * elev )
40 {
41 // Statements
42 printf( " \nThe door is being closed ..." );
43 printf("\nWe are going down" );
44
45
-
(elev >currentFloor) ;
—
46
-
while (elev >buttons [elev >currentFloor]
- W
{
47 printf( " \n" );
48 timePass ( 2);
49 printf("\nPassing floor %d", elev->currentFloor
50 printf("\n");
51 timePass ( 2 );
52
-
( elev >currentFloor ) ;
— contititw
Chapter 12 Enumerated, Structure, and Union Types 797
Program 1 2 - 1 4 Analysis Both moveUp and moveDown operate similarly. They move past the current floor in
the correct direction and "call out" the floors as we pass them. A timing loop is
included to simulate the time it takes the elevator to reach the next floor.
Program 1 2 - 1 5 contains two functions: anyUpRequest checks for floors
above the current position; anyDownRequest checks for floors below the cur-
rent position.
16 check++)
17 isAny = (elev > buttons[check ] = = IN );
-
18 return isAny ;
19 } // anyUpRequest
20
anyDownRequest =================
=====
21
This function checks to see if any request is for a
22
continued
798 Section 12.5 Programming Application
39 > // anyDownRequest
Program 12- 15 Analysis Compare the for loops in anyDownRequest and anyUpRequest. One is much
more efficient than the other. Do you see why? In anyUpRequest, we stop when we
find the first request for a floor above the current one. In anyDownRequest, we
check all floors below the current one, even if the first one has been requested. This
means we always examine all lower floors.
Note the logic to set isAny at statement 37 in Program 12- 15. The current setting
is or ed with a button. If either are true, the result is true. Thus, once we find a floor has
been requested, isAny will remain set regardless of the settings of the other buttons.
This is a common technique to analyze a series of logical values.
Program 12 - 16 contains a loop to simulate time passing. We use a simple
for loop .
continueA
Chapter 12 Enumerated, Structure, and Union Types 799
Program 12 - 16 Analysis The factor in the timing loop depends on the speed of your computer. In this case, we
use 10,000. On a faster computer, you need to make the factor larger. On a slower
computer, you need to make it smaller.
Note how we code the null statement in the for loop . Putting the null statement on a
line by itself makes the function more readable. If the null statement were put at the end
of the for statement, it would be too easy to think that the next statement, the return in
this function, were part of the for loop.
I he termination code is shown in Program 12 - 17 .
PROGRAM 12 - 17 Elevator: Terminate
1 ==== terminate ===
2 Release the memory occupied by buttons ,
3 Pre given elevator
4 Post elevator memory is released
5 */
6 void terminate ( ELEVATOR * elev )
7 {
8 // Statements
9 free (elev->buttons);
10 return;
11 } // terminate
12 ===== End of Program ====
a program, we
Program 12 - 17 Analysis Although we do not need to release memory when terminating
include the logic to do so for completeness.
800 Section 12.6 Software Engineering
Coupling
In Chapter 1 1 , we discussed a concept known as functional cohesion , a mea -
sure of how closely related the processes are within a function. A related
topic , coupling, is a measure of how tightly two functions are hound to each
other. The more tightly coupled they are, the less independent they are . Since
our objective is to make the modules as independent as possible, we want
them to he loosely coupled .
Loose coupling is desirable for several reasons:
1 . Independent —that is, loosely coupled —functions are more likely to be
reusable.
2. Loosely coupled functions are less likely to create errors in related func-
tions; conversely, the tighter the coupling, the higher the probability that
an error in one function will generate an error in a related function .
——
3. Maintenance modifications that is, modifications required to imple-
ment new user requirements are easier and less apt to create errors
w ith loosely coupled functions .
In his book on designing structured systems , Page-Jones describes five
types of coupling. We review them here. For an in -depth discussion of the
concept, reler to Chapter S in Page-Jones s book , The Practical Guide to
Structured Systems Design . 2
Data Coupling
Data coupling passes only the minimum required data from the calling func-
tion to the called function . All required data are passed as parameters, and no
extra data are passed . This is the best form of coupling and should be used
whenever possible.
When you write simple functions that work on on y one task the cou ,
.
*
We could have fallen into the trap of passing extra parameters by passing
the function , the array, and the index locations of the two integers to be
exchanged . The function would have worked just as well, but the coupling
woidd not have been as loose. Now it requires an array of integers instead of
just integers, furthermore , we could have made the coupling even tighter
had we referred to the maximum size of the array using the precompiler dec -
laration S I Z E. At this point , it is highly questionable whether the function
could be used in another program .
Stamp Coupling
functions are stamp coupled if the parameters are composite objects such as
arrays or structures. Most ol the functions in the selection sort in Chapter 10
.
use stamp coupling because they pass the array ( Although it could be argued
that we are passing only a pointer to the array, the intent is to modify the
array. We are, therefore , passing the array for the purposes of this discussion . )
You should now be arguing, “ But we have to pass the array! ” Yes, that is
true. Stamp coupling is not bad and is often necessary. The danger with
stamp coupling is that often it is just too easy to send a structure when all the
data in the structure are not required . When extra data are sent , we begin to
open the door for errors and undesired side effects.
Consider the time stamp described in Figure 12- 15 . This structure con -
tains two nested structures, date and time . If we were to use these data , for
example , to print the date in a report heading, and passed the whole struc-
ture , we would send too much data ! In addition , if we were to pass the struc-
ture by address rather than by value , we risk the possibility of an error in one
function accidentally changing the data in the structure and causing a second
error. The correct solution is to pass only the data that are needed and then
only by value when possible.
“ n
“ ""'
Chapter J 2 [numerated, Structure, and Union Types 803
Content Coupling
The last type of coupling is very difficult , hut not impossible , to use in C.
Content coupling occurs when one function refers directly to the data or
statements in another function . Obviously, this concept breaks all the tenets
of structured programming.
Data Hiding
We have previously discussed the concept of global and local variables. In
the
, we pointed out that anything placed before main was said to be
discussion
in the global part of the program . With the exception
of data that need to be
other compile units, no data need to be placed in this
visible to functions in
section .
that the internal
One of the principles of structured programming states
data structure should he hidden from the user’s view . The two terms you
usu ally hear are data hiding and data encapsulation . Both of these principles
objective protecting data from accidental destruction by parts
have as their
of your program that don ’t need access to the data . In other words , if a part
data do its job , it shouldn ’t be able to see
of your program doesn’t need to
the data .
Programming Standard:
Vo not place any variables in the global area
of a program .
804 Section 12.6 Software Engineering
mam
—
Any variables placed in the global area of your program that is , before
an be used and changed by every part of your program . This is in
direct conflict with the structured programming principles of data hiding and
data encapsulation .
Summary
We have described five different ways that two functions can communicate.
The first three are all valid and useful , although not without some dangers.
These communication techniques also provide data hiding. Data coupling is
universally accepted and provides the loosest communication between two
functions. Stamp and control coupling present some dangers that must be
recognized . When using stamp coupling, do not pass more data than are
—
required . Keep control coupling narrow that is , between only two functions.
You should always avoid the last two , global and content coupling. They do
not protect the data .
Chapter 12 Enumerated, Structure, and Union Types 805
( * ptr ).mem
3. The indirect selection operator ( - > ) is one token . Do not put a space
between its symbols ( between - and > ).
4. Io access a member in an array structure, you need the index operator.
For example, the correct expression to access a member named mem in an
array of structure named ary is
ary[i].mem
5 . The type name in a tvpedef comes after the closing brackets and before
the semicolon :
typedef struct
{
... r
... ,
_
} TYPE NAME;
6 . You cannot define a variable at the same time that you declare a type def -
inition . In other words, you are not allowed to do the following:
typedef struct
{
... ,
... ,
} TYPE NAME variable name;
_ // ERROR
7. You may not use the same structure name with a tag inside structure
a .
The following declaration is not valid :
8. A union can store only one of its members at a time. You must always
keep track of the available member. In other words, storing one datatype
in a union and accessing another data type is a logic error and may be a
serious run - time error.
9. Although structures can be assigned to each other, it is a compile error to
assign a structure of one type to a structure ol a different type.
10. It is a compile error to use a structure type with only its tag; that is, with-
out the keyword struct, as shown below.
struct stu
{
>;
stu aStudent ; // Compile Error
struct stu aStudent; // Correct Code
.
11 It is a compile error to compare two structures or unions, even if they arc
ol the same type .
.
12 It is a compile error to refer to a structure member without qualification,
such as id rather than student ,id.
.
13 It is a compile error to omit a level of qualification when structures arc
.
nested For example, in Figure 12 - 13, it is an error to omit the time qual-
ifier in the following reference.
stamp.time.min
14. It is a compile error to forget the address operator with scanf when refer-
ring to anonstring member. The pointer is the address ol the structure,
not .
the address of the member
13. Referencing an identifier in a union when a different type is active is a
logic error and may cause the program to fail.
16. It is a compile error to initialize a union w ith data that do not match the
type of the first member.
12.9 Summary
An enumerated type is built on the standard type, integer.
In an enumerated type, each identifier is given an integer value.
A structure is a collection of related elements, possibly of different types,
having a single name.
Each element in a structure is called a field.
One difference between an array and a structure is that all elements in an
array must he of the same type while the elements in a structure can be of
the same or different types.
We have discussed two different ways to declare and/or define a structure:
tagged structure and type-defined structure.
A structure can be initialized when it is defined. The rule for structure ini-
tialization is the same as for array initialization.
LI We can access the members of a structure using the direct selection
operator ( )..
Structures can he accessed through a pointer. The best way to do this is to
use theindirect selection operator ( - > ).
The following two expressions are the same if ptr is a pointer to a
structure:
.
( *p ) x p- >x
.
9 Which of the following statements about enumerated types is true?
a. Declaring an enumerated type automatically creates a variable.
b. Declaring an enumerated variable without a tag creates an enumer-
ated type.
c. Enumerated types cannot be used in a type definition.
d. I he enumerated values are automatically assigned constant values
unless otherwise directed.
e. I he identifiers in an enumerated type are enumeration variables.
10. A ( n) is the smallest element of named data that has
meaning.
a. array
b. field
c. record type
d. structure ( struct )
.
e type
I 1 . Which of the following statements about structures ( struct ) is true ?
.
a A structure without a tag can be used as a derived type.
b. Structures are derived types based on the integer standard type.
.
c The fields in a structure must be of the same type.
.
d C normally uses two different structure declarations: tagged and
type-defined.
.
12 The structure creates a type that can be used later to
define variables.
.
a array
b. record- typed
.
c tagged
d. type-defined
e . variable
1 3. Given a structure variable named stu. whose type is STU, which contains
a field named major , which of the following statements correctly
refers
to major?
a. major
b. stu major-
c. stu.major
d. STU-major
e. STU.major
containing a field name, which of
14. Given a pointer, ptr, to a structure stu
the following statements correctly references name?
a. ptr.name
b. ptr->name
c. ptr.stu.name
d. ptr >stu >name
-
e. ptr >stu.name
-
-
Section 12.10 Practice Sets
.
1 5 A(n) _ is a construct that allows a portion of memory
to be shared by different types of data.
.
a array
.
b field
.
c struct
d. union
e. variable
.
16 Determine which of the following statements are true and which are false:
.
a A structure cannot have two Reids with the same name.
.
b A structure cannot have two fields of the same type .
.
c A structure must have at least one field.
.
d A field in a structure can itself be a structure.
.
17 Determine which of the following statements are true and which are false:
a. A union can have another union as one of the fields .
b. A structure cannot have a union as one of its fields.
c. A structure cannot have an array as one of its elements.
d. W hen accessing the fields of a structure through a pointer p, the fol-
lowing two expressions are the same:
Exercises
18. Declare a tagged structure for a student record consisting of five fields:
student ID ( integer ) , first name ( a dynamically allocated string), last
name (a dynamically allocated string), total credits completed ( integer ) ,
and accumulated grade point average ( floating- point ). A graphic repre -
sentation of the structure is shown in Figure 12 - 25 .
i
t
i
i
H [IJEIlllLiJ
_
'
_ _ . _ _ _ [_ T. _ :
fTTTTTlTt
i i i i i l. i LJ
-
partNo descr
i reOrder onHand unitMeas price
<
l
__
i
i
I
i
i
i
i
i
i
i
i
I l
_ II _ |L i
i
i
i
[0 ]
month alphaMonth days
7T Tir T7 1
U l - - -J i
i i i
r
i i
i i i
7i
LI
[ in
month alphaMonth days
_ ••_ _
l
i
i
l LV
_ _ _•_
J
»
i
l l
'
*: : : :;;.7 TTi
month days activity
i I
l
I
I
I
l
_V
l
l -J
I i
i . _ - -- LI
I
'i «
i
m ' '
' -i
_ _ L.V.: Ji l'.lIJ.
I
I
i
i
.
i
«
i
_ _ _ __ _
•«
l
i
i
L
•L..." J
i
i i
i
i
l t J
i
typedef struct
{
char x;
char* y;
int z[ 20 ];
> FUN ;
Determine which of the following definitions are valid and which are
invalid. II invalid , explain the error.
a. struct FUN fl;
I). struct FUN f 5[ 23];
c. FUN f 3 ;
d. FUN f4 [ 20 ];
23. Imagine we have the following declaration and definitions:
struct FUN
{
char x ;
char* y ;
int z[20 ];
> ;
s.fixedBefore = 23.34 ;
s.choice = ONE;
s.flexible.choicel = 'B ;
1
s.fixedAfter = 12.45 ;
ps = &s;
--
printf ( "\n%f " , ps >fixedAfter );
printf (" \n%d " , ps >flexible.choicel );
printf ( "\n% f " , s.fixedBefore);
return 0;
} // main
Problems
. me with two parameters, the start time
26 Write a function called elapsedTi
three fields showing
and the end time. Each parameter a structure with
is
814 Section 12.10 Practice Sets
the hours, minutes, and seconds of a specific time ( see Figure 12- 13).
The function is to return a time structure containing the time elapsed
between the two parameters. You must handle the situation when the
start time is in the previous day.
.
27 Write a function called increment that accepts a date structure with
three fields. The first field contains the month (a pointer to a string). The
second field is an integer showing the day in the month. The third field is
.
an integer showing the year The function increments the date by I day
and returns the new date. II the date is the last day in the month, the
month field must also he changed. II the month is December, the value
of the year field must also he changed when the day is 3 1 . A year is a leap
year if
.
a It is evenly divisible by 4 but not by 100, or
b. It is evenly divisible by 400.
.
28 Write a function called futureDate. The function is to use two parame-
ters. The first parameter is a structure containing today s date ( as defined
in Problem 27 ) . I he second parameter is an integer showing the number
ol days after today. The function returns a structure showing the next
date, which may he in a future year .
29. Write a function called later that receives two date parameters, com-
pares the two dates, and returns true ( 1 ) if the first date is earlier than
the second date and false (0) if the first date is later. Each parameter isa
structure containing a date ( as defined in Problem 27 ) .
.
30 Write a function that accepts an integer representing money in dollars
( no fraction) and returns a structure with 6 fields. The fields represent,
respectively, the minimum number of $ 100, $ 30, $ 20, $ 10, $5, and
S I hills needed to total the money in the parameter.
.
31 \ \ rite a function that compares two fraction structures (see Figure 12 - 3 ».
II the Iractions are equal, it returns zero. If the fraction in the first
parameter is less than the fraction in the second parameter, it returns a
.
negative number. Otherwise, it returns a positive number Hint: Convert
the fraction to a floating-point number.
32. A point in a plane can be represented by its two coordinates, x and y -
I hereIore, we can represent a point in a plane by a structure having two
fields as shown below.
typedef struct
{
int x;
int y;
> POINT;
x y
I positive positive
II negative positive
III negative negative
IV positive negative
.
33 A straight line is an object connecting two points. Therefore, a line can
be represented by a nested structure having two structures of the type
POINT, as defined in Problem 32.
typedef struct
{
POINT beg ;
POINT end ;
> LINE;
34. Write a function that accepts a structure of type LINE ( see Problem 33)
and returns an integer ( 1, 2, 3), where 1 means vertical, 2 means hori-
zontal, and 3 means oblique. A vertical line is a line whose x coordinates
are the same. A horizontal line is a line whose y coordinates are the
same. An oblique line is a line that is not vertical or horizontal.
35 Write a function that shuffles a deck of cards. The deck of cards is repre-
.
.
sented by an array of 52 elements Each element in the array is a struc -
ture for one card, as shown below.
typedef struct
{
char* suit ; // Clubs, Diamonds, Hearts , Spades
int value; // Ace, 2..9, Jack , Queen , King
} CARD;
been entered , print the array. Your program is to contain separate Func-
tions lor reading names and printing the array.
37. Modify the program in Problem 36 to add a function to sort the rnames
after they have been entered . Person names arc to he sorted on last
name , first name , and initial.
Projects
38. Write a program to keep records and perform statistical analysis for a
class of students. For each student , we need a name of up to 20 charac-
ters, an ID for four digits, four quizzes, and one examination The stu . -
.
dent data will he stored in an array of student structures Provide for up
to 50 students.
.
The input is read from a text file Each line in the file contains a stu-
dent ’s name, four quiz scores, and one examination score in order. If a
quiz or examination was not taken , the score is zero. The students name ,
the quiz scores, and the examination score are all separated from each
,
other by one or more spaces. A new line ends the data for one student.
I he number ol lines in this file is the same as the number of students.
I he output consists of a listing of the students in the order they are
.
read from the File; no sorting is required Print each student on a separate
line with an appropriate caption for each column. After the last student,
print the highest , lowest , and average score for each quiz and the exami-
nation . In determining the lowest score, do not consider zero scores. A
suggested report layout is shown in Table 12 - 1 .
DATA
Name ID Quiz Quiz Quiz Quiz Exam
1 2 3 4
Student 1 1234 23 19 22 23 89
Student 2 4321 0 23 21 18 76
Student n 1717 21 22 18 19 91
STATISTICS
Highest scores 23 25 23 25 96
Lowest scores 17 15 12 18 53
Average scores 21.3 20.1 19.8 21.1 81.3
Name
52
1
7
Quiz
2
Quiz
100
3
Quiz
78
4
Exam
34
Harry Smith 2134 90 36 90 77 30
Tuan Nguyen 3124 100 45 20 90 70
Jorge Gonzales 4532 11 17 81 32 77
Amanda Trapp 5678 20 12 45 78 34
Lou Mason 6134 34 80 55 78 45
Sarah Black 7874 60 100 56 78 78
Bryan Devaux 8026 70 10 66 78 56
.
42 A standard deck of playing cards consists of 52 cards as shown in
I’able 12 - 3 . Create an array of 52 structures to match a new deck of
cards as shown in Table 12 - 3.
Suit Rank
Clubs Ace...King
Diamonds Ace . ..King
Hearts Ace . .. King
Spades Ace ...King
CircleArea = rcr “
TriangleArea = Jt ( t - s i d e l ) x t ( t - s i d e 2 ) x t ( t - s i d e 3 )
1
where t = -( s i d e l + s i d e 2 + s i d e3 )
Then write an interactive test driver that prompts the user for the
type of figure to be entered and the appropriate components. It then calls
the area function and prints the area before looping for the next input .
I lint : Use a switch statement to process the different codes.
44 . Modify Program 12 - 5 to include functions to add , subtract, divide , and
compare fractions. Also change the print function to print the fraction in
—
its simplified format that is, to reduce the numerator and denominator
by the largest common factor. Thus, the fraction
20
8
would be printed as
5
2
,
—
20 and 5
8
- 2
Then modify main to include a loop that allows the user to enter two
the
fractions and an option to add , subtract, multiply, divide, or compare
fractions.
Binary Input/Output
In Chapter 7, we discussed text input /output . We introduced text streams
and formatting text input and output. We also discussed some text input and
output facilities in Chapter 11.
In this chapter, we discuss binary input/output. We first show the differ -
ence between a text stream and a binary stream. We then discuss the C input
and output I unctions, which are mainly designed to be used with binary
streams. We conclude with a discussion of converting text files to binary files
and binary files to text files.
Objectives
To understand the differences between text and binary files
To write programs that read, write, and/ or append binary files
To be able to detect and take appropriate action when file errors occur
To be able to process files randomly
To be able to create text files from binary files and vice versa
To understand and be able to implement file merges
To understand and be able to implement the classic sequential file update
821
822 Section 13.1 Text versus Binary Streams
Text Files
A text file is a file in which data are stored using only characters; a text file is
written using a text stream . Non -character data types are converted into a
sequence of characters before they are stored in the file. When data are input
from a text file , they are read as a sequence of characters and converted into
the correct internal formats before being stored in memory. We read and
write text files using the input/output functions that convert the characters to
data types as shown in Figure 13 1 . -
^ ^ ,? ° nsis*String
1
(Chapter 7), character Ch m ~
/ * "
Binary Files
A binary file is a collection of data stored in the internal format of the computer.
Unlike text files, the data do not need to he reformatted as they are read and writ -
ten ; rather, the data are stored in the file in the same format that they are stored
in memory. Binary files are read and written using binary streams known as block
input/output lunctions. Figure 13- 2 depicts the operation of block read and write
functions.
Block Write
Destination
Function
Data
Block Read
Source
Function
Data
7 6 8 A
00110111 00110110 00111000 01000001 00000011 100000000 1010000Q1
768 H — A - 768
* *
Text File Binary File
In the text file, the number 768 is stored as three numeric characters. The
character A is stored as one character. At the end, there is an end-of-Hle marker
that is not shown because its representation depends on the implementation.
In the binary file, the number 768 can be stored in 2 bytes, which
assumes that the short int type has a size of 2 bytes. The character A is stored
as I byte. At the end ol the file there is an end-of- file ( EOF) marker, again
its format is implementation dependent.
Text files store data as a sequence of characters; binary files store data as
they are stored in primary memory.
State of a File
An opened file is either in a read state, a write state, or an error state. If we
want to read from a file, it must be in the read state. If we want to write toa
(ile, it must be in the write
state. The error state results when an error
occurs when the file is opened or when it is read or written. II we try to read
from a ( ile in the write state, an error occurs. Likewise, if we try to write toa
( ile in the read state, an error
occurs. When a file is in an error state, we can-
not read from it or write to it.
Io open a (ile in the read state, we specify the read mode in the open
statement. A file opened in the read state must already exist. II it doesntthen
the open fails.
A (ile can he opened in the write state using either the write or append
mode. In write mode, the writing starts at the beginning of the file. If the file
already exists, its data are lost . In append mode, the data are added at the end
ol the file. II the ( ile exists, the writing begins after the existing data. If lhc
file does not exist, a new file is created.
In addition to read, write, and append, files can he opened in the updaU
mode. Updating allows a file to he both read and written. Even when the
is opened lor updating, however, it can
still be in only one file state at a time
1 Pen a file lor updating, we add a plus sign ( ) to the basic mode. T e
°°
initial state ol a file opened for updating is
+
+
determined by the basic mode: r
opens the file in the read state, while w -f and the write
a+ open the file in
state. Iable 13 - 1 lists the six file
modes with their features.
Chapter 13 Binary Input/Output 825
Mode
Open State read write write read write write
Read Allowed yes no no yes yes yes
Write Allowed no yes yes yes yes yes
Append Allowed no no yes no no yes
File Must Exist yes no no yes no no
Now look at the read update mode, r + . In this mode, the file can be in
either the read or the write state. To move from one to the other, we must use
one of the positioning functions, which are discussed later. That is the mean-
ing of the arrows between the read state and write state. If we try to write
after a read without repositioning, the file will he in the error state. The error
can be cleared by clearerr , which is discussed in the section " File Status
Functions” later in the chapter.
Recall that the file name is the name used for the file by the operating
system . The name we use internally in our program is a file pointer whose
address is filled by the fopen return value. Here , we are most interested in the
mode , which is a string that contains a code defining how the file is to he
—
opened —for reading, writing, or both —and its format text or binary Text
files use just the basic modes discussed above. To indicate that a file is binary,
.
we add a binary indicator ( b ) to the mode. The six binary file modes are: read
binary ( rb) , write binary ( wb ), append binary ( ab ), read and update binary
( r + b ) , write and update binary ( w + b ) , and append and update binary ( a + bl.
I he following examples contain examples of open statements lor binary files.
I iguu I 3- 5 shows the basic file modes and the file state they create. The
-
marker ( s ) represents the end -of file marker.
m a ]0
File File File
Marker Marker Marker
Read Mode ( r, r+) Write Mode ( w, w+) Append Mode (a, a+)
FIGURE 1 3 - 5 File -Opening Modes
File
Open/Close
Character
Input/Output
Formatted
Input/Output
Line
Input/Output
Categories of
I/O Functions
Block
Input/Output
File
Positioning
System
File Operations
File
Status
data, we cannot “see ” the data in a binary file; it looks like hieroglyphics. If
you have ever accidentally opened a binary file in a text editor, you have seen
these strange results. The block read function is file read ( / read ). The block
write function is file write ( fwrite ).
ED ED 0
3*4
— 12 bytes before
read
T after
read
- __T
inArea
^ HI
fread ( inArea , sizeof (int ) , 3, spData ) ;
File read returns the number of items read. In Figure 13 - 7, it will range
between 0 and 3 because we are reading three integers at a time. For exam-
ple, assume that when we try to read the file, only two integers are left to be
read. In this case, / read will return 2. If we return and try to read the file
againffread will then return 0.
Note that freiul does not return end of file—it returns the number of ele-
ments read. End of file is detected in the above situation when we called
fread with fewer than three integers left in the file. The question is, how can
we tell that we are at the end of file? C provides another input /output func
-
tion. feof to test for end of
}
file. We discuss this function in “ File Status Func -
tions ” later in this section.
Now let ’s look at a more common use of fread : reading structures
(records). Assume that we have defined a structure that stores data
about stu -
dents. Given the type of data that will he stored , we would expect the struc -
some string data and other data , such as integers or real
ture to contain
that they
numbers. One advantage of block input output / functions is can
transfer these data one structure ( record ) at a time . A second advantage is
that the data do not need to be formatted . Figure 13 - 8 shows the operation of
fread when a structure is being read.
830 Section 13.2 Standord Library Functions for Files
Before Read
imrrnn
oneStudent
mn after
read
After Read
1 he parameters for file write correspond exactly to the parameters for the
file read function.
4 * 3 ^ 1 2 bytes
/ /
IP
..
/ / before
write
/
/
/
/
/
/
E Al
/
t after
write
outArea
- TT
// /
/
/
/
Before Write
itznn nurmn A
s
file
marker
aStudent
mn iirmn 11
file
marker
aStudent
After Write
Note, however, that testing for an error does not reset the error condi-
tion. Once a file enters the error state ( see Figure 13-
4 ) it can only return to
a read or write state by calling clear error ( see below ) .
Note, however, that even though we have cleared the error, we have not
necessarily cured the problem . We may find that the next read or write
returns to the error state .
Positioning Functions
Positioning functions have two uses. First , for randomly processing data in
disk files (we cannot process tape files randomly ) , we need to position the File
to read the desired data . Second , we can use the positioning functions to
change a file’s state. Thus, il we have been writing a file, we can change to a
read state after we use one of the positioning functions. It is not necessary to
change states after positioning a file, but it is allowed .
We will discuss three file position functions: rewind , tell location, and
file seek .
Before Rewind
mem EDD
file
marker
inj|nrmn
After Rewind
file
marker
I he same effect tan be accomplished by closing the output file and open
mg K aS inPut However, the rewind
* is a faster operation .
Chapter 13 Binary Input/ Output 835
Note that ftell returns a long integer. This is necessary because many files
have more than 32,767 bytes, which is the maximum integer value on many
computers. The operation of ftell is graphically shown in Figure 13 - 12.
Beginning
^
of File ^
mm
Current
Number of Bytes location (16)
of
Another important factor to consider is that ftell returns the number
bytes from the beginning of the file. This is true even when we read or write
numChar =
numStruct = numChar
ftell ( sp);
/ sizeof(STRUCTURE TYPE);
_
In Figure 13 - 12, ftell returns 16. Since
each structure is 4 bytes, the
above is 4 , which means that there are four
result of the calculation shown
the current location. Another way to look at it is that the file
structures before
is positioned at the fourth integer
relative to zero .
U' ftell encounters an error, it returns I We -. know of only two conditions
, ftell with a device that cannot store
that can cause an error. The first using
.
, program logic or design error The second
data, such as the keyboard is a
when the position is larger than can be represented in a long
error occurs
836 Section 13.2 Standard Library Functions (or Files
integer. Obviously, this could occur only with very large files, but files of more
than a million records are common in industry.
The primary purpose offtell is to provide a data address (offset ) that can
be used in a file seek . It is especially useful when we are dealing with text files
for which we cannot calculate the position of data .
The first parameter is a pointer to an open file. Since the seek is used
with both reading and writing files , the file state can he either read or write.
The second parameter is a signed integer that specifies the number of bytes
the position indicator must move absolutely or relatively. To understand what
we mean by absolutely or relatively, we must first discuss the third parameter.
wherefrom .
C provides three named constants that can he used to specify the starting
point ( wherefrom ) of the seek. They are shown below.
_
When wherefrom is SEEK SET or 0 , then the offset is measured abso-
lutely from the beginning of the file. This is the most common use of file
seek. Thus , to set the file indicator to byte 100 on a file , we would code the
following statement:
_
f s e e k ( sp , 99 L , SEEK SET ) ;
_ .
We can use zero in place of SEEK SET If you are puzzling over the sec
ond parameter in the above statement , remember that the file position is rel-
ative to zero and must be a long integer. Actually, the compiler is smart
enough to convert an integer value to long integer, but it is more efficient if
...
we specify the correct type , especially with
< > U Cts
OI
° k at
°
literals.
_
wherefrom option , SEEK CUR . If wherefrom
displacement is calculated relatively from the cur-
. - displacement is negative , the file’s position moves
i
P <
JSILOIL ^
ack toward the beginning of the file. If
it is positive, it moves forward toward
VC e. It is an error to move beyond the beginning of the file* 1
we move beyond the end of the file, the of
file is extended , but the conten ts
nc extended bytes are unknown . Whenever
we extend the file , there is aM *
Chapter 13 Binary Input/Output 837
the possibility of running out of space , which is an error. To position the file
marker to the next record in a structured file , we execute the following
statement:
_
fseek( sp, sizeof( STRUCTURE TYPE), SEEK CUR ); _
To position the student file described earlier at the structure indicated by
the integer variable stuLoc , we use the following statement:
fseek(stuFile, ( stuLoc - _
1)* sizeof( STU ), SEEK SET);
am am *
ED nan QID
_
fseek (sp, 4 * sizeof(STRUCTURE TYPE), SEEK SET );
an urn A
fseek ( sp,
"
1
4 * sizeof(STRUCTURE_TYPE), SEEKEND);
The file seek is intended primarily for binary files. It does, however, have
limited functionality with text files. We can position a text file to the begin -
ning using / seek with a zero offset from the beginning of the file (SEEK SET).
However, rewind provides the same functionality and is more appropriate for
text files. To position a text file at the end , we can use fseek with a zero offset
and a wherefrom SEEK_END, as discussed earlier.
We cannot use file seek to position ourself in the middle of a text file
unless we have used ftell to record the location . The reasons for this have to
do with control codes, newlines, vertical tabs, and other nuisances of text
files. However, if we have saved a location using ftell and want to go hack to
that position , we can use fseek , as shown helow.
_
fseek( sp , ftell location , SEEK SET); _
Note that since ftell returns a position relative to the beginning ol the
file , we must use SEEK _SET when we reposition the file.
cotitiIIM#
Chapter 13 Binary Input/Output 839
Program 1 3 - 4 Analysis First, note the way the files are opened in Program 13 4. Because it appends
- the data
from the second file to the end of the first file, the first file is opened in append mode
and the second file is opened in read mode. Both files are opened as binary.
The open is suc-
Opening the first file in append mode presents a minor problem:
the file marker
cessful even if the file doesn't exist. Recall that the append mode places
file , if there is no existing file , at the beginning of a new file.
at the end of an existing or
To ensure that an existing file was opened successfully
, therefore, we use the ftell func-
, dataCount will be nonzero.
tion in statement 24. If an existing file was opened
file are handled. Under the assumption
Now look at the way the external names
that this program will be used to append different files at different times, we asked the
840 Section 13.2 Standard Library Functions for Files
user to enter the file names. This technique provides maximum flexibility for generalized
programs.
Statement 22 contains one of the few valid uses of multiple statements on one
—
line in this case, multiple expressions separated by the comma operator. Why is this
statement considered valid when we have so strongly emphasized one statement per
line? The answer is that we are handling an error situation that should never occur. In
good human engineering style, we want to deemphasize the error logic so that it
doesn't distract the reader. Therefore, we place the print error message and exit in
one expression .
The heart of the program is contained in statements 33 through 37. As long as
the read is successful, we keep going. When we reach the end of file, the read
returns zero and the loop terminates. We now have another problem, however. We
don' t know if the read terminated at end of file or because of a read error. We there-
fore use the feof function to make sure we read to the end of the file.
The program concludes with a printed message that contains the number of
records on the appended file. Since we didn' t read all the data on the first file, how-
ever, we must calculate the number of integers in it. To do this, we used the result of
the ftell that verified that the first file existed. Recall that ftell returns the number of
bytes to the current location in the file — in this case, the end of the file. To get the
number of integers, we simply divide by the size of one integer. (See statement 26.)
Then, each time we write, we add 1 to the record count. This gives us the number of
integers in the combined file.
Any attempt to access a file after it has been removed will result in
an error.
Chapter 13 Binary Input/Qutput 841
Both the old name and the new name must he given as parameters. The
rename function returns zero il renaming is successful; it returns nonzero if
there is an error.
For example, in a DOS system, if we want to rename a student file and
designate it a backup, we could use the rename function as shown below.
To create a temporary file, we first define a file pointer and then open it,
as shown below.
FILE* sp ;
sp = tmpfile ( );
Now we can write to the file as we would to any file. Because the file is
opened in the w+b mode, if we want to read from the file we must reposition
or fseek .
it using one of the repositioning functions such as rewind
createFile
getData write
BinaryFile
Let ’s assume that we want to convert student data to a binary file. The
data consist of a student s name, ID , three exams , eight problems, and a final
grade. In the text file version , each field is separated by one or more
whitespace characters and each student ’s data are stored on a separate line.
The create file program is shown in Program 13- 5.
44 {
45 printf( " \nCannot open %s\n" , binFilelD);
46 exit ( 200);
47 } // if binFile
48
49 while ( getData ( textFile, &aStudent ))
50 writeBinaryFile (&aStudent , binFile );
51
52 fclose( textFile );
53 fclose( binFile);
continued
844 Section 13.3 Converting Pile Type
continue
Chapter 13 Binary Input/Output 845
Program 1 3 - 5 Analysis Note how we specified the external file names as strings. This makes it easy to
change the file names when necessary. It also allows us to identify the file by name if
the open fails.
The program starts and ends with a message that identifies what program is run-
ning and that it has successfully completed . This is a good programming technique and
a standard in many organizations.
The while loop is controlled by the results of the getData function call. To under-
stand how it works, therefore, first look at getData. When you need to read a lot of
text data, especially if it is coming from a file, the preferred technique is to use the get
string [ fgets ] function and then use the string scan ( sscanf ] function to convert it into
internal binary formats. The get string function reads text data until it finds a newline
character, an end of file, or the maximum number of characters has been read. It
returns the address of the input string, in this case buffer. When end of file is
detected, it returns a null pointer.
To make this function robust, we would break the sscanf into several calls, one for
the first two strings, one for the exams, one for the problems, and one for the grade at
the end. This would allow us to easily verify that the correct data were read by examin-
ing the returned value from sscanf .
Now that we understand that the getData function returns either an address or
NULL, we understand how the while statement in main works. It simply loops until an
end of file is detected- that is, until NULL is returned.
caption. Finally, the report should have an end of report message as the last
line of the report so that the user knows that all data have been reported.
Since we put a title on the top of each page, we need to know
when a
page is full. This is generally done by counting the number of lines on a
page
and when the count exceeds the maximum, skipping to the next page. Good
structured programming requires that the heading logic he in a separate
function. The design for printing the student data is shown in Figure 13 15
- .
printStudentsI
getData
I write
Report
pageHeaders
We write the data to a file so that it can be sent to the printer when we
want a hard copy. You should he aware of one final point before looking at the
program. In the print functions, we use a type modifier known as the s /«tic
storage class ( see statements 97 and 128). We discuss the
concept of storage
classes in Appendix K. l or now, you only need to know that static keeps the
contents ol a variable available between calls. The
first time we call a function
with a static variable, the variable is initialized. After that, its value is retained
between calls. I his is a great C feature that reduces the need to pass parame-
ters just to retain a local variable. The
program is shown in Program 13- 6.
PROGRAM 1 3 - 6 Print Student Data
1 /* Reads a binary file
of student data , and prints it.
2 Written by:
3 Date:
4 */
5 # include <stdio.h>
6 # include <stdlib.h >
7
8
9
_
# define MAX LINES PER
_
#define BUFFER SIZE
_ _
PAGE 50
133
10 # define FORM FEED • \f 1
11
cotiti ,,,ie‘
Chapter 13 Binary Input/Output 847
48 exit ( 200);
49 } // if prtFile
50
51 aStudent = getData (stuFile );
52 while (!feof( stuFile ))
53 <
54 writeReport (aStudent , prtFile);
aStudent = getData (stuFile );
55
continued
848 Section 13.3 Converting File Type
continued
Chapter 13 Binary Input/Output 849
pageHeaders =================
=====
!
121 Writes the page headers for the student report.
122 Pre prtFile is opened as a text output file
123 Post Report headers and captions written
124 */
125 void pageHeaders (FILE * prtFile)
126 {
127 // Local Declarations
128 static int pageNo = 0;
129
130 // Statements
131
132
pageNo++ ;
fprintf( prtFile, "%c", FORM FEED);
_
133 fprintf( prtFile, "% 66s Page %4d \n ",
-
134 "Student Report " , pageNo );
135 fprintf ( -
prtFile , " %-25s %-6s % 10s % 27s Grade\n\n ", -
136 "Student Name" , "ID " , " Exams " , "Problems " );
137 return ;
138 } // pageHeaders
139 ===== End of Program ====
header the first time through the function. This leads us to the second point you should
note. The logic for pageHeaders will cause the first page to be blank. That is, we
issue a page form feed before any data have been written. This is standard in produc-
tion programs, but you may want to change it so that the first page is not wasted. In this
case, you will have to call the pageHeaders function before you start the file reading
to write the first headings and move the form feed write to just before statement 103.
The file is read using look -ahead logic . At statement 51, we read the first record of
the file. This allows us to use the feof function to control the while loop. At the end of the
loop, we read the file again. Although this loop closely resembles a posttest loop, there
is one difference . If the file is empty, a post - test loop would fail. By reading the first
record from the file before the loop, we ensure that we can process any file condition
using the simplest possible logic.
Note the way we handle the report title and line captions in pageHeaders.
Many programmers simply try to code them in the format string. This works, but it takes
a lot of trial and error to get it right. Our technique simply adds the widths from the
data write and uses them for the widths in the caption prints. The program may still
need a little manual adjustment, but it is a much simpler approach .
Finally, note how we formatted the student output. We used sprintf to format a line
of output. There is no significant advantage to using the string format function rather
than the print function, but we wanted to demonstrate its use. After the data have been
formatted, we use fputs to write them . Note also the way we aligned the data for read-
ability. Since it takes several lines, we group the common data on lines by themselves.
This makes it much easier to read the code. We could have used for statements to print
the array data, but that would have been less efficient.
Program 1 3 - 7 Analysis The main function simply calls three functions in order. The first function receives a
pointer to the file pointer, which is the only variable declared in main . It is necessary
to use double dereferencing here because buildFile needs to pass back the
file
pointer to main . The other two calls do not change the file pointer, they just use it.
Therefore, it can be passed to them by value.
Program 13 - 8 Analysis The buildFile function in Program 13 - 8 simply creates a file with ten records.
Each record consists of a single integer, which is the square of the ordinal record
number relative to 1 (not 0) . The file is opened with write plus so that we can first
write to it and then later in the program read it. Note that all references to the file use
the dereference operator to update the file pointer, which exists in main .
Results:
Record 0: 1
Record 1: 4
Record 2: 9
Record 3: 16
Record 4: 25
Record 5: 36
Record 6: 49
Record 7: 64
Record 8: 81
Record 9: 100
Program 1 3 - 9 Analysis The printFile function in Program 13 - 9 simply reads the file sequentially starting
at the beginning (Record 0). Study the while loop . Note that we have coded it with
the first read before the loop. We use this technique so that we can detect end of file
in the while statement. The while loop prints the current record and then reads the
next record. When all records have been processed, the read will detect end of file,
and the loop will terminate with all records processed.
Results:
File contents in random sequence.
Record 8 ==> 81
Record 8 ==> 81
Record 3 ==> 16
Record 5 ==> 36
Record 1 ==> 4
Record 7 ==> 64
Record 0 ==> 1
Record 9 = > 100
Record 2 = => 9
Record 6 ==> 49
from *
zero to 0106 TU IS
^ * ^
° mo< u ° ten' which gives us potential record numbers
record nndf 0 ° ^
corresP nc s exactly with the file on disk, which occupies
^**^ » *‘
*
on output created using our computer is
,e uncstarts
shown in Pro9
s all oth" "" °* ^ ^ Piling Record 8 twice and then
prmT
Program 1 3 - 9
Merge Files
AS L
°
^
Qn Recorb
bX
Compare the output to the file print in
,
In “ B ock Input/Output Function • Se .
. 13 2 we discussed the con-
” c ,CtIon >
of
cePl combining two files hv aPPenc ng the data. In this section , we discuss
.
another wav to romKin A " '
,
, i“, When w merge data from two
fife . he | i, one C
requires that we comnl '| Wlj
Ibis
^
1 l e ata ol dered in key sequence. This
^ ^ *
/
28 / :
•/ «
2 7T
23 19
21 16
Merge Merge
File 1 18 15 File 2
17 12
14 10
10 12 14 15 16 17 18 19 21 23 25 27 28 31
3 HighSentinel
-
2 input (File2, Rec2 )
high value-
4 loop ( not eof(Filel)) OR ( not eof(File2 ))
1 if Reel.Key <= Rec2.Key then
1 output (File3, Reel )
2 input (Filel , Reel )
3 if eof(Filel)
1 Reel.Key = HighSentinel
4 end if
2 else
1 output (File3, Rec2 )
2 input ( File2, Rec2)
3 if eof(File2 )
1 Rec2.Key = HighSentinel
4 end if
continued
856 Section 13.4 File Program Examples
3 end if
5 end loop
End MergeTwoFiles
The difficult part of the merge design is the end - of - file logic. One of
the
merge files will end first , hut we never know which one . To simplify the
end
of file processing, this design introduces a concept known as a sentinel.
A
sentinel is a guard ; in our merge algorithm , the sentinel guards the end of
file. The sentinel has the property that its value is larger than any possible
key. For the sentinel value, we use INT_MAX , which is found in the limits.
!I
header file . The code is seen in Program 13- 1 1 .
Programs that involve the comparison of data from two files require that the first
record from both files be read before any comparisons can be made. This is sometimes
called "priming the files." The reads that prime the files are coded before the main
while loop. Since duplicate read statements are required in the loop, you might wonder
why we didn't use a do...while. The reason is that the program would fail if both files
were empty. As we coded it, the program works if either or both merge files are
empty.
Study the logic at statements 54 and 62 carefully. These statements implement the
sentinel concept. When a file reaches its end, we set the key in the record area for the
file to the sentinel value. This ensures that all the data on the other file will compare low
and be written to the output file.
The most difficult statement in this simple program is the while statement. We need
to keep looping as long as either of the files is not at the end of file. The most straight-
forward code is as we coded it in statement 46. Another common way to code it is
shown below.
Use De Morgan's ( see Chapter 5) rule to prove to yourself that these two statements
are identical.
Chapter 13 Binary Input/Output J159
Update Files
I hree specific files arc associated with an update program . First is the perma -
nent data file , called the master file. The master file contains the most cur-
rent computer data for an application.
I he second file is the transaction file . It contains changes to be applied
to the master file. I hree basic types of changes occur in all file updates ; oth -
ers may also occur, depending on the application . Add transactions contain
data about new students to be added in the master file ; delete transactions
identify students who will he deleted from the file; and change transactions
contain revisions to specific student records in the file .
To process any of these transactions , we need a key. A key is one or more
fields that uniquely identify the data in the file. For example, in the student
file, the key would he student ID. In an employee file , the key would be Social
Security number.
The third file needed in an update program is an error report file. An
update process is very rarely error free. When an error occurs, we need to
report it to the user. The error report contains a listing of all errors discovered
during the update process and is presented to the user for corrective action .
File updates are of two types: hatch and online. In a batch update,
changes are collected over time and then all changes are applied to the file at
once. In an online update , the user is directly connected to the computer
—
and the changes are processed one at a time often as the change occurs.
could just as easily be stored on a disk. Note that after the update program
completes, the new master file is sent to off - line storage, where it is kept
until
it is needed again. When the file is to he updated , the master file is
retrieved
from the off - line storage and used as the old master.
Generally, at least three copies of a master file are retained in off line
storage , in case it becomes necessary to regenerate an unreadable file.
-
This
retention cycle is known as the grandparent system because three genera
tions of the file are always available: the grandparent , the parent ,
-
and
the child .
off-line
storage
imjn
new master record
error
report
new master
^ «.
S recIu , res that we match
1 b ... .53riri
the keys on the transaction and
master file and , assuming that there are no
errors, take one of the following
three actions:
1. II the transaction file key is less
than the master file key, add the trans-
action to the new master.
2. If the transaction file key is equal to the master file key, either
a. C hange the contents of the master file data if the transaction is a
revise transaction , or
b. Remove the data from the master file if the transaction is a delete .
3. If the transaction file key is greater than the master file key, write the old
master file record to the new master file.
14 > 10
Thus, Rule 3 is used , and we write the master record to the new master
record . We then match 14 and 13, which results in 13 being written to the
new master. In the next match , we have
==
i
14 14
Thus, according to Rule 2a , we use the data in the transaction file to change
the data in the master file. However, we do not write the new master file at
this time. More transactions may match the master file, and we need to pro-
cess them , too.
'
\ A 31
R 25 21
A 23 20
D 21 16
Transaction Old
File A 18 14 Master File
A 17 13
R 14 10
17 < 20
2 1 == 2 1
and since the transaction is a delete, according to Rule 2b, we need to drop
2 1 from the new master file. To do this, we simply read the next
master record
and transaction record without writing the new master. The
processing con-
tinues in a similar fashion until all records on both files have been
processed.
Update Errors
Two general classes of errors can occur in an update program. The user can
submit bad data, such as a grade that is not A, B, C, D, F. For our discus-
or
sion, \ \ e v \ i assume that no
.
data errors are present Detecting data errors is
the subject of data validation and has been
discussed.
I lie second class ol errors is file errors. File
errors occur when the data
on
n ,- lransiac ‘
' t on arc n t in synchronization with the data on the master
h e 1 hree different situations can °
occur:
.
on ansactIon matches a key on the master file, therefore, we
reject
reject the* transact
rhr f ,
on as invalid and report it on the error report .
• A rev se transaction's key does not
match a record on the master file. In
case, we are trvin u ,-
tJlat d<) not ex st This is also a file
error and must be renf °
» I on the
1 led error report .
3
n match a record on the master file In .
^^ ' '
this ase we are trvin8
be reported as an error.
() IT °‘
e etc data that do not exist, and this must also
Sequential
Update
Not Sentinel
£
Read Update
Write
Master NewMaster
Key Change
Read
Add (+) Change (+) Delete Transaction
Update Logic
I n i t i a l i z a t i o n is a function that opens the files and otherwise prepares
the environment for processing. Endof Job is a function that closes the files
and displays any end of job messages. The mainline processing is done in
P r o c e s s.
Although it is beyond our scope to develop the complete set of update
functions, it is important that you at least understand the mainline logic
found in P r o c e s s. Its pseudocode is shown in .Algorithm 13- 2 .
Let ’s look at the update logic in a little more detail . The first three state-
.
ments contain initialization logic for Process The driving force behind the
update logic is that in each while loop, we process all the data for one student
.
( entity) To determine which student we need to process next (statements 3
and 4.7 ) , we determine the current entry by comparing the current transaction
key to the current master key. The current key is the smaller .
Before we can compare the keys, however, we must read the first record in
.
each ( lie This is known as " priming the files” and is seen in statements 1 and 2.
The loop statement in Algorithm 13- 2 contains the driving logic for the
entire program . It is built on a very simple principle: As long as data are
present in either the transaction file or the master file, we continue to loop.
When a file has been completely read , we set its key to a sentinel value.
W hen both files are at their end , therefore, both of their keys will be senti-
.
nels I hen , when we select the next student to be processed , it will be a sen-
tinel , which is the event that terminates the while loop.
1 hree major processing functions take place in the while loop. First we
determine if the student on the old master file needs to be processed. If it
does, we move it to the new master output area and read the next student
f rom the old master file. The key on the old master can match the current key
in two situations: a change or delete transaction exists for the current stu -
dent . This logic is seen in statement 4.1 .
1 he second major process handles transactions that match the current
student . It calls a function that determines the type of transaction being pro-
cessed ( add , change, or delete ) and handles it accordingly. If it is an add. it
moves the new student’s data to the new master area. If it is a change, it
updates the data in the new master area . And if it is a delete, it clears the key
in tlu* new master area so that the record will not be written . To handle mul -
tiple transactions in the update function , it reads the next transaction and
continues if its key matches the current student.
I he last major process writes the new master when appropriate. If die
current student matches the key in the new master file area , then the record
needs to be written to the file . This will he the case unless a delete transaction
was processed .
Summary
In this section we looked at a very important algorithm , the classic sequ tial
file update, and discussed its mainline logic flow.
The elegance of Dwyer’s algorithm , as seen in Algorithm 13- 2, ics
determination of the current student and the separation of the up a e
Chapter 13 Binary Input/Output 865
into three distinct functions: read the old master, update the current student ,
and write the new master. Study the concept of the current student carefully,
and make sure you understand how it controls the three major processes in
the loop. Then , with a little thought , you should be able to develop the other
( unctions in the update program .
Also note that Algorithm 13- 2 contains only one read transaction func -
tion . Its function is to read a valid transaction . Therefore , it contains all of
the simple data validation logic to determine if the data are correct. It does
not perform any file errors ; they are handled in the update function . If any
errors are found , the read transaction function writes the transaction to an
error report and reads the next transaction . When it returns to the calling
function , it has either read a valid transaction or has found the end of the file.
866 Section 13.6 Tips ond Common Programming Errors
-' “
Bl' \ff -
the fil
‘
^ . , ,,
, . lo add an element at“ the it i referring o a hvtc „f e h end with
end of the file , you use the fseek lunction
7 .
the second parameter set to zero and the third
parameter to the value o
SEEK END .
5 Remember that every time U USC
*
fread or fwrite function , it auto *
17 . It is a compile error to reter to a file with its external file name rather
than its file pointer.
18. It is a compile error to omit the file pointer when using a file function .
I his error most often occurs when using the file versions of the file for-
mat functions, such as fprintf .
1 9 . It is a logic error to refer to a file before it is opened .
20. It is a logic error to open a file for reading when it doesn ’t exist . This is
usually an error in the external file name in the open statement .
2 1 . It is a logic error to attempt to read from a file in the write state and vice
versa. This is true even when the file has been opened in the update mode.
22. Opening an output disk file will fail if the disk is full .
23. Opening an existing file in write mode deletes the file. If your input file
disappears , check your open modes.
24 . It is a logic error to usefseek to place the file marker before the first byte
of a file.
13.8 Summary
A file is a collection of related data stored in an auxiliary storage device.
A stored stream of Os and Is can he interpreted as either a text or a
binary file.
A text file is a file of characters.
A binary file is a collection of related data stored in the internal format of
the computer.
(ile is always in one of the following states: read , write , or error.
^
A binary file can be opened in one of the following modes:
rb, r + b, wb ,
w + b , ab, and a + b .
J The fread function reads the number of bytes specified
by the product of
the element size and the number of elements .
868 Section 13.9 Practice Sets
The fwrite function writes the number of bytes specified by the product
of
the element size and the number of elements.
The feof function checks for end of file.
The /error function is used to check the error status of a file.
The clearerr function is used to clear an error.
The rewind function sets the file position indicator to the beginnin
g of
the file.
I he /tel / function tells you the current position of the file position indicator
.
Thefseek function positions the file position indicator to the beginning of
a specified byte.
The remove function removes or deletes a file from the disk.
The rename function renames a file on the disk.
The tmpfile function creates a new temporary file .
Io be kept current , master files must be updated .
The master file update requires four files: the old master, a new master, a
transaction file, and an error report file.
J 1 he basic master file transactions are add , revise , and delete.
Review Questions
1. A file can be read only if it is in the read
state.
a . True
b. False
2. A file opened in the r + b mode is a
binary file opened in the read state and
update mode.
a. True
b. False
3. Binary files are read using
the block input/output function fread .
a . True
b. False
4. Using fseek to position a file
beyond the current end of file places the
in an error state .
a . True
b. False
Chapter 13 Binary Input/Output 869
5 . Io merge two files, you can use the merge function found in the standard
input /output library.
a. True
b. False
.
6 Which of the following statements about files is true?
a. All files must be opened before they can be used.
b. Binary files arc more portable than text files.
c. Binary files are slower than text files.
d. Files must be closed.
e. Text fdes contain data stored in the internal format of the computer.
7. Which of the following statements about files is false?
a. Because they are more flexible, binary files are more portable.
b.Binary' files contain data stored in the internal format of the computer.
.
c Binary files usually contain records in the form of structured data.
d. Text files store only character data.
e. Some text file data need to be converted to internal storage formats for
processing.
.
8 The results when a failure occurs during an open or
during either a read or write operation.
.
a error state
b. fail state
c. read state
d. update state
e. write state
9. If a file in the read state is written to, then the following occurs:
a. The file is placed in an error state regardless of the file mode.
b. The file is placed in an error state unless it was opened in the update
write mode (w+b).
The file state is automatically switched if the file is opened for updating.
c.
-
d. The program is aborted regardless of how the file was opened.
e. The program is aborted unless the file was opened for updating.
10. Which of the following file modes opens a file in the write state for
updating?
a. ab
b. a+b
.
c rb
d. r +b
.
e wb
c . write
d . writef
e . fprint/
12 . Which of the following is a file status function?
a . feof
b . /report
c. / rewind
d. / seek
e . / tell
13 . The function may he used to position a file at the
beginning .
a . /close
h . / eo/
c . / report
d . /seek
e. / tell
14 . A is one or more fields that uniquely identify the
data in a file.
a . field
b . identifier
c . key
d . structure
e . transaction
1 3. \ \ hich ol the following statements about sequent
ial file updating is falser
a . Add , change , and delete transactions are used to update the file,
h . Sequential files are often updated in an online environm .
ent
c . Sequential files contain structures (records )
with a key to identif'
the data .
d . 1 he file must he processed starting at the beginnin .
g
e . The file must he entirely re -
created when it is updated .
Exercises
16 . 1 xplain the difference between the
following pairs of modes:
rb and r + b
wb and w + b
ab and a + b
.
17 Find the error( s ) in the followin
been properly declared . )
_
g code . ( Assume the PAY REC lyPe
char*
char* str = "Payroll ” ;
_
PAY REC payRec ;
FILE* sp;
sp = fopen (str , m );
fread( payRec, sizeof( payRec ), 1 ,
18. Given the following declarations and assuming that the file is open:
FILE * sp;
char s[ 20];
c. fread( s , 1, 20 , sp);
d . fread(sp, 1 , 20, s);
e. fread( sp, 20, 1 , s);
19. Given the following declarations and assuming that the file is open:
FILE* sp;
char s[ 20];
# include <stdio.h>
int main (void )
{
long int pos;
FILE* sp;
# include <stdio.h>
int main ( void )
{
char c;
FILE* sp ;
# include <stdio.h>
int main ( void )
{
char c ;
FILE* sp;
# include <stdio.h>
int main ( void )
{
char c ;
long int pos;
FILE* sp ;
sp = fopen( "SAMPLE.DAT" ,
Mw+b");
for (c = ' A * ; c <
= E'; C++)
fwrite(&c , sizeof(char ), 1 , sp);
continue
Chapter 13 Binary Input/Output 873
printf( "\n\n%c", c );
return 0;
> // main
24. What would be printed from the following program? Draw a picture of
the file with the file marker to explain your answer.
——
pos ;
pos ;
fseek(sp, pos, 1); -
fread(&c, 1 , 1 , sp );
printf( "\n\n %c" , c );
return 0;
} // main
.
25 What would be printed from the following program ? Draw a picture of
the file with the file marker to explain your answer.
# include <stdio.h>
int main ( void )
{
char d;
long int pos;
FILE* sp;
fwrite(&c , sizeof(char), 1, sp );
pos = ftell(sp);
——
pos ;
pos ;
fseek(sp, 2 , 2 );
continued
874 Section 13.9 Practice Sets
fseek( sp , 1 , 1 );
fread( &d , 1 , 1 , sp );
printf( "\n\n%c " , d);
return 0 ;
} // main
27. What would be printed from the following program ? Draw a picture ot
the file with the file marker to explain your answer.
# include <stdio.h>
int main ( void )
{
int i;
FILE* sp;
sp =
fopen( " SAMPLE.DAT" , " w+b " );
for ( i = 1; i < = 5 ; i++ )
fwrite(& i , sizeof(int), 1, sp);
fseek( sp , sizeof ( int ) * 2 , 0 );
fread(&i, sizeof (int), 1, s p ) ;
printf( " %d \n" , i );
return 0;
> // main
.
28 \ \ hat would be printed from
the following program ? Draw a picture o!
the file with the file marker to explain your
answer.
# include <stdio.h >
int main (void )
{
contimid
Chapter 13 Binary Input/Output 875
int i;
FILE* sp;
29. What would be printed from the following program ? Draw a picture of
the file with the file marker to explain your answer. Is the answer
strange? Why?
tinclude <stdio.h>
int main ( void )
{
int i;
FILE* sp;
#include <stdio.h>
int main (void)
{
int i;
long int pos ;
FILE* sp;
fseek( sp , pos, 0 );
fread(&i, sizeof(int), 1, sp);
printf( "%d " , i);
fclose (sp);
return 0;
} // main
Problems
.
31 Write a function that copies the contents of a binary file of integers toa
second file. The function must accept two file pointers and return an
integer ( zero representing a processing error and nonzero indicating suc-
cessful completion ).
32 . Write a function that prints a specified number of records from the
.
beginning of a file The function is to accept two parameters. The first is
a pointer to a binary file of structure type STR. The second is an integer
that specifies the number of records to he printed ( inclusive ). The struc-
ture type is shown below:
typedef struct
{
int 1?
float f ;
> STR ;
II any errors occur, such as fewer records in the file than specified , it
should return zero. Otherwise, it returns nonzero.
33. Write a lunction that compares two files and returns equal or not equal
based on the result of the comparison . The functions should receive file
pointers to two opened files and compare them byte by byte.
34. Write a function that returns the number of
items in a binary file.
35. Write a function that prints the
last integer in a binary file of integers.
36 . Write a function that physically removes all items with a specified value
(data) f r o m a binary file of structure STR. You may use a temporary file-
I IK file may contain more than one
record with the delete value. The ke>
value removed is to be entered from the keyboard .
typedef struct
{
int data;
char c;
> STR ;
.
38 Write a function that, given a binary file, copies the odd items ( items 1,
3, 5, n) to a second binary file and the even items (items 2, 4, 6, n)
to a third binary file. After all items have been copied, print the contents
of both output files.
.
39 Write a function that reads items from a binary file and copies them to a
dynamically allocated array. I he function must first find the size of the
hinarv file to allocate the array.
.
40 Write a function that takes a binary file ol long integers and appends a new
long integer at the end that is the sum of all integers in the original file .
Projects
. .
41 A company has two small warehouses The list of the products in each
warehouse is kept in a text file(invFilel and InvFile2), with each line
representing information about one product. The manager w'ants to have
only one list showing information about all products in both warehouses.
Therefore, the two text files must he combined into one single text file
(OutFile).
Write a program that will copy information from the two text files
(InvFilel and InvFile2)to two binary files(BinFilel and BinFile2).
After creating the binary files, the program is to merge the two binary
files to produce a combined binary file. After the combined binary file has
been created, create a report file that can he printed. The report file is to
contain page headers with an appropriate title and page numbers. The
structure for the files is shown below.
To read a record on the Ides, you will need to determine its position
on the file. This is easily done by subtracting 1000 from the PartCode
for the part file and 5000 from the SupplierCode for the supplier file.
Then use /seek to position the file for reading.
43. Write a program that builds a histogram ( see Chapter 8 ) using a ran-
domly accessed disk file. The program is to begin by creating a new file of
20 integers. Each integer will represent the accumulator for its relative
position; the first for the number 0, the second for the number 1, and so
forth until the last for the number 19. The program is to then use the ran-
dom number generator to create 100 random numbers in the range
0 . .. 19. As each random number is generated, it will be displayed in a 10 10
x
matrix ( 10 lines of 10 numbers each ) and 1 added to its accumulator on
the disk using the fseeli function . After the random numbers have been
generated, the file is to be read sequentially and a histogram displayed.
that allows you to check
44. Your stockbroker has an online inquiry system
the price of stocks using your personal computer . Simulate this system as
described .
below
Each stock is assigned a unique integral number in the range
that stock 1000 is stored in
1000... 5000. They are stored on the disk so
location 0. stock 1001 in location 1 , stock 2010 in location 1010, and so
the disk address for a requested stock, your program
forth. To calculate
1000 from the stock number and uses the result as the address
subtracts
880 Section 13.9 Practice Sets
structure
stock key short integer
stock name string[21]
stock symbol string!6 ]
current price
YTD High
-
floating point number
YTD Low
-
floating point number
-
floating point number
-
Price Earning Ratio short integer
( YTD: Year to Date)
\ \ hile the preceding chapters contain the material required for a traditional
first course in programming, we have not discussed all the basic capabilities
of the C language . This chapter discusses the C bitwise operators.
I he C language is well suited to system programming because it contains
operators that can manipulate data at the bit level. For example, the Internet
requires that hits be manipulated to create addresses for subnets.
C has two categories of bitwise operators that operate on data at the bit
level : logical bitwise operators and shift bitwise operators. Before we discuss
these operators, however, we need to introduce exact -size integer types.
To demonstrate these concepts we develop three programs. The first dis -
cusses a checksum calculation and develops an example that could be used to
validate Internet transmissions. The second uses masks to determine the first
or the last address in a block of Internet addresses. The third demonstrates a
hit manipulation technique that can be used in polynomial calculations.
In Software Engineering, we present a case study to demonstrate an
approach to designing structure charts.
Objectives
IJ To be able to use the bitwise logical operators in programs
To be able to use the bitwise shift operators in programs
To understand how to create and use masks to manipulate bits
882 Section 14.2 Logical Bitwise Operators
Type Description
int 8 t 8-bit signed integer
int!6 t 16-bit signed integer
Signed
int 32 t 32-bit signed integer
int 64 t 64-bit signed integer
uint 8 t 8-bit unsigned integer
uint!6 t 16-bit unsigned integer
Unsigned
uint 32 t 32-bit unsigned integer
uint 64 t 64-bit unsigned integer
^^ T
^ ^ndi' hits
ta 3S to
operators are bitwise “ nd ( ), bitwise inclusiveof
( I ), bitwise exclusive or '‘pUjate es comPlement
:
*
(-) The first three are
’ " °" •
binary> operators the,. one s complement
perators, th is a unary operator.
Results:
Input and results in hexadecimal:
numl: 0X0257
num2: 0XA463
result: 0X0043
884 Section 14.2 Logical Bitwise Operators
Program 14 - 1 Analysis The result of an and operation is logically the intersection of the two values. We con
veriify the result by manually calculating it using binary values.
Results:
Input and results in hexadecimal:
numl: 0X 0257
num2: 0XA463
res: 0XA677
two integral .
operands It does a bit -by-bit comparison between the two oper -
ands. I he result ol the comparison is 1 only if one of the operands is 1 and
the other is 0; it is 0 if both operands’ hits are 0 or 1 — that is, if they are both
the same. Table 14 - 4 shows the result of bit-by-bit comparison.
1 0 1
1 1 0
Results:
Input and results in hexadecimal:
numl: 0X0257
num2: 0XA463
res: 0XA634
continue
Chapter 14 Bitwise Operators 887
Results:
Input and results in hexadecimal:
num: 0X0257
res: 0XFDA8
Checksum
One bitwise manipulation application is the calculation of a checksum. A
checksum is a mathematical calculation used for error detection. For exam-
pie, credit cards use a checksum to verify that the card number is valid.
Another example involves the detection of a bad transmission through a com-
munication channel. When the data are sent, a checksum is calculated and
attached to the transmission. When the data are received, the checksum that
includes the sender ’s checksum is recalculated. If the newly calculated
checksum is not zero, an error has occurred.
The traditional Internet checksum uses a 16-hit calculation. To calculate
the checksum, the following steps are used.
.
1 Set the checksum to 0 .
using one’s complement
2. Add 16-bit sections of data to the checksum
arithmetic. ( See Appendix D.)
.
3 Complement the result .
888 Section 14.2 Logical Bitwise Operators
We complement the result at the end because a number (T) and its
com-
plement (-T), when added always give - 0 in one's complement.
Figure 14- 1 shows the calculation of a checksum for a small
string.
“ABCDEFGHI.” Note that because the number of characters is nine, we
include the null character ( \ 0 ) at the end of the string to make live 16- bit val
ues . The string is logically divided into groups of 16 hits ( two characte
-
rs),
which are then added together using one’s complement.
contint ei
‘
Chapter 14 Bitwise Operators 889
Results:
str: ABCDEFGHI
checksum: 0XA5EA
0 0 0 ... 1 1 0 0 0 1
/'"O or 1 bits"^
Inserted on righi Rightmost bit
^
discarded .
... 1 1 1 0 0 1
Results:
Original value: 0000000001000000 (0x0040)
Shifted 1 right: 0000000000100000 ( 0x0020)
Shifted 2 right: 0000000000010000 (0x0010)
Shifted 4 right: 0000000000000100 (0x0004 )
892 Section 14.3 Shift Operators
Dividing by 2
Let’s start with something we know, decimal numbers When we shift a
deci- .
mal number one position to the right and insert a 0 on the left, we are
in
effect dividing by 10. If we shift it two places, we are dividing by 100. If we
.
shift it three places, we are dividing by 1000 But actually, we are dividing by
a power of 10—in our examples, 101, 102, and 103 .
Applying the same principle to binary numbers, the right-shift operator
divides by a power of 2. If we shift a binary number two places to the right,
we are dividing by 4 ( 22). If we shift it three places, we are dividing by ( }
8 2 ).
Table 14 - 6 shows the division pattern used w i t h bit shitting.
n 2n »n
rcmi
th< *
.bltVy.,
v|
S
Se Shlft
JCftPerantk
°
<< precedence 1 1 ) is a binary operator that
(
(character or integer). The first operand is
nd specifies the number of bits to
he shifted ° *
^ ** ^ S seconc 0Pe
0 0 0 ... 1 1 0 0 0 1
Leftmost zero
vbit discarded.
1 Zero insert
on right
L
0 0 0 0 . .. 1 0 0 0 1 0
Program 14- 7 demonstrates the basic operation of the shift - left operator.
Again, it uses the bin 16 1 unction , which we have now included as a library.
Results:
Original value: 0000000000110001 (0x0031 )
Shifted 1 left: 0000000001100010 (0x0062)
Shifted 2 left: 0000000011000100 (0x00c4)
Shifted 4 left: 0000001100010000 ( 0x0310)
894 Section 14.3 Shift Operators
Multiplying by 2
Shift left is the opposite of shift right ; multiply is the opposite of divide. It is
reasonable to assume , therefore , that shifting left has the effect of multiplying
by a power of 2. If you examine the results of Program 14-7 closely, you will
note that is exactly what happens. When we shift a binary number two places
to the left , we are multiplying by 4 ( 22 ) . If we shift it three places, we are mul
-
tiplying by 8 ( 23). Table 14-7 shows the multiplication pattern used with bit
shifting.
n 2n «n
Rotation
In computer science , we often need to rotate bits . For example , in security
the creation of a hash digest of a message rotates and then scrambles bits to
create a hash digest of data.
Rotation requires that hits be taken off one end of a value and moved to
the other end. When we rotate hits left , we shift them off the left end of the
value and insert them on the right . When we rotate right , we shilt oil the
right end and insert them on the left . Figure 14 - 4 demonstrates the rotation
of a 16 - hit integer 4 bits to the right and then 4 bits to the left .
Although there are no explicit operands for rotating left or right , the
rotations can be coded using two shifts and an or. For example, to rotate the
original 16 - bit number in Figure 14 - 4 , we use the following steps .
Original: 0x2345
Shift right 4 bits: 0x0234
or'd results:
-
Shift left ( 16 4 ) bits: 0x5000
0x 5234
Results:
Original: 0X 2345
Rotated Left: 0X 3452
Rotated Right: 0X5234
14.4 Masks
In many programs, bits are used as binary ( lags: 0 is off, and 1 is on. To set
and test the Hags, we use a bit mask. A mask is a variable or constant, usu-
ally stored in a byte or short integer, that contains a bit configuration used to
control the setting or testing of bits in a bitwise operation. The bits are num-
bered Irom the least significant bit (rightmost), starting at 0, as shown in
figure 14 - 5.
1000000100000001
Creating Masks
Masks can 1 ,,r < .
niically Sialic masks are generally liter-
*
•Is. For esaie “o!" "'
oZm IT “
V» L
m k in <«•'« could code the constant:
operators
,
shift, and, or. In thi SCtl < )n > We examine * the basic manipulation
^
CTCalec us ng the bit
techniques for creating
dynamic masks, tb ,.
thata ;s masks that may change as the program executes.
Chapter 14 Bitwise Operators 897
_
uint8 t mask ;
mask = 0x01 « 5;
We start with a constant binary 1 . This places a one hit in the rightmost
position . We then shift the one bit to the left five positions. The result is
shown in the previous example .
An easier way to control the location of the hit flags is to name them .
This is easily done with a defined constant , as shown in the next example.
# define FLAG0 0
# define FLAG 1 1
# define FLAG5 5
uint8 t mask ;
mask = (0x01 « FLAG1) (0x01 « FLAG5);
The result of the above code would set the mask as show n below.
00100010
898 Section 14.4 Masks
Complement Masks
To create a mask that turns off a s pecific bit — that is, that complements it—
we must set the hit to 0 while setting the rest of the hits in the mask to I .
This_ requires two statements. In the first statement, we set only the desired
hit on. In the second step, we complement the mask, changing all zero bits
to I and all one hits to 0 The code is shown below. .
mask = 0x01 << FLAG 1 ;
mask = mask ;-
Using Masks
Now that we’ve seen how to create masks with various bit flags set, let ’s look
at some examples that use them.
32 It A Prefix
7 . \ |
example.
Computer address: 01111011010011100001100100001101
Mask: 11111111 11111111 11110000 00000000
Network address: 01111011010011100001000000000000
Program 14- 9 determines a network address given a computer host
address. To be flexible, it asks the user to enter the size of the prefix.
PROGRAM 14 -9 Determine Network Address
1 /* Given a host address and the size of the prefix,
2 determine its network address.
continue
Chapter 14 Bitwise Operators 899
Results:
Enter host address <x.y.z.t>: 123.45.78.12
Enter prefix: 18
Addresses:
Computer Address: 123.45.78.12
Mask: 255.255.192.0
Net Address: 123.45.64.0
^Ta? ,!
COn /erts
address Each comtv P nen o the address is considered one byte in the final address.
Since each byte has a value of 256 ( 28), we com
pute the address as follows:
Once we have the dotted- decimal address converted to a 32 - bit address, we must
create a prefix mask by setting a 32-bit mask to /n leading 1 bits. This is done in the
for loop in statement 34.
Finally, the network address and mask are converted back to the dotted-decimal
format. Each byte is determined by taking modulo 256 of part of the network address.
To get the next byte in the address, we then divide by 256 . This logic is seen in the for
loops in statement 41 and 48 .
Turning Bits On
—
One common application of bit inclusive or is turning bits on that is, forc -
ing selected bits in a field to one. Io force to one, all that we need to do is to
construct a mask with the desired one bit set on. This guarantees that the
result will have a one bit in that location .
A mask can be used to find the last address in a block of network
addresses. I he last address is used for limited broadcasts. If a computer wants
to send a message to all computers inside its network, it uses this address.
To find the last address, we or the complement of a prefixed mask with
any address in the block . Conversely, to find the first address in the network
block , we and the mask with any address. Program 14 - 10 determines the last
address in a network.
J
902 Section 14.4 Masks
Results:
Enter host address <x.y.z.t>: 123.45.78.12
Enter prefix: 18
Printing Addresses
Computer Address: 123.45.78.12
Mask: 255.255.192.0
Last Address: 123.45.127.255
Program 14 - 10 Analysis Let's compare Program 14- 9 and Program 14- 10. In Program 14 - 9, we calculate the
first address in a block . In Program 14- 10, we calculate the last address in a block. If
we subtract these addresses ( in base 256 ) and add 1, we get the total addresses in
the block. Looking at the program results, we see that the first address is 123.45.64.0
( Program 14 - 9 ) and the last address is 123.45. 127.255 (Program 14- 10) . The dif -
ference is
-
( 127 64) * 256 + ( 255 - 0) + 1 = 16 ,384
We have 16,384 addresses in this block . We could have found this result using the
prefix ( 18).
232-prefix
_ 232- 18 = 214 = 16 ,384
Flipping Bits
One of the applications of bitwise exclusive or is Hipping bits— that is, forc-
ing Os to Is and Is to 0s , called force to change. The resulting hit is 1 when 1
bit is a 0 and the other is a 1 ; if both hits are 0 or both are 1 , the result is 0.
To force a bit to change: therefore, the forcing bit in the mask is set to 1 ; bits
that are to he unchanged are set to 0.
For example , assume that we want to change the 5 leftmost bits of a
number stored as an 8- hit integer. We create the mask with 5 one bits and 3
zero hits, as shown below. ( Y indicates a changed hit
.)
result YYYYYXXX
Dividing Polynomials
bits, let’s examine how we can use
As a more advanced example of flipping
fixed -size integers to represent polynomials. Polynomials with coefficients of
904 Section 14.4 Masks
1 or 0 are used in computer science for error detection , encoding, and other
applications.
A fixed -size integer can represent a polynomial il we think of 0 and 1 as
coefficients of the polynomial. For example, the hit stream 01001001 can
represent a polynomial of degree 6 or x + + 1 . In this polynomial , the coel -
6
Positions 7 6 5 4 3 2 1 0
.
0 1 0 0 1 0 0 1
X6 + X3
'
+
.
x
°
FIGURE 14 - 6 Polynomial Coefficients
+ X + X2+1
6 3 Quotient
X
Divisor x3 + 1) x9 + x5 + X2 + X + 1 Dividend
x9 + x6
+ X6 + X 5 + X2 + X +1
Term Hex
+ x6 + X3
Dividend 0x0227
+ x5 + x3 + x2 + x + 1 Divisor 0 x 0009
Quotient 0 x004D
+ X5 + x2
Remainder 0 x 0002
+ X3 +X +1
+ X3 +1
x Remainder
27 {
28 q = 1 « dgre; < < degree (
q)) /
( dvsr A
29 rmdr = dvdn
qtnt | q
30 qtnt = i
31 dvdn = rmdr ;
32 > / / while
", qtnt ) ;
33
Quotient : %#06X\n
printf (" rmdr);
34
"Remaind er: % # 06X \ n " /
35 printf (
36 return 0 ;
37 > / / main by
represented
38 degree of
polynomial continued
39 /* Determine *.
T
906 Section 14.4 Masks
Results:
Dividend: 0X 0227
Divisor: 0X0009
Quotient : 0 X 004D
Remainder: 0X0002
Program 1 4 - 1 1 Analysis We use a function to determine the degree of the polynomial. In main we use a while
loop to simulate the division. The variable poly is the term in the quotient that is
obtained in each step. Note that the multiplication of a term such as xn by the division
is done by shifting the divisor n bits.
ppj
Chapter 14 Bitwise Operators 907
a. Earnings
.
b FICA taxes
c. SD 1 taxes
d . Federal withholding
e. State withholding
f. Health plan fees
g. United Way donations
h . Union dues
5. Algorithms: * Rate * 1.5 )
^ ^J _
rs
^
a. Gross Pay = ( 8
,
« piCA Rate ) if less than Max
b. FICA Taxes = ( G «* ' >, D Ratc ) if ess than Max SDI
FICA
Pa } >
c. SDI Taxes = ( Gross GrossFay t _ jons • Exemption Rate ) )
d. Taxable Earnmgs
( = ^
Payroll Case Study
FIGURE 14- 8 Requiremen ts for
Section 14.5 Software Engineering
Determine Modules
The first step in program design is to identify the processes that the program
will use This is usually done by reviewing the program specification and
identifying the tasks it needs to accomplish . A review of our payroll case iden -
tifies the following tasks. (The references are to the case study descnpt on in
Figure 14-8.)
.
1 Read hours worked ( 1 ).
.
2 . Compute pay ( l ) -
.
3 Maintain payroll master file ( 1 ) .
4. Prepare paychecks ( 1 ).
5. Prepare payroll ledger ( 1 .
)
6. Calculate nonstatutory
deductions ( 2 ).
7 . Calculate year- to-date
totals ( 4 ) .
( S ).
8. Calculate gross pay
9. Calculate taxes ( 5 ).
Remember the rule ha ‘ w prepare output instead of prepare
miight be tempted
to start wi
£AnJ fact, you may well end up with
pa ychecks and prepare-
an intermediate PJ ' Jines
module ma
„ the preparation of all reports. But at
910 Section 14.5 Software Engineering
Classify Modules
—
In transform analysis , we look for the central transforms that is, the mod
ule( s ) that turns inputs into outputs. To identify these transforms , we classify
-
each identified task , which now represents a module in the structure chart,
as afferent , efferent , or transform. A module is an afferent module if its pro-
cessing is directed toward the central transform . Another way of looking at
this concept is to say that the module is a gatherer of data . Efferent modules
direct data away from the central processing or toward the outputs of the pro-
gram . ( Remember that input comes before output and a comes before e\
therefore , afferent is input and efferent is output . ) A transform module is
balanced ; that is, it has data flowing both in and out . The concepts of affer-
ent , efferent , and transform are shown in Figure 14 -9 .
t J JJJ
afferent efferent transform
program
For the first -cut structure chart, place all afferent modules below the
,
input block, all transform modules below the process block and all efferent
modules below the output block. In this process , analyzx each module to
be called before or after the other processes at the
determine if it needs to
placed the left, and those
same level. Those that need to be called first
are on
that need to he called last are placed on the right . The resulting structure
program
process output
input
Up to this point, we have been almost algorithmic; that is, we have exer-
cised little judgment. We now need to analyze the preliminary structure chart
to see if it makes sense . This is done by asking a simple question , "What do
we mean by...?” For example, "What do we mean by compute pay? ” The
answer is that we must take hours worked and multiply them by the pay rate,
also considering overtime and so forth . Or, to put it another way, we compute
gross pay. But this is already a module in the structure chart , so these two
modules are the same. We will therefore delete compute pay since it is a less
specific description than calculate gross pay.
Decompose Modules
To decompose modules, we look at the cohesion of each module. For exam -
ple, when we ask what we mean by calculate taxes, the answer is calculate
federal taxes and calculate state taxes. Since we are dealing with two different
entities ( things ) , the cohesion of this module is communicational; it uses the
gross pay and payroll data to calculate the different taxes.
When we find that a module is doing more than one thing, or that it is
so complex that it is difficult to understand , then we need to consider break-
ing the module into submodules. This refinement of the modules was
named stepwise refinement by Niklaus Wirth , the creator of Pascal . 1 It
refines the processes in a module until each module is at its most basic,
primitive meaning.
Decomposition continues until the lowest levels in our structure chart
are all lunctionally cohesive and easily understood.
payroll
for each
initialize employee end of job
get emp
data
calculate
pay i output
i
I
worked I master ^
read hours rea payroll write pay
checks
write
ledger
write payroll
master i
calc calc calc
gross pay deductions year to date
report
errors
calc non- calc
stat dedns taxes
: ‘
,K
Section 14.6 Tips and Common Programming Errors
14.8 Summary
J C is a proper language for system programming because it contains opera-
tors that can manipulate data at the bit level . These operators are called
bitwise operators.
I here are two categories of bitw ise operators: logical bitw ise operators and
shift bitwise operators.
I he bitwise and ( & ) is a binary operator applied to two operands of integral
value. It does a bit - by- bit comparison of the two operands. The result of
each comparison is 1 if both bits are Is. It is 0 otherwise.
bits in an operand .
J The bitwise and operator ( & ) is used to turn off masked
to two integral
The bitwise inclusive or ( | ) is a binary operator applied The result of
operands. It does a bit - by-bit comparison of its two operands .
each comparison is 0 if both bits are Os. It is otherwise
1 .
) used to turn on mask bits in an
The bitwise inclusive or operator ( | is
operand.
binary operator applied to two integral
The bitwise exclusive or ( ) is a
A
The bitwise left-shift operator («) shifts the hits in the first operand to the
left as many hits as is defined by the second operand. This effectively mul-
tiplies a number by 2”, where n is the number of hits to be shifted.
The bitwise right -shift operator (») shifts the hits in the first operand to
the right as many hits as is defined by the second operand. This effectively
divides a number by 2”, where n is the number of bits to be shifted.
The C language has no operator to rotate hits in a data item. Rotation can
be accomplished by or ing a shift-left operation and a shift - right operation.
d. -
.
5 The operator can he used to turn masked hit on.
.
a &
b. |
c. A
d. ~
6. The operator can he used to turn masked hit off.
a. &
b. |
Chapter 14 Bitwise Operators 917
c. A
d. -
7. The operator can be used to flip masked bit .
a. &
I, . |
c. A
d. ~
S. I he bitwise - operator is used to divide a data item by
a power of 2.
a. &
b. |
c. A
d. »
e. «
9. In program design , a function that collects data that are directed toward
a central transform is classified as a ( n ) module.
a. afferent
b. efferent
c. input
d . output
e. transform
to use the
10. To find the first address in a block of address, we need
operator with the network mask.
a. &
b. |
c. A
d. »
e. «
i block of addresses
, we need to use the
I 1 . To find the last address in a of network mask.
operator with the complem ent
a. &
b. i
c.
d. »
e. «
.
13 Show the value of each of the following hexadecimal numbers in binary:
a. 0x37
I). OxAB
C. 0x0237
d. 0xA234
14. Determine the result of the following operations (operands are in binary ):
a . -11011001
-
b. 11111111
.
c -11110001
-
d. 00000000
1 3. Determine the result of the following operations (operands are in binary ):
a . 11111100 & 00111111
b. 11111111 & 10101010
c. 00000000 & 11111001
d. 10101010 & 11100001
16. Determine the result of the following operations ( operands are in binary ):
a . 11111100 | 00111111
b. 11111111|10101010
c. 00000000 j 11111001
d . 10101010 j 11100001
1 7. Determine the result of the follow ing operations (operands are in binary ):
a . 11111100 A
00111111
b. 11111111 A
10101010
c. 00000000 A
11111001
d . 10101010 A
11100001
18. When numbers are represented in hexadecimal, we can simplify the
result ol one’s complement ( ~ ) operation . The result of complementing
each digit is 13 minus the value of that digit ( 3 = ~ A ) . Using the above
short -cut rule, find the result of the follow ing operations:
a. 0x02Al -
b. 0XF305 -
C. ~0xE2 A 8
d. OxOFFA -
19. When numbers are represented in hexadecimal, we can use some rules
.
that simplify the bitwise and ( & ) operation in three cases If one of the
digits is 0, the result is 0 (0 = 5 & 0). If one of the digits is F, the result is
-
the other digit ( 3 = I & 3). II two digits are the same, the result is one o!
them. ( A = A & A ). Using the above short-cut rules, find the result ol the
following operations:
a. 0xC201 & 0x02AF
b. 0xA240 & 0xF005
Chapter 14 Bitwise Operators 919
C . 0 xE 205 & 0 xE 2 A 0
d . 0 xA 531 & OxOFF O
.
20 \ \ hen numbers are represented in hexadecimal , we can use some rules
that simplify the bitwise inclusive or ( |) operation in three cases. If one
of the digits is 0, the result is the other digit ( 5 = 5 0 ). If one of the dig-
its is F, the result is F ( F = F | 3). It two digits are the same , the result is
one ol them . ( A = A | A) . Using the above short-cut rules, find the result
of the following operations:
.
a 0 xC 201 | 0 x02AF
.
b 0 xA 240 | 0 xF 005
.
C 0 xE 205 | 0 xE 2 A 0
d. 0 xA531 | OxOFFO
.
21 When numbers are represented in hexadecimal , we can use some rules
that simplify the bitwise exclusive or ( ) operation in three cases. II one
A
of the digits is 0, the result is the other digit ( 5 = 5 0). II one of the dig -
its is F, the result is 15 minus the other digit ( 9 F 6 ). II two digits are
= A
-
the same, the result is 0 (0 = A A A). Using the above short cut rules, find
the result of the following operations:
a . 0 xC 201 0 x 02 AF
A
b. 0 xA 240 A
0 xF005
c. 0 xE 205 0 xE 2 A 0
A
d. 0 xA 531 A
OxOFFO
:
22. Find the value of the following expressions
a. 0 x13 « 2
b . 0 x 13 » 2
.
c 0 x l 3A 2 « 4
d. 0 xB23E » 2
that turn on the most significant bit of
.
2.3 Show the mask and the operator
, the hits are numbered from the right , start -
an 8- bit integer. ( Remember .
in binary an d hexadecimal form
ing with 0.) Give the answer
most significant bit of
24. Show the mask and
the operator that turn off the , start -
, the hits are numbered from the right
an 8-bit integer. ( Remember
ing with 0. )
first and third bits of
complements the values of the
25. Find the mask that , the hits are number ed from the right , start -
an 8- bit integer. ( Remem ber
should be used?
with zero. ) What operator
ing
operator sets the second and
that when used with the &
26. Find the mask
integer to zero. ( Remem
ber, the bits are numbered
fourth bits of an 8-hit with 0.)
starting
from the right ,
920 Section 14.9 Practice Sets
27. Find the mask that when used with the | operator sets the second and
fourth hits of an 8- hit integer to 1 . ( Remember, the hits are numbered
from the right , starting with 0. )
. -
28 What is the result of the following expressions if a is an 8 hit unsigned
integer?
a. a & -a
b. a | a -
c. a a
d. a &
A
-OxFF
e. a & 0x00
f. a | OxFF
g. a|0x00
h. a A
OxFF
i. a A
0x00
29. Show the binary and hexadecimal representation of following polynomial
using 16- hit unsigned integer: x 1 * + x6 + x 3 + x + 1
30. Given two polynomials x 4 + x 3 + 1 and x6 + x3 + x + 1
a . Add them using modulo 2 arithmetic for coefficients.
b. Use 8- bit unsigned integers ( in hexadecimal) to represent each poly-
nomial and apply addition using exclusive or operations.
c . Compare the result of part a and b .
31 . Given two polynomials x5 + x2and x6 + x4 + x2 + 1
+ 1
a . Subtract the first polynomial from the second using modulo 2 arith -
metic lor coefficients.
b. Use 8- hit unsigned integers ( in hexadecimal ) to represent each poly-
nomial and apply subtraction using exclusive or operations.
c. Compare the result of parts a and b.
32. Given two polynomials x 12 + x 4 + x3 + 1 and x6 + x 3 + x + 1
a . Divide the first polynomial by the second using modulo 2 arithmetic
for coefficients. Find the quotient and the remainder.
b . Use 16 - bit unsigned integers ( in hexadecimal ) to represent each poly-
nomial and use Program 14 - 11 to find the quotient and the remainder.
c. Compare the result of part a and b.
Problems
.
33 Write a function that sets a specified bit in a 16- bit unsigned integer to 1 .
I he 1 unction is to have two parameters: The first parameter is the integer
to he manipulated and the second is the bit location , relative to the least
.
significant bit , that is to he turned on Your test program should start with
0 and turn on several hits in turn .
34 . Rework Problem 33 to turn off ( set to zero ) the specified bit.
.
35 Rework Problem 33 to flip the specified bit.
71
W
.
36 Write a function that flips
the hits in an 16-hit unsigned integer.
37. VY rite a function that
accepts two polynomials ( with 0 and 1
in the form of two 16-hit unsigned coefficient)
integers and returns the result of add-
ing the two in the form of a 16-hit
unsigned integer. Use hexadecimal
notations. Can the same function be used for
subtracting the two polyno-
mials? If yes, does the order of two polynomials
make a difference?
38. Write a function that returns the first (
leftmost) hexadecimal digit from a
32-bit unsigned integer.
.
39 Rework Problem 38 to return the last (rightmost ) digit.
.
40 Write a function that complements the first (
leftmost) hexadecimal digit
in a 32 -bit unsigned integer. Note that the complement of a
hexadecimal
digit is the 15 minus that digit.
.
41 Rework Problem 40 to complement the last (rightmost) digit .
42. Write a function that changes the first ( leftmost) hexadecimal digit i in a
32-bit unsigned integer. The function is to have two parameters: The first
is the integer to be manipulated, the second the replacement digit.
43 . Rework Problem 42 to change the last (rightmost ) digit.
Projects
.
44 Modify Program 14- 5 for checksum calculation at the sender site to a
checksum checker at the receiver site. The program receives a message
of an arbitrary length and a 16-hit checksum calculated at the sender
site. It then calculates a new checksum out of both. If the new checksum
is 0, it prints a message that the data is valid; otherwise, it prints a mes-
sage that the data is corrupted.
a First test your program with the string and checksum calculated in
.
Program 14- 5. The new checksum should be 0 .
b. Then change one of the characters in the string and test the program
. - .
again The result should be non zero
c. Note that the checksum itself can
be corrupted during the transmis-
program by changing one of the hexadecimal
sion. Also check your
checksum calculated the sender site. The result should
digits of the at
be non-zero again.
with the traditional checksum calculation
.
45 There is one major problem
If 16 -bit items are transposed in transmission,
discussed in the text two
error. The reason is that the traditional
the checksum cannot catch this each data item equally. In other
checksum is not weighted immaterial to the calculation
; it treats
item is
.
w ords, the order of data a new checksum
To catch transposition errors, J. Fletcher created
was devised to weigh each data
algorithm. The Fletcher checksum
. The algorithm uses eight bits (known as
item according to its position
16-bit checksum.
systems) and rcreates a
octets in telecommunication
922 Section 14.9 Practice Sets
Start
a =b=0
More Data
a=(
r
a+ d( )
mod 256
>
i
b = ( b + a)
mod 256
f
checksum = B A
b x 256 + a
proved that a prime modulo has a better detecting capability in some com -
binations of data. Figure 14-14 shows a simple, though inefficient , algo-
rithm in flowchart form .
Start
a= 1
b=0
-V
/ More Data v\—
T
a = ( a + d, )
mod 65521
b = ( b + a)
mod 65521
checksum = B B A A
b x 65536 + a
32-bit Checksum
Return
Block
128-bit (16-character ) block
1 } 1
32-bit 32-bit 32-bit 32-bit
T l f T
- -
Compress +||« HI*
1 \ \ I
16-bit 16 - bit 16-bit 16-bit
r1
!
^ 64-bit
1
f
32-bit key
H-
64-bit key
Exclusive or A
H
\ Expand
64-bit
Swapping
£ t
64-bit
\
Compress
32-bit
Digest
4 S. C ombine Programs 14-9 and 14 - 10 and make them interactive. The new
program accepts an address in the form x.y.z. t , and an integer prefix
between 0 and 32 and creates the network address ( first address ) , broad -
cast address ( last address ), the mask. The program also prints the total
number of addresses in the block. This can be done by first changing the
first and last address to a 32 - bit integer and then finding the difference
plus 1.
49. Write a program that , given the first address in a block and the number
of addresses in the block , the program can find the last address and the
corresponding mask. Note that there is only one restriction to this prob -
lem : the first address must divide the number of addresses evenly. Your
program must check for this restriction .
Aside from some advanced C concepts
and
future studies, we have covered the basics of functions that we leave for your
we turn our attention to a concept the C language. In this chapter,
that is pervasive throughout computer
ence: lists. sci
A list is a collection of related data. We can
ries: linear lists and non -linear lists. Linear
divide lists into two catego-
lists are divided into general lists,
stacks , and queues. Non - linear lists are divided
into trees and graphs. We
define and discuss each of these lists briefly in this chapter . Figure 1 5- 1 con -
tains the breakdown of lists.
Lists
Linear Non-Linear
General
) Stacks | Queues Trees Graphs
Objectives
To introduce the basic concepts of linked lists
To introduce the basic concepts of stacks
To introduce the basic concepts of queues
To introduce the basic concepts of tree structures
To introduce the basic concepts of graph structures
927
-
928 Section 15.1 List Implementotions
Array Implementation
In an array, the sequentiality of a list is maintained by the order structure of
elements in the array ( indexes ) . Although searching an array for an individual
element can be very efficient , addition and deletion of elements are complex
and inefficient processes. For this reason arrays are seldom used , especially
when the list changes frequently. In addition , array implementations ol lists
can become excessively large, especially when there are several successors lor
each element .
list
1zlink \
data link\
A
a list
(c ) Empty list
X
link data link link data link
(b) Non-linear list
Nodes
In linked list implementations, the elements in a list are called nodes A node .
is a structure that has two parts: the data and one or more links. Figure I 5 -3
shows two different nodes: one for a linear list and the other for a non-
linear list.
The nodes in a linked list are called self-referential structures. In a self-
referential structure, each instance of the structure contains one or more
.
pointers to other instances of the same structural type In Figure 15 - 3, the
colored boxes with arrows are the pointers that make the linked list a self-
referential structure.
FIGURE 15 - 3 Nodes
..
an id and grade points ( grdPts —and a
)
, "““,,”, 7 , ,
*****
|,hei «
"„r
'Jetl
d
h «
which hen pu In n he
i
The field are defined
definition of » node rucu e. „ „
T?Tz
'
Structure
in a Node
Insert a Node
We use the following four steps to insert a node into a linear list :
.
I Allocate memory for the new node .
2 . Determine the insertion point that is , the position within the list where
—
the new data are to be placed . Fo identify the insertion position, we need
to know only the new nodes logical predecessor ( pPre ) .
MIS
_
l j Hdata
pList
i
241link+Hdata
181link+I Hdata 39
i i
link
••• H 57 N
data link
pPre | |
^
pPre
^
FIGURE 15- 6 Insert Node to Empty List _ _
1
/
mT
^fy
932 Section 15.2 Generol Linear Lists
The* statements to insert a node into an empty list are shown below.
-
pNew >link = pList; // set link to NULL
pList = pNew ; // point list to first node
Note the order of these two statements. We must first point the new node
to NULL , then we can change the head pointer. If we reverse these state-
ments, we end up with the new node pointing to itself, which would put our
program into a never-ending loop when we process the list .
Insert at Beginning
We insert a node at the beginning of the list whenever it must be placed
before the first node of the list . We determine that we are inserting at the
beginning of the list by testing the predecessor pointer (pPre). If it is null,
then there is no predecessor, so we are at the beginning of the list.
To insert a node at the beginning of the list, we simply point the new
node to the first node of the list and then set the head pointer (pList) to
point to the new first node. We know the address of the new node. I low can
we find the address of the first node currently in the list so we can point the
new node to it ? The answer is simple: The first node’s address is stored in the
head pointer (pList).
If you compare these two statements to the statements to insert into an
empty list, you will see that they are the same. This is because, logically,
inserting into an empty list is the same as inserting at the beginning of a list.
We can therefore use the same logic to cover both situations. Inserting at the
beginning of the list is shown in Figure 1 5 - 7.
Insert in Middle
When we insert a node anywhere in the middle of the list, the predecessor
( pPre) is not null. Io insert a node between two nodes, we must point the
new node to its successor and then point the predecessor to the new node.
Again, the address of the new node’s successor can be found in the predeces-
sors link field. I he statements to insert a node in the middle of the list are
shown in Figure 15 -8.
Chapter 15 Lists 933
pNew->link = pPre->link;
oPre->link = pNew;
~ ~
pNew \\*\
~ 96 [ pNew
£
[ 75 | -) *fl24 -U-.
"
Tn n124
pPre pPre
Insert at End
When we insert at the end of the list , we only need to point the predecessor
to the new node. There is no successor to point to. It is necessary, however to
,
set the new node s link field
'
to NULL . The statements to insert a node at the
end of a list are shown below .
pNew-> link = NULL;
pPre-> link = pNew ;
\M \ \ pNew £ 134 X
pNew £ +
J 75 - J 124 ^
pPre
pPre After Add
Before Add
a node into
".
inserted ( item). We
m . J
the data to be
934 Section 15.2 General Linear Lists
( malloc ) and adjust the link pointers appropriately. Since we might insert a
node before the beginning of the list , we also need to he able to change the
contents of the head pointer. We ensure that the head pointer is always cor-
rect by returning it . The function declaration for insertNode is shown below.
'
that ma/ oc returns either on address in the heap or null when there is no more room
m the heap . Therefore, we need to test to see if memory is available.
When memo7 is exhausted, we have an overflow condition. The action taken
depends on the application being programmed; however, the general course of action
is to abort the program. We assume the general case in the algorithm; if null is returned
from malloc, we print a message to the user and exit the program.
With the idea in mind that all code needs to be tested, how can we test the over -
flow logic? We could use the brute- force method-insert data until the list overflows —
but that could take a long time. We tested it by inserting an array of 10,000 long dou-
ble variables to the data structure. On our system, we got an overflow on the fifth node
and the logic was tested.
The technique used to call insertNode is important, since it returns the head of
the list under all circumstances. The call should assign the return value to the list head
pointer. This ensures that when the head of the list changes, the change will be reflected
in the calling function. A typical call to insert data into a student list is coded below.
Note that the head pointer (stuList) is found both in the assignment and as an
actual parameter.
Delete a Node
from the linear list by
When we delete a node we logically remove the node
deleting it from the heap.
changing various link pointers and then physically the first node,
The delete situations parallel those for insert . We can delete
end node of a list . As you will see below, these
any node in the middle, or the first node, and
ons: Delete the
three situations reduce to only two combinati
delete any other node. (identified
must first locate the node itself
To logically delete a node, we
by pPre . We) discuss local ,
on con-
bv pcur)and its predecessor (identifiedthat we want to delete, we can s ,mpjy
cepts shortly. .After we
locate the node
to point to the deleted
node s successor We
change its predecessor s link field
’
. We need to be concerned, however, about
list so we
the only node in a list.
must be careful that in this case the head po
.
then recycle the node using /*Deleting the only node results man empty
deleting ,
nter s set to NULL .
null, we
936 Section 15.2 Generol Linear Lists
~~
[ — I -(-
pList
~T ^|
^
75 - 124 pList ; (Recycled) 124 •••
^
ppre pCur pPre
Before Delete After Delete
If you examine this logic carefully, you will note that it also handles the
situation when we are deleting the only node in the list . If the first node is
the only node, then its link field is null. Since we move its link field ( a null )
to the head pointer, the result is by definition an empty list .
-
pPre > link =
free ( pCur);
-
pCur >link ;
pPre
f
75
pCur [J
- -
- 96 - 124
75 ( Recycled ) 124
pPre
of pcuroT After Delete
fill&L .- . . * *
^. Unction
men '
Program 1 5 - 2 Analysis Three points in this functioniden |
is ca ec || that the
. It assumes
ef f they aren't, then the ,prog
deleted must be ram
node to be $
will b
|
, ,
ong.|l b b «r a pro
mo
gram we placed the recycle
ed it to the end of the I
.
* i
i
938 Section 15.2 General Linear Lists
function. When the same statements appear in both the true and false blocks of a selec-
tion statement, they should be moved out of the selection logic . This is the same concept
as factoring common expressions in algebra . The result is a program that is smaller
and easier to maintain.
Finally, we return the list. This is necessary because it is possible for the first node of
the list to be deleted, which results in a new node being the head of the list.
// Global Declarations
typedef int KEY TYPE; // Application Dependent
typedef struct
{
_
KEY TYPE key ;
// Other Data Fields
> DATA ;
Given a target key, the search attempts to locate the requested node in
t le linear list. If a node in the list matches the target value , the search
.
returns true ; if no key matches , it returns false The predecessor and current
pointers are set according to the rules in Table 15 - 1 .
Condition
target < first node NULL
pPre
2
first node
Return
0
target == first node NULL first node 1
first < target < last largest node < target first node > target 0
pList
o
pPre pCur
ho
pPre pCur
oh
pPre pCur
target == last
target == first target == middle
)
Successful Searches (Return true
pList
> pPre < pCur
HQ RRElh PHD! ~
o pPre pCur
a
pPre pCur
pPre pCur target > last
target > *pPre
target < first
target < *pCur
UnSUC
^ ^
; Tseareh (Return false )
.
2 Donald L . Knuth , / he Art of Computer Programming
, Volume 3 Sort ing and Searching Second
Edition . ( Boston , Mass.: Adilison Wesley, 1979 ) , Algorithm
- T, p. 398.
Chapter 15 Lists 941
Program 1 5 - 3 Analysis Examine the while statement at statement 25 carefully. Note that there are two tests.
The first test protects us from running off the end of the list; the second test stops the
loop when we find the target or, if the target doesn't exist, when we find a node
larger than the target. It is important to make the null list test first. If the function is at
the end of the list, then pCur is no longer valid. Testing the key first gives unpredict-
able results.
We can make the search slightly more efficient if we use a rear pointer. With a rear
pointer, we can test the last node before the loop to make sure that the target isn't larger
than its key value. If the target is larger, we simply exit the search function after setting
the predecessor pointer to the last node and the current pointer to null. Once we know
that the target is not greater than the last node, the loop doesn't need to test for running
off the end of the list.
V’7 IX
~
-T* “* •" '
-
39
f /
LrJ .
..
pList
pWalker
18 > // while
19 printf("\n " );
20 return ;
21 > / / printList
Program 1 5- 4 Analysis Study the while expression in statement 14 carefully. This is the most common form of
pointer evaluation for a linear list. C guarantees that the evaluation of a null pointer
is false, and all other pointer values evaluate to true. This expression is therefore
equivalent to
pWalker = -
pWalker >link;
TO
This statement advances us through the list. If we forget it, we are in a permanent
loop. Since pWalker points to the current node, we need to change it to advance to
the next node. This is done by simply assigning the link field to pWalker. When we.
finally arrive at the end of the list, the link is null, which, when assigned to pWalker,
terminates the while loop.
To build the list , we need to get the data, create the node, determine the
insertion position in the list, and then insert the new node I he design is .
shown in Figure 15 - 14 .
list pointer
m
buildList
list pointer
getData searchList
£
insertNode
I
FIGURE 15 - 14 Design for Inserting a Node in a List
Build List
Building a key-sequenced list uses the search function to determine the
insertion position. It uses the design shown in Figure 15 - 14. The implemen-
tation is shown in Program 15 - 6.
contimiec
Chapter 15 Lists 945
PROGRAM 15 - 6 Build List (continued )
23 exit (210);
24 } // if open fail
25
26 while (getData (fpData, &data
))
27 {
28 // Determine insert
position
29 searchList (pList, &pPre, &pCur, data.key
30 );
pList = imsertNode(pList, pPre
, data);
31 } // while
32 return pList;
3 3 } // buildList
Remove a Node
I IK* design to remove a node is also simple. After reading the key to he deleted ,
we search Lor its location and its predecessor location and then call the delete
node function to do the physical deletion . The design is shown in Figure 15- 15.
deleteKey
searchList
deleteNode
testLinkList
I
§
buildList
I printList
I printList
J deleteKey printList
o
1
searchList
A insertNodeJ
averageList
I
\ >1
searchList insertNode searchList deleteNode
Results:
Begin list test driver
List contains:
continued
rn
'
Tests complete.
Top
~ ~
FIGURE 15 - 17 Stack
Stack Structures
Several data structures can be used to implement a stack. We implement the
stack as a linked list .
Data Structure
To implement the linked list stack , we need two different structures, a head
—
node and a data node. T he head structure contains metadata that is , data
—
about data and a pointer to the top of the stack. The data structure contains
data and a link pointer to the next node in the stack. The conceptual and
physical implementations of the stack are shown in Figure 15 - 18.
Head
Top
13-
count top
Data
nodes
Stock Head
Generally, the head lor a stack requires only two attributes: a top pointer and
a count of the number of elements in the stack. These two elements are
placed in a structure. Other stack attributes can he placed here also. A basic
head structure is shown in Figure 15- 19 .
Chapter 15 lists 951
typedef struct
{
count top int count;
Stack Head Structure struct node* top;
} STACK ;
typedef struct node
{
int data ;
data link struct node* link ;
Stack Node Structure } STACKNODE ;
Stack Algorithms
The basic stack operations defined in this section should be sufficient to
solve basic stack problems. If an application requires additional stack opera
-
develop it. , . ,
Although the implementation of a stack depends somewhat
on the imple-
with a stack head structure in
mentation language , it is usually implemented
15 - 19.
C. We use the design shown in Figure
Push Stack
the stack. The first thing we need to do
Push stack inserts an element into
^ .
en we push data into
lore »11«, : * attack .
is
..
simply assign
to the node cu
stack top pointer and add 1
^
push stack operation in which .
to
,
llack ,„1 . ,„d hen
„ K link
stack
data to be inserted into the
7
_
pNew R I
^ data
123 I K pNew R
123 _
in < data link
stack stack
m n
count op
757 3
count op
757
data ink
data ink
„ I 357 I K r IWI K
data ink
data in <
( a ) Before ( b ) After
22
-
pNew >link - pStack >top;
-
23
-
pStack >top = pNew;
24
-
pStack >count++;
success = true;
25 } // else
26 return success;
27 > // push
Pop Stack
Pop stack sends the data in the node at the top of the stack hack to the call-
ing algorithm. It then adjusts the pointers to logically delete the node. After
the node has been logically deleted, it is physically deleted by recycling the
memory, that is returning it to dynamic memory. After the count is adjusted
by subtracting 1, the algorithm returns the status to the caller: if the pop was
successful, it returns true ; if the stack is empty when pop is called, it returns
.
false The operations for pop stack arc traced in Figure 15 - 21.
Recycled
-
r “i
dT
stack stack
IDO top I 757 1 [3
count data link count top data link
c HZ
data linl
r ran KI
data link
15 - 10.
The pop stack code is shown in Program
i
;
t
954 Section 15.3 Stacks
Program 15 - 10 Analysis It is interesting to follow the logic when the last node is being deleted. In this case, the
result is an empty stack . No special logic is required; however, the empty stack is cre-
ated automatically because the last node has a null link pointer, which when moved
to top indicates that the stack is empty. A count of zero, which automatically occurs
when we decrement the count, is also an indication of an empty stack.
Stack Demonstration
lo demonstrate the push and pop stack algorithms, we write a program that
inserts random numbers into a stack . After the numbers are inserted, they are
popped and printed. When the stack is empty, the program terminates. I he
design for this program is shown in Figure 15 - 22.
I he node declarations, function declarations, and test driver are con-
tained in Program 15 - 1 1 .
Chapter 15 Lists 955
Basic
Stack
insert
Data
push pop
Results:
Beginning Simple Stack Program
Creating numbers: 854 763 123 532 82 632 33 426 228 90
Stack contained: 90 228 426 33 632 82 532 123 763 854
End Simple Stack Program
Program 1 5 - 1 1 Analysis To verify that the program works correctly, we print the numbers as they are gener -
ated . This allows us to verify that the stack output was in fact correct. Note that this
simple program verifies the LIFO operation of a stack.
Insert Data
the insert data function loops while creating numbers and inserting them
into the stack. To create a random number, vve use the random number gen-
erator and scale the return value to a three- digit range . This code is devel -
oped in Program 1 5 - 1 2 .
continuec
Chapter 15 Lists
PROGRAM 15 - 12 Insert Data (
continued)
8 {
9 // Local Declarations
10 int numln;
11 bool success;
12
13 // Statements
14 printf( "Creating numbers
: ");
15 for (int nodeCount = 0;
16
nodeCount < 10; nodeCount++ )
{
17 // Generate random
number
18 numln = rand() % 999;
19 printf("%4d\ numln);
20 success = push(pStack , numln);
21 if (!success)
22 {
23 printf( Error 101: Out of Memory \n " )
;
24 exit ( 101);
25 } // if
26 } // for
27 printf( "\n");
28 return;
29 } // insertData
Print Stack
Once the stack has been built, we print it to verify the output. The print func -
tion calls the pop function until the stack is empty. The print code is found in
Program 15 - 13.
15.4 Queues
A queue is a linear list in which data can he inserted only at one end , called
the rear, and deleted from the other end , called the front. These restrictions
ensure that the data are processed through the queue in the order in which
—
they are received. In other words, a queue is a first in first out ( FIFO)
structure.
A queue is the same as a line . In fact, if you were in England , you would
not get into a line , you would get into a queue. A line of people waiting lor
the bus at a bus station is a queue, a list of calls put on hold to be answered
by a telephone operator is a queue, and a list of waiting jobs to he processed
by a computer is a queue .
A queue is a linear list in which data can be inserted at one end, called the
.
rear, and deleted from the other end, called the front It is a first in^first out
( FIFO) restricted data structure .
Figure 15 - 23 shows two representations of a queue: one a queue ol peo-
ple and the other a computer queue . Both people and data enter the queue at
the rear and progress through the queue until they arrive at the front. Once
they are at the front of the queue, they leave the queue and are served .
Banks'R 'Us
lIKt hi
(a ) A queue ( line) of people
Remove Insert
( dequeue) ( enqueue)
Queue Operations
There are two basic queue operations. Data can be inserted at the rear and
deleted from the front. Although there are many similarities between stacks and
queues, one significant structural difference is that the queue implementation
Chapter 15 Lists 959
H
needs to keep track of the front and the rear of the queue, whereas the stack
only needs to worry about one end: the top.
Enqueue
I he queue insert operation is known as enqueue . After the data have been
inserted into the queue, the new element becomes the rear. As we saw with
stacks, the only potential problem with enqueue is running out of room for
the data. If there is not enough room for another element in the queue, the
queue is in an overflow state.
Queue
Queue Operation
FIGURE 15 - 24 Enqueue
357 757
Dequeue front rear
123
rear
front Queue After
Operation
Queue Before
Data Structure
We need two different structures to implement the queue: a queue head struc -
ture and a data node structure. After it is created, the queue will have one head
node and zero or more data nodes, depending on its current state. Figure 15 - 26
shows the conceptual and physical implementations for our queue structure.
I 123 357
(a ) Conceptual queue
757
HD
c
-
123
front - I 357
( b ) Physical queue
j H 757
rear
DO
Queue Head
.
I he queue requires two pointers and a count These fields are stored in the
.
queue head structure Other queue attributes, such as the maximum num-
ber of items that were ever present in the queue and the total number of
items that have been processed through the queue, can he stored in the head
node if such data are relevant to an application. The queue head structure is
shown in Figure 15 - 27.
Before After
(a ) Case 1: Insert into Null Queue
front count rear
Queue
front count rear
0 Queue
ra
?
^
ED newPtr newPtr
S B!
data next data next data next data next
Before After
( b) Case 2: Insert into Queue with Data
PROGRAM 1 5 - 1 4 Enqueue
1 /* == === enqueue ========
2 This algorithm inserts data into a queue.
3 Pre queue is valid
inserted
4 Post data have been
continued
962 Section 15.4 Queues
Program 1 5 - 1 4 Analysis Because we must maintain both a front and a rear pointer, we need to check to see if
we are inserting into a null queue . If we are, we must set both pointers to the data just
inserted. If there are already data in the queue, we need to set the next field of the
node at the rear of the queue and the rear pointer to the new node. In this case the
front pointer is unchanged . Because the rear pointer is updated in either case, we
changed it after the if statement ( see statement 25 ).
Dequeue
Although dequeue is also a little more complex than deleting data from a
stack , it starts out much the same . We must first ensure that the queue con -
tains data . If the queue is empty, we have underflow and we return false , indi -
cating that the dequeue was not successful .
Given that there are data to he dequeued , we pass the data back through
the parameter list and then set the front pointer to the next item in the
queue. If we have just dequeued the last item , the queue front pointer auto -
matically becomes a null pointer by assigning it the null pointer from the link
held of the last node . I lowever, if the queue is now empty, we must also set
the rear pointer to null . These cases are shown in Figure 15 - 29 .
The dequeue implementation is shown in Program 15 - 15 .
Chapter 15 Lists 963
data next
Before
-
SKI
I
data next
[B
—ii
deleteLoc
After
r*T\
data next
PROGRAM 1 5 - 1 5 Dequeue
1 /* = = dequeue =====
2 This algorithm deletes a node from the queue.
3 Pre queue is pointer to queue head structure
4 dataOut is pointer to data being deleted
5 Post Data pointer to queue front returned and
6 front element deleted and recycled.
7 Return true if successful; false if underflow
8 */
9 bool dequeue (QUEUE* queue
, int* dataOut )
10 {
Local Declarations
11
12
//
_
QUEUE NODE* deleteLoc;
13
14 // Statements
15 if (!queue >count)
return false;
-
16
17
18 *dataOut = queue
>front >data;- -
19 deleteLoc = queue
=
>front;
= 1)
-
20 if (queue- >count
Deleting only item in
queue
/ /
21
22 queue-> rear = queue >front = NULL; -
else
23
24 queue >front = queue
- >front >next ;
- -
25 -
(queue >count)~
;
con ti ttueil
964 Section 15.4 Queues
Queue Demonstration
To demonstrate a queue, we write a simple program that creates a queue of
.
colors and then prints them The design closely parallels the design ol the
stack demonstration. The node declarations, Function declarations, and test
driver are contained in Program I 5 - 16.
continues
i
Chapter 15 Lists 965
PROGRAM 1 5- 1 6 Simple Queue Demonstration (
continued )
31 int main ( void
)
32 {
33 // Local
Declarations
34 QUEUE* pQueue;
35
36 // Statements
37 printf("Beginning Simple
Queue Program \n ");
38
39 pQueue = malloc(sizeof(QUEUE
));
40 if (!pQueue)
41 printf(" Error allocating queue"
), exit(100 );
42
43
-
pQueue >front = NULL;
44 -
pQueue >count = 0;
45
46
-
pQueue >rear = NULL;
47 insertData (pQueue);
48 print ( pQueue);
49
50 printf( "\nEnd Simple Queue Program \n " );
51 return 0;
52 } // main
Results:
Beginning Simple Queue Program
Creating numbers: 854 763 123 532 82
Queue contained: 854 763 123 532 82
End Simple Queue Program
Insert Data
The insert data function creates a queue by inserting numbers into the
queue. The code is developed in Program 15 - 17.
Print Queue
Once the queue has been built, we print it to verify' that the queue was built
.
correctly The print function calls the dequeue function until all items have
been printed. I he print code is found in Program 15 - 18.
15.5 Trees
I he study of trees in mathematics
can be traced to Gustav Kirchhoff in the
middle nineteenth century and several years
later to Arthur Cayley, who used
trees to study the structure ol algebraic
formulas .
laid the framework for Grace Hopper’s use of trees Cayley s work undoubtedly
in 1951 to represent arith -
metic expressions. Hopper’s work hears a
strong resemblance to today’s
binary tree formats.3
Trees are used extensively in computer science to represent
formulas; as an efficient method for searching large, dynamic listsalgebraic
; and lor
such diverse applications as artificial intelligence systems and
encoding
algorithms.
A tree consists of a finite set of elements, called nodes, and a finite set of
directed lines, called branches, that connect the nodes.
II the tree is not empty, the first node is called the root . The indegree of
the root is, by definition , zero. With the exception of the root , all of the nodes
in a tree must have an indegree of exactly one; that is, they may have only one
predecessor. All nodes in the tree can have zero, one, or more branches leav-
(
ing them; that is, they may have an outdegree of zero, one, or more zero or
more successors ). Figure 15- 30 is a representation of a tree.
.
3. Donald E. Knuth, op. cil ,
.
405, 458
968 Section 15.5 Trees
Terminology
In addition to root, many different terms are used to describe the attributes of
a tree. A leaf is any node with an outdegree of zero, that is, a node with no
.
successors A node that is not a root or a leaf is known as an internal node
because it is found in the middle portion of a tree.
A node is a parent if it has successor nodes— that is, if it has an outdegree
greater than zero. Conversely, a node with a predecessor is a child A child .
node has an indegree of one. Two or more nodes with the same parent are
siblings. Fortunately, we don’t have to worry about aunts, uncles, nieces, neph-
ews, and cousins. Although some literature uses the term grandparent , we do
not. We prefer the more general term ancestor. An ancestor is any node in the
path from the root to the node. A descendent is any node in the path below
the parent node; that is, all nodes in the paths from a given node to a leaf are
descendents of that node. Figure 15 -31 shows the usage of these terms .
Several terms drawn from mathematics or created by computer scien-
tists are used to describe attributes of trees and their nodes A path is a .
sequence of nodes in which each node is adjacent to the next one. Every
node in the tree can he reached hv following a unique path starting from the
root. In Figure 1 3 - 3 1 the path from the root to the leaf I is designated as
AFI. It includes two distinct branches, AF and FI.
Level 0
A
Branch
AF
Level 1
B
<i} F
Branch
FI
Level 2 C D G H
Root: A
Parents: A , B, F
.. . . . .. .
Siblings: { B, E ,F } {C, D} , {G H I}
Leaves: C D EG HI
.
Children: B, E, F, C. D, G, H I Internal nodes: B, F
The level of a node is its distance from the root. Because the root has a
zero distance from itself, the root is at level 0. The children of the root are at
level 1 , their children are at level 2, and so forth. Note the relationship
between levels and siblings in Figure 1 5 - 3 1. Siblings are always at the same
level, hut all nodes in a level are not necessarily siblings. For example, at
level 2 , C and D are siblings, as are G , H , and I . Flowever, D and G arc not sib-
lings because they have different parents.
I he height ol the tree is the level ol the leaf in the longest path from the
root plus 1. By definition the height of an empty tree is 1. Figure 1 5 - 3 1
-
I
Chapter 15 Lists 969 T!
contains nodes at three levels: 0, 1 , and 2.
Its height is 3. Because the tree is
drawn upside down, some texts reler to
the depth of a tree rather than
its height .
A
Subtree
B
Root of
Subtree I
FIGURE 1 5 - 32 Subtrees
CD
B C B C
D E
(e ) (f )
A Right A
Subtree
Right
B B Subtree
C C
(g) (h)
BINJREE
count root
typedef struct
{
int count;
NODE* root;
to tree } BIN TREE;_
NODE typedef struct node
left data right <
int data;
¥ ¥
struct node* left;
struct node* right;
} NODE;
to left to right
subtree subtree
1 2
In the preorder traversal, the root is processed first, before its subtrees.
Using the tree in figure 15- 36 the processing sequence for a preorder
traversal processes this tree as follows: First we process the root A. After
the root , we process the left subtree. To process the left subtree , we lirst
process its root , B , then its left subtree and right subtree in order. When B s
left and right subtrees have been processed in order, we are then ready to
process A s right subtree, E . To process the subtree E , we first process the
root and then the left subtree and the right subtree. Because there is no
left subtree, we continue immediately with the right subtree , which com -
pletes the tree.
Figure 15- 37 shows another way to visualize the traversal of the tree.
Imagine that we are walking around the tree, starting on the left ol the root
and keeping as close to the nodes as possible. In the preorder traversal we
process the node when we meet it for the first time ( on the left of the node ).
I his is shown as a black box on the leit ol the node. The path is shown as a
line following a route completely around the tree and back to the root .
figure 15 - 38 shows the recursive algorithmic traversal of the tree. The
first call processes the root of the tree, A. It then recursively calls itself to pro-
cess the root of the subtree B, as shown in Figure 15- 38( b ). The third call ,
shown in Figure 15 -38 ( c ) , processes node C , which is also subtree C. At this
point we call preorder with a null pointer, which results in i an immediate
return to subtree C to process its right subtree. Because C s right subtree is
Chapter 15 Lists 973
( b) Process Tree B
( a ) Process Tree A
(f ) Process Tree F
(e) Process Tree E
Tree
FIGURE 15 - 38 Algorithmic Traversa of
| Binary
T
9 / 4 Section 15.5 Trees
Because the left subtree must be processed first , we trace from the root
to the far- left leaf node before processing any nodes. After processing the far-
left subtree, C, we process its parent node, B. We are now ready to process the
right subtree , D. Processing D completes the processing of the roots left sub-
tree , and we are now ready to process the root , A, followed by its right sub-
tree . Because the right subtree, E, has no left child , we can process its root
immediately followed by its right subtree, F. The complete sequence for inor-
der processing is shown in Figure 15 - 39 .
Definition
A binary search tree ( BST) is a binary tree with the following properties:
• All items in the left subtree are less than the root.
• All items in the right subtree are greater than or equal to the root .
• Each subtree is itself a binary search tree.
In a binary search tree, the left subtree contains key values less than the
root, and the right subtree contains key values greater than or equal to
the root.
.
root ( 17 ) The second tree
, Figure -
.
subtree ( 22 ) is greater than the key in the
than the root. The key in the left I 5 42( b), breaks the second rule: all terns
than or equal to the root. The key m the
in the right subtree must greater
be
976 Section 15.5 Trees
right subtree ( 1 1 ) is less than the key in the root ( 1 7 ). Figure 1 5 -42 (c ) breaks
the third rule: each subtree must be a binary search tree. In this tree the left
subtree key ( 6) is less than the root ( 17 ) , and the right subtree key ( 19 ) is
greater than the root. However, the left subtree is not a valid binary search
tree because it breaks the first rule: its left subtree ( 11 ) is greater than the
root ( 6 ) . Figure 15- 42 ( d ) also breaks one of the three rules . Do you see
which one? ( H i n t : What is the largest key in the left subtree? )
Insertion
The insert node function adds data to a BST. To insert data all we need to do
is follow the branches to an empty subtree and then insert the new node. In
—
other words, all inserts take place at a leaf or at a leaflike node a node that
has at least one null subtree.
( a ) Before inserting 19
( b ) After inserting 19
Program 1 5 - 21 Analysis This algorithm must be carefully studied to fully understand its logic. It begins with a
recursive search to locate the correct insertion point in a leaf node. A leaf node is
identified by a subtree pointer, either right or left, that is null. When we find a leaf
pointer, we create a new node and return its address so that it can be inserted into
the parent pointer (statement 25).
Because this is a recursive function, it must have a base case. Can you see it? The
base case occurs when we locate a leaf and return newPtr in statement 25. At this
point we begin to back out of the tree.
BST
i
preOrder inOrder ]
BST Insert
i
FIGURE 1 5 - 44 Binary Tree Program Design
> // main
Results
Please enter a series of integers.
Enter a negative number to stop
Enter a number: 45
Enter a number: 54
Enter a number: 23
Enter a number: 32
Enter a number: 3
Enter a number: 1 -
Data in preOrder: 45 23 3 32 54
Data in inOrder: 3 23 32 45 54
15.6 Graphs
A graph is a collection of nodes, called vertices, and
merits, called lines, connecting pairs of vertices a collection of seg-
sists of two sets, a set of vertices . In other words, a graph con -
and a set of lines.
Graphs may be either directed or
digraph for short , is a graph in which eachundirected. A directed graph, or
to its successor. The lines in a line has a direction (arrow head)
directed graph are known as arcs. In a directed
graph, the flow along the arcs between two
vertices can follow only the indi-
cated direction. An undirected graph is a
graph in which there is no direc-
tion (arrow head ) on any of the lines,
which are
undirected graph , the flow between two vertices canknown as edges. In an
go in either direction .
Figure 15-45 contains an example of both a
directed graph (a ) and an undi-
rected graph ( b).
( or neighbors) if
Two vertices in a graph are said to be adjacent vertices
connecting them . In Figure 15 45 ( a ), B is adjacent
there is a path of length 1 , D is adjacent to E . In
adjacent to D; on the other hand
to A, whereas E is not
D and I are not .
Figure I 5 45 ( b), E and D are adjacent hut
- ,
A cycle is a path consisting of at least three vertices that starts and ends
with the same vertex. In Figure 15 - 45 (b), B, C, D, E, B is a cycle. Note, how-
.
ever that the same vertices in Figure 15 - 45 (a ) do not constitute a cycle
because in a digraph a path can follow only the direction ol the arc, whereas
in an undirected graph a path can move in either direction along the edge. A
loop is a special case of a cycle in which a single arc begins and ends with the
same vertex. In a loop the end points of the line are the same. Figure 15 - 46
contains a loop .
© Cycle fB
(ABC) Loop
The degree of a vertex is the number of lines incident to it. In Figure 15 - 47(a)
the degree of vertex B is 3 and the degree of vertex E is 4. The outdegree of a
vertex in a digraph is the number of arcs leaving
the vertex; the indegree is
the number of arcs entering the vertex. Again, in Figure 1 5 - 47 ( a) the inde-
gree of vertex B is 1 and its outdegree is 2; in Figure 1 5 - 47 ( b ) the indegree of
vertex E is 3 and its outdegree is 1 .
F\
One final point: a tree is a graph in which each vertex has onlyy one
prede-
cessor ; however, a graph is not a tree. We will see latcr in the chapter that some
graphs have one or more trees in them that can he algorithmically determined .
Graph Traversal
A complete discussion of graph algorithms is beyond the scope of this text . As
« m example of a graph algorithm , we discuss two graph
traversals. The
remaining algorithms to maintain and traverse graphs are left for your data
structures course .
I here is always at least one application that requires that all vertices in a
given graph he visited; that is , there is at least one application that requires
that the graph be traversed. Because a vertex in a graph can have multiple
parents, the traversal of a graph presents some problems not found in the tra -
versal of linear lists and trees. Specifically, we must somehow ensure that we
process the data in each vertex only once. However, because there are multi -
ple paths to a vertex, we may arrive at it from more than one direction as we
traverse the graph . The traditional solution to this problem is to include a vis -
ited flag at each vertex . Before the traversal we set the visited flag in each ver -
tex to off . Then , as we traverse the graph , we set the visited flag to on to
indicate that the data have been processed.
There are two standard graph traversals: depth first and breadth first
.
Both use the visited flag .
Depth-first traversal
vertexs dcscendents before
In the depth -first traversal, we process all of a
. This concept is most easily seen when the
we move to an adjacent vertex
tree preorder traversal - processing
graph is a tree. In Figure 15-48 we show the
first traversals.
sequence, one of the standard depth -
Y
UK E
0
0( G 00 0( M 0
( a ) Graph
p Y
H E E M M J
A X Q G G G G G G
1 2 3 4 5 6 7 8 9
( b) Stack Contents
Breadth-first Jroversol
-
In the breadth first traversal ol a graph , we process all adjacent vertices ol a
.
vertex before going to the next level We saw the breadth -first traversal earlier
.
in the chapter Looking at the tree in Figure 15- 50 , we see that its breadth -
first traversal starts at level 0 and then processes all the vertices in level 1
before going on to process the vertices in level 2 .
T1
Chapter 15 Lists 985
-
Breadth first Traversal
AXGHPEMYJ
W Y
UK E
[D( G 0TM J
(a) Graph
M Y Y J J
H P P E E
G H 7 8 9
4 5 6
1 2 3
( b) Queue contents
Graph
FIGURE 15 - 51 Breadth- first Traversal of a
^
C H in
*“ 0 G'
to Jqueuefcr step 3, in
empty, the traversal
”
^complete. is
.
3 When the
is
is
before pro-
-first tra
tergal, all adjacent vertices are processed
In the breadth of a vertex .
cessing the descendents
J
n 15.7 Software Engineering
int
VALUES: - oo , ... , -2, -1, 0 , 1 , 2 ... , oo
OPERATIONS:
float
*t +I %, I , ++ , —
, < / > , ...
Data Structure
A data structure is a collection of elements and the relationships among
.
them. Data structures can be nested That is we can have a data structure
that consists of other data structures.
Data Structure
1. A combination of elements, each of which is either a data type or another
data structure .
2. A set of associations or relationships (structure) involving the combined
elements.
I or example, we can define the two structures array and struct , as shown
in Table 15 - 2.
array struct
..
increase
The first computer programming . It did not take long to
to read the file device
programmers wrote the code writing the same code over and over again . So
. ,
irp
<
A rne wrote h cod o
P
^
read a file and placed Urn
This concept
library is an
"Jo ^
11
data.
a„d . standard inpat/oatpat
set of operations that
is done
can be
but
used to read and write rned with how the task
With an ADT , the user ADT consists of a set of
what it can do. In other uor
rather with
Section 15.7 Software Engineering
prototype definitions that allow the programmer to use the functions while
hiding the implementation . This generalization of operations with unspeci -
fied implementations is known as abstraction . We abstract the essence of the
process and leave the implementation details hidden .
OO-OO
A linked list
J Public
Interface Private
Functions Functions
Application I
Data flows in and out of the ADT through the operation headers repre-
sented by the rectangular " Interface ” pathway. The interface is also the path -
would have
way for the ADT functions. For instance, a linear list ADT
in this chapter , such as insert , remove, and search . In
operations that we saw
C, these operations are defined as prototype header
j
declarations that are visi-
only the ‘‘ Public " functions are available
ble to the user. Note, .however, that
" functions are totally contained with
to the application program; the Private
"
1»
within the ADT.
the ADT and can only be used by other functions
need to create a user header file.
To use the ADT in our program , we
list ADT, we give its header file a
When we create a header file for a linear our program , we would use a
name, such as Llinklist . h. To
include it in
preprocessor directive such as :
.
peering
-
pCur = pCur >link ;
- _
printf( " %d ", pCur >data.member name); // ERROR
} // while
6. It is a logic error to allocate a node in the heap and not test for overflow.
7. It is a logic error to refer to a node after its memory has been released
with free.
8. It is a logic error to set the only pointer to a node to NULL before the
node has been freed . The node is irretrievably lost .
9. It is a logic error to delete a node from a linked list without verifying that
the node contains the target of the delete.
.
10 It is a logic error to fail to set the head pointer to the new node when a
node is added before the first node in a linked fist . The new node is irre-
trievably lost .
to a new node
11 . It is a logic error to update the link field in the predecessor
before pointing the new node to its logical successor. This error results in
a never-ending loop next time the list is traversed.
in the last node to NULL . This
12. It is a logic error to fail to set the link field
the end of the list .
causes the next traversal to run off
node pointer in a linked list search
.
13 It is a potential logic error to use the
following statement , the compares
before testing for a null pointer. In the memory access.
need to be reversed to prevent an invalid
15.10 Summary
U List can be divided into linear lists and non - linear lists.
Linear lists can be divided into general lists , stacks , and queues.
Non - linear lists can be divided into trees and graphs.
In a general linear list , data can be inserted anywhere, and there are no
restrictions on the operations that can be used to process the list .
Four common operations are associated with general linear lists: insertion ,
deletion , retrieval , and traversal .
When we want to insert into a general linear list we must consider
four cases:
a . adding to the empty list
b. adding at the beginning
Chapter 15 Lists 993 TH
c. adding at the middle
d. adding at the end
When we want to delete a node from a general
sider two cases: delete the first node or linear list , we must con -
delete any other node.
Traversing a general linear list means going through the
and processing each item. list, item by item,
root.
a . All items in the left subtree are less than the
or equal to the root .
b. All items in the right subtree are greater than
c. Each subtree is itself a binary search
tree
vertices, and a collection of line
A graph is a collection of nodes , called
connecting a pair of nodes.
segments, called edges or arcs,
. In a directed graph , or digraph ,
Graphs may be directed or u ndirected graph , there is no direction on
each line has a direction . In an undirected
is called an arc.
the lines. A line in a directed graph
There are two standard graph traversals: depth first and breadth first .
994 Section 15.11 Practice Set
c>. A general linear list is a list in which operations, such as retrievals, inser-
tions, changes, and deletions, can he done
a. anywhere in the list
I ). only at the beginning
c. only at the end
cl . only at the middle
10. A stack is a list in which operations, such as retrievals, insertions,
changes, and deletions, can be done
a. anywhere in the list
b. only at the top
c. only at the base ( bottom )
.
d only at the middle
1 1 . A queue is a list in which operations, such as retrievals , insertions,
changes, and deletions, can be done
a. only at the beginning
h . only at the end
c. only at the middle
d . none ol the above
in
1 2. A is a last in-first out ( LIFO ) data structure
are restricted to one end , called the top .
which insertion and deletions
a. stack
b. queue
c. tree
d. binary tree
13. A
_ is a first in-first out ( FIFO) data, andstructure in
deletions
end , called the rear
which insertions are restricted to one front .
are restricted to another end
. called the
a. stack
b. queue
c. tree
.
d binary tree operation.
a stack, we use the
14. To add an element to
a. pop
h. push
.
c enqueue
d. dequeue use the
from a stack , we
15. To delete an element
operation .
a . pop
b. push
c . enqueue
d . dequeue
996 Section 15.11 Practice Set
a . atomic data
b. composite data
c. derived data
d . standard data
e. structured data
.
19 A( n ) is a collection of elements and the relationship
among them .
a. abstract data type
b. array
c. data structure
d . standard type
e. type definition
Exercises
20. Imagine we have the general list shown in Figure 15 - 54 . Show what hap -
pens if we apply the following statement to this general list:
pList
What is the problem with using this kind of statement ? Does it jus -
tify the need for two walking pointers (pPre and pCur) that we intro-
duced in the text ?
"1
Chapter 15 Lists 997
Lcur .
' • haVe the 8eneral list shown in Figure 15 - .
WC
55 As discussed in
W w
,
'
rent (peur ) node K
rcn )
' 1
lh’' Th ^“ ' b bk >» P
* of the Sack
(pPre ) and the location ~
cur-
based on search criteria. A typical search design is
shown in Figure 15 - 55 .
pCur = -
pCur > link ;
pPre = -
pPre > link ;
.
22 Imagine we have a dummy node at the beginning of a general list The .
dummy node does not carry any data. It is not the first data node; it is an
empty node. Figure 15 - 56 shows a general list with a dummy node. Write
the code to delete the first node ( the node after the dummy node) in the
general list.
Dummy a
list
pPre pCur
,w*,
2 ke
.
on a general list Ho
node simplify the operation
with a dummy node. Write the
an empty general list
24’ Figure 15 - 57 shows general list .
code to add a node to this
empty
] Section 15.11 Practice Set
Dummy
pPre
25. Write the statements to ad cl a node in the middle of a general list with
the dummy node ( see Exercise 22 ) . Compare your answer with the
answer to Exercise 24. Arc they the same? What do you conclude? Does
the dummy node simplify the operation on a general list ? I low?
.
26 Imagine we have the two general lists shown in Figure 15 - 58. What
would happen if we apply the following statement to these two lists?
listl = list2 ;
—
listl
H —
Iist 2
.
27 \ \ hat would happen if we apply the following statements to the two lists
in Exercise 26?
temp = listl ;
-
while ( temp >link 1 = NULL)
-
temp = temp >link ;
-
temp > link = list2 ;
28. Imagine we have the general list shown in Figure 15 - 59. What would
happen if we apply the following statements to this list?
temp = list;
-
while (temp > link != NULL )
-
temp = temp >link ;
-
temp >link = list;
Chapter 15 Lists 999
Dummy
list
29. Imagine we have two empty stacks of integers , si and s2. Draw a picture
of each stack after the following operations:
Algorithm Exercise 29
1 pushStack (si, 3)
2 pushStack (si , 5 )
3 pushStack (si , 7)
4 pushStack (si, 9 )
5 pushStack (si , 11)
6 pushStack (si , 13)
7 loop not emptyStack (si)
1 popStack (si , x)
2 pushStack (s2 , x )
8 end loop
End Exercise 33
a queue of integers , Q . Draw
30. Imagine you have a stack of integers, S and
,
following operations:
a picture of S and Q after the
Algorithm Exercise 30
1 pushStack (S, 3)
2 pushStack (S, 12)
3 enqueue ( Q , 5 )
4 enqueue ( Q , 8 )
5 popStack (S, x )
6 pushStack ( S, 2 )
7 (Q / x )
enqueue
8 dequeue (Q/ y)
9 pushStack (S, x )
10 pushStack ( S, y )
End Exercise 30
of queue Q1 and queue Q2 are as shown . ?What
31. Imagine that the contents code is executed The
, „f 03 after the following
>">» <« > »“ r
31
Algorithmi Exercise
1 Q3 = createQueue
2 count continual
1000 Section 15.11 Practice Set
3 y = o
4 loop ( not empty Q 1 or not empty Q2)
1 dequeue (Ql , x )
1 if ( count > y )
1 dequeue (Q2, y)
2 end if
3 if (y equal count )
1 enqueue (Q3, x)
4 end if
5 count = count + 1
5 end loop
End Exercise 31
Ql : 42 30 41 31 19 20 25 14 10 1112 15
Q 2: 4 5 6 10 13
32. Draw all possible non similar binary trees with three nodes ( A , B , C ).
-
33. Draw all possible binary search trees for the data elements 5 , 9 , and 12.
34. Create a binary search tree using the following data entered as a sequen -
tial set :
14 , 23 , 7, 10 , 33 , 56 , 80 , 66 , 70
35. Create a binary search tree using the following data entered as a sequen -
tial set :
7 , 10 , 14 , 23 , 33, 56 , 66 , 70 , 80
Problems
38. Write a program that reads a list of integers from the keyboard , creates a
general list from them, and prints the result .
39. Write a function that accepts a general list , traverses it , and returns the
key ol the node with the minimum key value .
40. Write a function that traverses a general list and deletes all nodes whose
keys are negative.
41 . Write a function that traverses a general list and deletes all nodes that
are after a node with a negative key.
42. W rite a function that traverses a general list and deletes all nodes that
are before a node with a negative key.
Chapter 15 Lists 1001
43. Rewrite the function
deleteNode (see Program 15-2) using a general list
with a dummy node.
44. Rewrite the function searchList (
see Program 15-3) using a general list
with a dummy node.
45. Write a function that returns a pointer
to the last node in a general list.
.
46 Write a function that appends two general
lists together.
47. \ \ rite a I unction that appends a general list to
itself .
48. One of the applications of a stack is to backtrack
—
that is, to retrace its
steps. As an example, imagine we want to read a list of items, and each
time we read a negative number we must backtrack and print the five
numbers that come before the negative number and then discard the
negative number. Use a stack to solve this problem . Read the numbers
and push them into the stack ( without printing them ) until a negative
number is read . At this time , stop reading and pop five items from the
stack and print them. If there are fewer than five items in the stack, print
an error message and stop the program . After printing the five items,
resume reading data and placing them in the stack. When the end of the
file is detected , print a message and the items remaining in the stack.
Test your program with the following data:
- -
1 2 3 4 5 1 1 2 3 4 5 6 7 8 9 10 2 11 12 3 1 2 3 4 5 -
49. \ \ rite a function called copyStack that copies the contents of one stack
into another. The algorithm passes two stacks, the source stack and the
destination stack. The order of the stacks must be identical. ( Hint: Use a
temporary stack to preserve the order.)
50. Write a function , catStack , that concatenates the contents of one stack
on top of another.
identi -
function to check whether the contents of two stacks are
;
5 I . Write a
should be changed . You need to write affunotion that
cal. Neither stack works .
your function
prints the contents of a stack to verify that
copies the contents of one queue
52. Write a function called copyQueue that
to another .
that creates a queue from a stack.
53. Write a function called stackToQueue
top of the stack should be the front
After the queue has been created the
,
should be the rear of the queue. At
of the queue and the base of the stack be empty.
the end of the function, the stack
should
, write a function that calculates
and prints the
.
54 Given a queue of integers
integers in the queue without changing the
sum a:nd the average of the
,
55. Given a queue of integers, write a function that deletes all negative inte-
gers without changing the order of the remaining elements in the queue.
56 . Write a function that calculates and passes up to the calling function the
sum and average of the nodes in a tree.
57. Write a function that counts the number of leaves in a binary tree .
58. Write a function to find the smallest node in a binary search tree .
Projects
59. Write a program that reads a file and builds a key-sequenced general list .
After the list is built , display it on the monitor. You may use any appropri -
ate data structure, but it must have a key field and data. Two possibilities
are a list of your favorite CDs or your friends' telephone numbers.
.
60 Write a program to read a list of students from a file and create a general
list . Each entry in the general list is to have the student ’s name, a pointer
to the next student , and a pointer to a general list of scores. You may have
up to four scores for each student! . be program initializes the student list
by reading the students’ names from the text file and creating null scores
lists. After building the student list , it loops through the list , prompting
the user to enter the scores for each student . The scores prompt is to
include the name of the student.
61 . W rite a stack and queue test driver. A test driver is a program created to
test functions that are to be placed in a library. Its primary purpose is to
completely lest functions; therefore, it has no application use . The func -
tions to be tested are push stack , pop stack, enqueue, and dequeue. You
may include other stack and queue functions as required . All data should
be integers. You need two stacks and two queues in the program , as
described below.
a . Input stack: used to store all user input
b. Input queue: used to store all user input
c. Output stack: used to store data deleted from input queue
d . Output queue: used to store data deleted from input stack
Use a menu - driven user interface that prompts the user to select
either insert or delete. If an insert is requested , the system should prompt
the user for the integer to be inserted. The data are then inserted into the
input stack and input queue. II a delete is requested , the data are deleted
Irom both structures: the data popped from the input stack are enqueued
in the output queue, and the data dequeued from the input queue are
pushed into the output stack.
Processing continues until the input structures are empty. At this
point print the contents ol the output stack while deleting all of its data.
Label this output “Output Stack,” then print all of the data in the output
TT1
Chapter 15 Lists 1003
queue while deleting all of its data. Label this output "Output Queue. ”
Your output should he formatted as shown below.
Output Stack: 18 9 13 7 5 1
Output Queue: 7 13 9 18 5 1
L
Character Sets
Computers use numbers. They store characters by assigning a
number to
each one. The C language was originally designed using
the American Stan -
dard Code for Information Interchange (ASCII ), which was the
standard of
the time. Based on the Latin alphabet , ASCII uses 128 characters (valued
horn 0 to 1 2 ) stored as / - hit numbers. This is enough to handle the lower
/
and uppercase letters , digits, most common punctuation characters, and some
control characters. Later, an attempt was made to extend the ASCII character
set to 8 hits . The new code, which was called Extended ASCII , was never
internationally standardized.
To overcome the limitations inherent in ASCII and Extended ASCII , the
International Standard Organization ( ISO ) and the Unicode Consortium ( a
consortium of manufacturers of multilingual software) created a universal
encoding system to provide a comprehensive character set. The Unicode con -
sortium created Unicode. ISO created the Universal Character Set ( UCS ),
designated ISO 10646 standard.
The main UCS set, sometimes referred to as UCS-4, is a four- bvte code.
A subset of UCS - 4, called UCS-2 , is a two-byte code that is compatible with
UCS- 4 ; it contains the first 32 ,364 values in UCS-4.
Unicode was originally a two- byte character set. The early versions of
Unicode were only compatible with UCS-2. Unicode version 3 is a four- byte
code and is fully compatible with UCS- 4. The compatibility of Unicode with
UCS- 4 makes Unicode compatible with ASCII and Extended ASCII.
The ASCII set , which is now called Basic Latin, is included in UCS- 4
, which
and Unicode with the upper 25 bits set to zero. The Extended ASCII
is now called Latin - 1 , is also included w ith 24 upper bits set to zero. Figure A- 1
shows the compatibility between the different systems .
1006 Section A . 1 Unicode
Extended ASCII
UCS- 2
*
UCS-4 and Unicode
A. l Unicode
The prevalent code today is Unicode. Each character or symbol in this code
is defined by a 32- hit number. The code can define up to 2 ^ 2 ( 4, 294 ,967,296 )
characters or symbols . The presentation uses hexadecimal digits in the lol -
Iowing format :
u -xxxxxxxx
where each X is a hexadecimal digit . Therefore, the numbering goes from
U -00000000 to U - FFFFFFFF.
Planes
Unicode divides the whole space code into planes. The most significant 16
hits define the plane, which means we can have 65,536 planes. For plane 0,
the most significant 16 hits arc Os (0x0000 ) ; in plane 1 , the hits are 0x0001 ;
in plane 2 , they are 0x0002; and so on until in the plane 65 , 536 , they are
OxFFFF.
Each plane can define up to 65, 536 character or symbols. Figure A- 2
shows the structure of Unicode spaces and its planes.
Reserved
Reserved
U-
u. 2 -o IL
o UJ
°oo
Q
o
u. o °o O
o o
o
Plane 0000: Basic Multilingual Plane (BMP)
Plane 0001: Supplementary Multilingual Plane (
SMP)
Plane 0002: Supplementary Ideographic Plane (
SIP)
Plane 000E: Supplementary Special Plane (SSP)
Plane 000F: Private Use Plane (PUP)
Plane 0010: Private Use Plane (PUP)
Range Description
A-Zone ( Alphabetical Characters and Symbols)
U+0000 to U+00FF Basic Latin and Latin- 1
U+0100 to U+01FF Latin Extended
U+0200 to U+02FF IPA Extension, and Space Modifier Letters
U+0300 to U+03 FF Combining Diacritical Marks, Greek
U+0400 to U+04FF Cyrillic
U+0500 to U+05 FF Armenian, Hebrew
U+0600 to U+06FF Arabic
U+0700 to U+08 FF Reserved
U+0900 to U+09FF Devanagari, Bengali
U+0A00 to U+OAFF Gurmukhi, Gujarati
U+0B00 to U+OBFF Oriya, Tamil
Range Description
U+OEOO to U+OEFF Thai, Lao
U+OFOO to U+OFFF Reserved
U+ lOOO to U+ l OFF Georgian
U+ l 100 to U+ l IFF Hangul Jamo
U+ l 200 to U+ 1 DFF Reserved
U + 1 E00 to U+ lEFF Latin extended additional
U + l FOO to U+ 1 FFF Greek extended
U + 2000 to U+ 20FF Punctuation, sub / superscripts, currency, marks
U+ 2100 to U+21 FF Letter-like symbols, number forms, arrows
U+2200 to U+ 22FF Mathematical operations
U + 2300 to U+23 FF Miscellaneous technical symbols
U+2400 to U+24FF Control pictures, OCR, and enclosed alphanumeric
U+ 2500 to U+ 25 FF Box drawing, block drawing, and geometric shapes
U+ 2600 to U+ 26FF Miscellaneous symbols
U+ 2700 to U+ 27FF Dingbats and Braille patterns
U+2800 to U+ 2 FFF Reserved
U + 3000 to U+ 30FF CJK symbols and punctuation, Hiragana, Katakana
U+ 3100 to U+31 FF Bopomofo, Hangul Jamo, CJK Miscellaneous
U + 3200 to U+32 FF Enclosed CJK letters and months
U+ 3300 to U+33 FF CJK compatibility
U+3400 to U+4DFF Hangul
1-Zone (Ideographic Characters)
U+ 4E00 to U+9FFF CJK Unified Ideographic
O-Zone (Open)
U +A000 to U+DFFF Reserved
R -Zone (Restricted Use)
U+E000 to U+F 8FF Private Use
continued
TABLE A - l Unicode BMP (continued )
Appendix A Character Sets 1009
Range Description
U+F 900 to U+FAFF CJK Compatibility Ideographs
U+FBOO to U+FBFF Arabic Presentation Form-A
U+FCOO to U+FDFF Arabic Presentation Form-B
U +FEOO to U+FEFF Half marks, small forms
U +FFOO to U+FFFF Half-width and full-width forms
A.2 ASCII
The American Standard designed
Code f ,
code f() r 28 symbols. Today
,
in
integer .
when converted to an
1010 Section A . 2 ASCII
44 2C Comma
45 2D Minus
46 2E
47 2F /
48 30 0
49 31
50 32 2
51 33 3
52 34 4
53 35 5
continued
54 36 6
55 37 7
56 38 8
57 39 9
58 3A Colon
59 3B Semicolon
60 3C <
61 3D
62 3E >
63 3F 2
64 40 @
65 41 A
66 42 B
67 43 C
68 44 D
69 45 E
70 46 F
71 47 G
72 48 H
73 49
74 4A J
75 4B K
76 4C L
77 4D M
78 4E N
79 4F O
80 50 P
continued
TABLE A - 2 ASCII Codes (continued )
Appendix A Character Sets 1013
98 62 b
99 63 c
TOO 64 d
101 65 e
102 66 f
103 67 9
104 68 h
105 69
106 6A
107 6B k
continued
110 6E n
111 6F o
112 70 P
113 71 q
114 72 r
115 73 s
116 74 t
117 75 u
118 76 v
119 77 w
120 78 x
121 79 y
122 7A z
123 7B { Open brace
124 7C Bar
125 7D } Close brace
126 7E Tilde
127 7F DEL Delete
1 . I he lirst code (0), is the null character, which represents the absence ol
.
any character
. .
2 I he lirst 32 codes, 0 to 3 I , and the last code, 2 5 5 , are control characters
They can he checked with the iscontrol function .
.
3 I he space character, which is a printable character, is at
position 32 .
Appendix A Character Sets 1015
5 . I he uppercase and lowercase letters differ by only one bit in the seven
-
n
hit code. For example , character A is 1000001 (0x 41 ) and character a is
1 100001 ( 0x61 ). The difference is hit 6, which is 0 in uppercase letters
and 1 in lowercase letters. II we know the code lor one case, we can find
easily the code for the other by adding or subtracting 32 in decimal , 0x20
hexadecimal , or Hipping the sixth hit .
in
6. I he uppercase letters are not immediately followed by lowercase letters.
I here are some punctuation characters in between.
7. Digits ( 0 to 9 ) begin at 48 ( 0x30 ). This means that if we want to change a
numeric character to its face value as an integer, we need to subtract 48
from it .
\unnnn
// For UCS 2 -
\Unnnnnnnn
// For UCS 4 -
hical character using the uni-
Program A- l shows how we can print a grap
versal encoding in C.
Result:
Character chi: C
Character ch2: £
r
Keywords
rhe C language contains 37 keywords, also known as reserved words, that
cannot be used as identifiers for functions, variables, or named constants.
They are shown in Table B-l.
TABLE B - l C Keywords
1018 Section
and compl or
and_eq complex OR EQ
bitand imaginary struct
bool not_eq
Cl Auxiliary Symbols
the read -
A flowchart is a combination of symbols. Some symbols enhance
they do not directly show instruc-
ability or functionality of the flowchart;
'
tions or commands. They show the start and stop points . the order and
how one part of a flowchart is connected to
sequence of actions, and
another. These auxiliary symbols are shown in
Figure -
C l .
1020 Section C.l Auxiliary Symbols
( START )
( STOP }
FIGURE C - 2 Use of the Start - Stop Symbols
Flow Lines
Flow lines show the order or sequence of actions in a program. They connect
symbols. Usually a symbol has some entering and some exiting lines. I he
S FAR I oval has only one exiting line. The STOP oval has only one entering
line. We have already shown the use of flow lines in Figure C - 2. We will show
other flows in the examples that follow.
'
Appendix C Flowchorfing 1021
Connectors
Wc use only one connector symbol, a
circle with a number in it, to show con
nectivity. It is used in two situations. -
1• When
at
. . .
_ we reach the end of a column or
,
0 VV cont nues at the t0
page, but our chart is not fin-
shcd In this case, at the bottom of the flow
we use a connector to show
P the next column or page. This condi-
tion is shown in Figure C-3 ( a).
( START
.
)
o
(
.
STOP )
2. When we need to show logic details that do not fit in the flow. This is
much like an inset in a map used to magnify a part of the map. In a
flowchart, it magnifies part of the logic flow. In this case, we use two
.
sets ol connectors I he first set connects the main flow to the entry
point in the magnified flow; the second set connects the end of the mag-
nified flow to the return point in the main flow. This condition is shown
in Figure C -3 (b) .
In either case, the number in the connector can be a simple
serial num-
and number in
her for an on-page flow, or it can be a combination of a page
the form page.number for an off page
- flow .
C. 2 Primary Symbols
to show the instructions or actions
^ ^ ^^ ^
needeXtc soLe hcproblt '
pre ented n the algorithm. With
these symbols,
"Ipossible represent all three structured
to
.
programmmg constructs:
Sequence
Sequence statements simply represent a series of actions that must continue
.
in a linear order Although the actions represented in the sequence symbol
may be very complex, such as an input or output operation, the logic flow
must enter the symbol at the top and flow out at the bottom. Sequence sym -
bols do not allow any decisions or flow changes within the symbol .
Flowcharts use four sequence symbols: assignment, input/output, mod-
ule call, and compound statement. They are shown in Figure C - 4.
/
7 Input/Output Statement Compound Statement
Null Statement
It is worth noting that do nothing is a valid statement . It is commonly referred
to as a null statement. The null statement is considered a sequence state-
ment , since it cannot change the flow direction of a program. There is no
symbol lor a nidi statement. It is simply a flow line. Figure C - 2 is an example
of a null statement .
Assignment Statement
The assignment statement is shown using a rectangle. Within the assignment
symbol, the assignment operator is shown as a left -pointing arrow. At the
right side of the arrow is an expression whose value must be stored in the
variable at the left side. Figure C - 5 shows an assignment statement.
variable expression
Input/Output Statement
A parallelogram shows any
input or output, such as reading
or writing on the system from a keyboard
console. For example, an algorithm
value of two variables from the keyboard that reads the
and then writes their values on the
screen in reverse order is shown in
Figure C-6.
( START
^
j READ ( A )
j
l READ ( B )
J
j WRITE ( B )
j
j WRITE ( A )
j
l
STOP
chart for a program that calculates and prints the average of three numbers.
First , the flowchart for the called
This example has two noteworthy points :
module ( AVRG) does not begin with START . Rather , it shows the name of the
RE I URN , indicat -
module and the parameter list. Also, the exit oval contains
but , rather a return from a called
ing that it is not the end of the program
,
READ ( a )
7 sum
x+y+z
—
READ ( b )
i rslt
sum / 3
READ ( c )
I RETURN
AVRG ( ave, a, b, c )
WRITE ( ave )
I
/
STOP
Compound Statement
Although flowcharts have no actual symbol to show a compound statement,
we encapsulate all statements that make a compound statement in a broken-
line rectangle. In a C program, compound statements represent a block ol
code, code that is enclosed in braces. In a flowchart , we use a dashed rectan-
gle to enclose a compound statement. An example of this is shown in Figure C -9.
Selection Statements
Unlike the sequence statements, conditional statements can cause the flow
of the program to change. They allow the execution of selected statements
and the skipping ol other statements. Structured programming has two selec -
tion statements: two - way and multiway selection.
T
FIGURE C - 8 i f ... else Statement
Multiway Selection
symbol used with structured pro-
The second application of the selection selection is
s implementation of multiway
gramming is multiway selection. C selection statement is nothing
multiway
the switch statement. Actually, the does
for the if...else statement . If a
language
more than a shorthand notation nested
the same logic is implemented
using
not have a switch statement
if ...else statements or the else- if construct .
1026 Section C. 2 Primary Symbols
START
READ ( aNum )
/
\aNum > 10y
N/ IR
newNum
aNum - 10
—
WRITE (newNum)
/
STOP
Figure C - 10 shows the use o! the switch statement. As you can see, we
can have as many branches as we need. On each branch, we are allowed to
have one, and only one, statement. Of course, the statement in each branch
can he a null or a compound statement. But remember that only one state-
ment in each branch is allowed; not less, not more. Also remember that the
whole Figure is only one statement, not two or three; it is one switch state-
ment . ( II these rules sound familiar, remember that the switch statement is
nothing but a shorthand form of if . .. else , so the rules are the same for both.
Let ’s design an algorithm for a program that reads one character repre-
senting a letter grade and prints the corresponding grade point average
(GPA). Figure C - l 1 shows this design.
Looping Statements
C has three looping statements: for , while, and do ...while .
for Statement
.
I he for statement is a counter-controlled loop It is actually a complex state-
ment that has three parts, any of which can he null: ( 1 ) the loop initialization,
which normally sets the loop counter; ( 2 ) the limit test; and (3 ) the end-of-
loop action statements, which usually increment a counter. Since the for
.
statement is a pretest loop, the loop might not be executed If the terminating
condition is true at the start, the body of the for statement is skipped.
I
Appendix C Flowcharting 102 /
T1
expression
m n o P
m Action n Action o Action p Action
?
FIGURE C- 10 Multiway Selection Statement
( START )
READ ( grade )
1
grade
A B jc \D
gpa 4 0 gpa 3.0l gpa
^ ^oj ^^^j
-2 gpa 9Pa ^ 0.0
"
WRITE ( gpa )
STOP
As is the case in
loop can contain one
anJ 0n
/ Initializationp
(Update \-
\ Condition M
Body
(One Statement )
\
FIGURE C - 1 2 for Statement
The for loop uses three actions: initialization , test , and update. However,
only two of these actions are required in each iteration flow. In the first flow,
when we enter the loop , only initialization and loop are used . This flow is
shown in Figure C - 13 ( a ). In all iterations except the first , only update and
test are used. Figure C - 13 ( h ) shows these flows.
1
Initialization Initialization
Update Update
TestV - ^ estV
^-
T T
Iteration Exit From Iteration Iteration Exit
( a) Initial flow (b) 2 ... n flow
FIGURE C -l 3 for Statement Flow
Let 's design an algorithm that will read 20 numbers and print their sum .
Since the number of times is known in advance, for is an excellent choice lor
the looping construct. I be design for this program is shown in Figure C - l 4.
while Statement
The second looping construct is the while statement. The major difference
between the for and while loops is that the while loop is not a counting loop.
Both are pretest loops; therefore, like the for , the body of the while loop may
never be executed .
We use the same basic symbol for the while loop, but since there is only a
limit test , the internal divisions are not necessary. Figure C - l 5 shows the
basic format of the while statement .
Appendix C Flowcharting 1029
START
sum
— o
READ ( num )
i ++
i — 0\ F
i < 20
T
sum 4<
sum + num —
o Note that t h e "'
body of the loop is a
compound statement
(
WRITE ( sum )
l
STOP
7
)
FIGURE C- 14 Design to Read 20 Numbers
^ —
condition
Body
(One Statement)
Let 's design another program that reads numbers from the keyboard and
we may be
prints their total. This time, we don t know how many numbers
’
reading. All we know is that all the numbers are positive . We can therefore
signal the end of the numbers by having the user key - 1 .
Since we don ’t know how many times we will loop
, we need a different
well designed for this type of logic . The program flow is
construct ; the while is
first number is read before
- .
seen in Figure C 16 Note that in this design
loop
,
and
the
is common in pretest loops.
the loop. This is know n as priming the
) Section C. 2 Primary Symbols
( START
sum o
/ num > 0
\—
M
F
U
sum
sum + num
/
/READ ( num
/WRITE ( sunn f
( STOP
I
^
)
FIGURE C - l 6 Program Flow to while Read and Total
"1
Appendix C Flowcharting 1031
Body
(One Statement )
, rTA
— condition \
(
I7
FIGURE C- l 7 do...while Statement
( START
/ WRITE /
/ ( 'Enter number " )i
j READ ( num )
J
I
(num < 1)
T
or
( num > 5 )
process ( num )
(not shown)
T
( STOP
1033
1034 Section D. l Computer Numbering System
1 4 7 8 2 7 2 1 Digits
4 3 2 1 0 -1 -2 -3 Positions
Weights
In the decimal system , each weight is 10 raised to the power of its position .
The weight of the symbol at position - 1 is 10 ! ( 1 / 10 ) while the weight of the
~
Weights
In the binary system , each weight equals 2 raised to the power of its position .
-
The weight of the symbol at position 1 is 2~ l ( 1 /2 ); the weight of the symbol at
position 0 is 2° ( 1 ); the weight ot the symbol at position 1 is 2 * ( 2 ) ; and so on.
Appendix D Numbering Systems
1035
Binory -to-Decimal Conversion
To convert a binary number to
digit by its weight and add all decimal we use the weights. We multiply each
of the weighted results. Figure
we can convert binary D-2 shows how
1001110.101 to its decimal equivalent 78. 625.
Decimal: 78.625
Divide Multiply
>
0 H H H 1 2 4 9 19 H 39 H 78 M 0- 625 H 0 -25 H 0 50 H 000
F T T T T To T T T l
1 0 0 1 1 1 1 0 1
Binary: 1001110.101
by
To convert the fractional part , we need to multiply the fractional part
either a 0 or a 1 ; it becomes the binary
two. The integral in the product is
by two to get the
digit . We then multiply the fractional part the product
of
is zero . For example, to con -
next binary digit and continue until the product
, resulting in 1.25 . We take the inte-
vert 0.625 to binary, we multiply it by 2 . In the next
(0.25 ) to the next step
gral part 1 , and move the fraction part 5
1036 Section D.l Computer Numbering System
step, after we multiply the 0.25 by two, we get 0.5. The integral part is 0,
.
which we keep The fractional part is 0.50 , which we move to the next step.
However, we need to limit the process because the product may never
become zero. If the resulting fraction becomes 0.0 , we stop because more
binary digits does not contribute to the precision of the number. 11 the prod -
uct does not become zero, we need to make a decision as to how many digits
we need at the righthand side of the binary number and stop when we
have them .
Weights
In the hexadecimal system , each weight equals 16 raised to the power ol its
position . The weight of the symbol at position 0 is 16° ( 1 ) ; the weight ol the
symbol at position 1 is 161 ( 16); and so on .
3 A 7 3 A 0 C
163 1i 161 °
16 16
1
16
16
2 3
Weights
12 ,288 2 ,560 112 3 0.625 0.0 0.003 Weighted Results
+
14,963 0.628
Decimal: 14963.628
Divide Multiply
4 >
0 3 58 935 — 14963 0.628 0.048 0.768 — 0.288
V T T T .X X 1c
3 A 7 3 A 0
Hexadecimal: 3A 73 . A0C
Base 256
Another numbering system that is used in computer science is base 256. We
we need to make a
encounter this base normally in two situations: when
and when we are dealing with Internet
number from individual bytes
addresses.
bytes where each byte
In the first application, we normally have individual
255. We need to consider several
contains an unsigned number between 0 to
the situation with four bytes.
of these bytes as a number. Figure I -6 shows
)
00 0 5_
o
2 1 0
3
131 • 32 •7 • 8
3 2 1 0
Weights
In base 256, each weight equals 256 raised to the power of its position. The
weight of the symbol 1 at position 0 is 256 ( 1 ); the weight of the symbol at
°
position I is 2561 ( 256 ) ; and so on.
A Comparison
I able D- l shows how the three systems represent the decimal numbers 0
through 1 5 . As you can see, decimal 1 3 is equivalent to binary 1 101, which is
equivalent to hexadecimal D.
.
1 In base 256, each symbol can be one to three digits.
I
Appendix D Numbering Systems 1039
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 mi F
Other Conversions
. .,
Binary- to- Hexadecimal Conversion
To change non be ta
from the right by fours. Then
we c
Sk
. we convert binary
^ D8
mal equivalent using Table ^ )
FIGURE D- 8 Rinnrv-to-Hexadecimal
Conversion
5
1040 Section D . 2 Storing Integers
Hexadecimal
2 8 E
10 1000 1 1 1 0
Binary
D . 2 Storing Integers
We have discussed bow integers and real numbers are presented in different
bases. Although base 16 and base 256 are used in computer science, data are
stored in the computer in binary. Numbers must be changed to base 2 to be
stored in the computer. All of our discussions so far have ignored the sign of
the number. In computer science, we use both positive and negative num-
bers. We need some way to store the sign. In this section, we concentrate on
bow integers are stored in a computer; in the next section, we show bow real
numbers are stored.
Unsigned Integers
Storing unsigned integers is a straightforward process. The number is
changed to the corresponding binary form, and the binary representation is
stored, lor example, an unsigned integer can be stored as a number from 0 to
1 5 in a 4 - bit integer, as shown in Figure D- 10.
fa
o
( oooo )
7 8
( oin ) ( 10OP )
Appendix D Numbering Systems 1041
15
QTTT)
in
FIGURE D- 10 Unsigned Integers Format
Signed Integers
Storing signed integers is different from storing unsigned integers because
we must consider both positive and negative numbers. Four methods
are
designed to store signed integers in a computer: sign and magnitude , one’s
com plement, two s complement , and Excess system .
..L - Complement
Complement
+7 -0 -7
+0
0111 1000
foooo )
FIGURE D- 1 1 Signed Integer Format
.
3 To change the sign of a number, we need to flip only the first hit We can -.
not use the complement operator (~) we learned in Chapter 14 because
this operator will flip all hits, not just the leftmost hit .
.
4 Addition and subtraction are very inefficient operations. We need to do
the following:
a. Separate the sign from the magnitude.
b. Compare two magnitudes to see if we need to add or subtract the
magnitude.
c . We have eight cases in total, four for addition and four for subtraction.
d. Insert the sign of the result after the absolute value of the result is
determined.
Applications
Properties 2, 3, and 4 make this method unsuitable for a general-purpose
computer in which we need to perform mathematical operations. The sign
and magnitude method, however, is used in computer science when we do
not need mathematical operations. One of these areas is storing analog and
digital signals.
One's Complement
One’s complement is similar to sign and magnitude; however, the partition
of the range between the positive and negative numbers is different. The
numbers are arranged symmetrically. A positive and a negative number are
.
symmetric with respect to the middle of the range Figure I) - 12 shows how
the positive and negative numbers are distributed.
Complements
+0 +3 +7 -7 -3 -0
/ \
( 0000 ) ( 0011 ) ( 0111 ) ( 1000 ) ( 1100 ) CmD
FIGURE D - l 2 One's Complement Format
Properties
Let us summarize the one’s complement properties:
1 . The leftmost hit contains the sign; 0 for positive, I for negative.
2. There are two zeros in this method: +0 ( ()()()()) and -0 ( 1 1 1 1 ).
3 . To flip the sign of a number, we flip each individual hit. We use the com-
plement operator (~) we learned in Chapter 14 to find -A from A as
shown in Figure D- l 3.
FIGURE D- 1 3 Flipping Signs in One's
-(-3)
-3
Complement
- (0 0 1 1 )
- (1 1 0 0)
>ndix D
1100
0011
Numbefingjysfems 1043
11
The figure also shows that if we complement
complement, we also can find the complement-A, we get back A. In ones
For example, -3 is stored as 15 -3 or 12, of A as -A = ( 2” -1 ) - A.
when n is 4 ( number of hits in
the integer ).
4 . If we add A + (-A) we get 0 (all
- hits are Is) . We saw this property in
Chapter 14 when we used checksum.
Addition and subtraction are very simple operations. To add (A )
+ B , we
just add the numbers hit by hit . To subtract A
- B, we just add A and the
complement of B. In other words, A - B = A + (- B ) . The only thing
we
need to consider about adding or subtracting is to add the carry
produced
at the last column to the result . Figure D- 14 shows how
numbers are
complemented, added , and subtracted.
Add
1 1111
( + 3) 0011 ( +3) 0011
+ (+2) +0010 + (-2) + 1101
( +5) 0101 (+ D 0000
> 1 -
0001
Applications
one’s complement. The
Early general - purpose computers, such as VAX, used for arithmetic cal-
does not use this method
modern general-purpose computer
of two and the need lor
0 s)
culation because of the second property (existence , ones complement
. However
keeping track of the carry from the last column
1044 Section D.2 Storing Integers
.
arithmetic has its own place in computer science We saw the use of it in the
checksum calculation in Chapter 14. We simulated the use of ones comple-
ment with unsigned numbers. To simulate adding the carry from the last col-
umn to the partial result , we used modulo 2n — 1 in our calculations.
Complements
0 +3 +7 -8 -3 -1
/ \
( 0000 ) ( 0011 ) ( 0111 ) ( 1000 ) ( 1101 ) ( 1111 )
FIGURE D- 15 Two's Complement Format
Note that the 0 in the positive range, which means that the negative
.
range can accommodate one more number In the figure, the first half of the
range accommodates 0 to 7; the second half accommodates - 1 to -8.
Properties
Let us summarize the two’s complement properties:
.
1 The leftmost bit contains the sign of the number; 0 for positive, I for
negative.
.
2 I here is only one zero in this method: 0 ( ()( ) ( )() ).
3. fo change the sign of a number, we need two operations. First we need to
flip each individual bit, then we need to add 1 to the previous result as
shown in Figure D- 16.
-3 ~ (0 0 1 1) + 1 1 1 0 1
-(-3) ~ (1 1 0 0) + 1 0 0 1 1
Applications
One single zero and the simplicity of adding and subtracting have made two’s
complement arithmetic the best candidate for modern computers. C uses the
hardware instruction set of the computers it runs on, which almost univer-
sally uses two ’s complement arithmetic.
^
( Store Integer
Complement Complement
the number Sign is positive
the number
*
Change the n-bit
Store the number binary to decimal
and add sign
T T
Return Return
j /
EXAMPLE D l Let s follow the storing algorithm to see how +76 is stored in a 16 - hit integer.
-
Decimal number: +7 6
Convert absolute value: 0000000001001100
Store value: 0000000001001100
EXAMPLE D- 2 Let ’s follow the storing algorithm to see how -76 is stored in a 16 -bit integer.
I he absolute value ol the number ( 76 ) is changed to a 16 - bit binary number.
I he sign is negative, so we need to complement the number and add 1 to it as
shown below.
Decimal number: -7 6
Convert absolute value: 0000000001001100
Complement: 1111111110110011
Add 1: 1111111110110100
Store value: 1111111110110100
*
Appendix D Numbering Systems
1047
EXAMPLE D- 3 Let ’s Follow the iretrieving
algorithm to see how a
ger is retrieved. The process is stored value in a 16- bit inte-
j
shown below.
Retrieved value:
Complement: 1111111111101011 ( sign 1
Add 1: 0000000000010100
Convert to decimal: 0000000000010101
21
Add sign: -21
Excess
I here are applications that require more
numeric comparisons than arith -
metic operations. In these cases we can use a simple strategy
named
by the IEEE, to store positive and negative integers. We just add a fixedExcess
value
(called the bias value ) to the negative number
to make them non - negative
(zero or positive) when we store the numbers. We
subtract the same bias
value when we retrieve the number. For example, in our hypothetical 4- bit
-
integer, we can store numbers From 7 to 8 as shown in Figure D- 19. In other
words, we store both positive and negative numbers in an unsigned Format.
-7 0 1 8
When two numbers are stored using the Excess method , we can easily
compare them on the value; we do not have to worry about the sign of the
numbers. When adding two numbers in the Excess system , the bias value is
added twice ; therefore, the bias value must he subtracted when we store the
the
result. When subtracting two numbers, the bias value is cancelled during
be added hack when we store the result .
subtraction therefore
; , it must
Overflow
. Overflow can be the
Wc need to discuss a very important issue, overflow
source of much confusion for a programmer
. Sometimes when we print a
number, we get a surprising result . Often the
reason is that an overflow has
are stored on a limited size word
occurred . Overflow occurs because integers integer is only 4 bits, we can
of the
in the computer. For example, if the size
1048 Section D.2 Storing Integers
15 0 -1 0
14 1 -2 +1
13 2 -3 +2
12 3 -4 +3
Unsigned Two's
integer complement
11 4 -5 +4
10 5 -6 +5
9 6 -7 +6
8 7 -8 +7
I he circle for the unsigned integer shows that 15 + 1 is 0. The circle lor
two’s complement shows that 7 + 1 is -8. If we increment an unsigned value
holding the maximum possible number, we get 0. If we increment an integer
value holding the maximum possible value, we get the minimum possible
value. We cannot test this concept with 4 - bit integers , but we can prove it
with the actual size of the integer in any computer as shown in Program D- l .
continued
PROGRAM D - ] Demonstrate
Overflow ( continued )
11 // Local
12
Definitions
unsigned short x
13 = USHRT MAX;
short y = SHRT__MAX ;^
14
15 // Statements
16 printf( " Maximum
17 unsigned short value:
X+ +; % u \n",
18 printf( "Maximum
19
unsigned short value +
1: % u \n ",
20 printf( " Maximum
21
short value:
y++; %d \n\
22 printf( " Maximum
short value + l j
23 %d \n ",
24 return 0;
2 5 } // main
Results:
Maximum unsigned value: 65535
Maximum unsigned value + 1 : 0
Maximum short value: 32767
Maximum short value + 1 : - 32768
-10011.10101010 x 2 100
Normalization
We have one problem to solve before we can store this number in the com -
puter: the position of the binary point ( the point that separates the integral
part from the fractional part ). We can store only binary digits, not a point .
I he solution is normalization . We normalize the number so that the point is i
always at a fixed position. Tradition and the standard state that the number
should have only one binary digit to the left of the point . For non -zero values,
1050 Section D.3 Storing Real Numbers
the digit is a 1 . To normalize the number, therefore, we shift the point to the
left or to the right based on its original position . Shifting binary numbers
requires multiplying or dividing the number by two lor each shift. In other
words, if we move the point to the left , we need to add the number of digits
shifted to power ; if we move the point to the right , we need to subtract the
number of digits shifted from the power. The normalized version of the previ -
ous number is shown below. Note that we moved the point four positions to
the right , and we added 4 to the power, making it 8 ( the power is represented
in binary ) .
-1.001110101010 x 21000
After normalization , we are left with only the sign , the precision , and the
power to store; we do not have to store the integral part ol the precision or
the binary point .
Fortunately, C99 provides us with help to find how real numbers are
stored in the computer. We can use the % A or %a conversion code to print
the real number in a format that represents these values. Program D - 2 dis -
plays a positive and a negative real number. Note that the values are the same
except for the sign .
Results:
-314.625: -0X 1.3AA0000000000P+8
+314.625: 0X 1.3AA0000000000P+8
Sign
The Sign of the number is
stored using one bit (0 for plus and I for minus).
Exponent
The exponent ( power of 2 )
defines the power. Note that the power can he
negative or positive. Excess is the
method used to store the exponent .
Mantissa
I he mantissa is the binary number to
the right of the binary point. It defines
the precision of the number. The mantissa is
stored as an unsigned integer.
IEEE Standards
I he Institute of Electrical and Electronics Engineers (
IEEE) has defined two
standards to store numbers in memory (single precision and double preci-
sion ) . These formats are shown in Figure D-21 . Note that the number inside
the boxes is the number of bits for each field .
Excess 127
23
Sign Exponent Mantissa
Single Precision
Excess 1023
II r
1 11 52
Sign Exponent Mantissa
Double Precision
Store Real )
^
( Retrieve Real J
negative positive 1 0
sign? leftmost
bit ?
s 1 s 0 S S
* *
Concatenate Concatenate
s e m S D
and store and present
T
Return
I
Return
V
EXAMPLE D - 4 We show how the real number 123.8125 is stored in the computer using the
storing algorithm in Figure D - 22 and IEEE for single precision.
1 . The sign is positive; s = 0.
2. T he absolute value in binary is 1111011.1101
3. We normalize it to 1.1110111 101. I he value ol in in IEEE single preci -
sion ( 23 hits ) is m = 1 I 101 1 I 1010000000000000 and the power is 6.
4 . The value of e in IEEE single precision is 6 + 127 or 10000101.
5. When we concatenate s, e, and m , we have the value shown below:
0 10000101 11101111010000000000000
EXAMPLE D-5 We show how to find the decimal value of the following 32- hit real number
stored using IEEE single precision.
1 10000010 00011000000000000000000
We use the retrieve algorithm in Figure D-22.
1 . The leftmost bit is 1 ; S =
Appendix D Numbering Systems 1053
\
\
E.l limits.h
Table E - l contains hardware- specific values for the integer types.
E. 2 f l o a t . h
-
Table E - 2 contains hardware-specific values for the floating point types.
. *
Function libraries
In this appendix we list most of the standard functions found in
the C language.
We have grouped them by library so that related functions are grouped
together. We have also listed them alphabetically in Section F.l for your con-
venience. Note that not all functions are covered in the text and there are
functions in the libraries that are not covered in this appendix.
105
1 Function Index
F. 2 Type Library
I he following functions are found in ctype . lt .
_t a _char)
iswspace int iswspace ( wint
_t a _char)
iswupper int iswupper ( wint
iswxdigit iswxdigit
_t a _char)
int ( wint
_t a _char)
towlower int towlower ( wint
towupper int towupper ( wint
General I /O
General input/output contains functions that apply to all files.
clearerr void clearerr ( FILE * sp);
fclose int fclose ( FILE * sp );
feof int feof ( FILE * sp );
terror
terror ini ( FILE * sp);
_
(const char * extn name , const char * filc mode ) ;
__
fopen
freopen
FILE *
FILE *
fopen
frcopen _
( const char * extn name, const char * file mode, FILE * stream );
Formatted I /O
Convert text data to/from internal memory formats.
fprintf int fprintf ( FILE* fileOut , const char * format _string, ... ) ;
_
fscanf
printf
int
int
fscanf
printf ( const char * format string, ... ) ; _
( FILE * fileln , const char * format string, ...) ;
Character I /O
Read and write one character at a time .
fgetc
fputc
int
int
fgetc
fputc
( FILE * sp ) ;
_
( int char out, FILE * sp ) ;
getc int getc ( FILE * sp ) ;
getchar int getchar (void ) ;
putc int putc _
( int char out , FILE * sp ) ;
putchar int putchar _
( int char out ) ;
ungetc int ungetc _
( int char out , FILE * sp ) ;
File I/O
These functions work with binary files.
( read
( seek
size _t fread _ _ _
( void* in area , size t size, size t count ,
FILE* sp);
int fseek ( FILE* sp, long offset , int from
Joe );
ftell long ftell ( FILE * sp ) ;
( write size_ t fwrite ( const void * out_data , size_ t size , size_ t
count , FILE* sp ) -
rewind void rewind ( FILE * sp );
String I /O
Read and write strings.
fgets char 4 ( gets (char 4 string, int size, FILE 4 sp );
fputs int fputs ( const char 4 string, FILE 4 sp );
gets char 4 gets (char 4 string) ;
puts int puts ( const char 4 string ) ;
Math Functions
.h .
The following math functions are found in stdlih
int abs ( int number );
ahs
div div_ t div ( int numerator, int divisor ) ;
long labs ( long number ) ;
labs
long long llabs ( long number ) ;
llabs
_ Idiv ( long numerator, long divisor ) ;
Idiv
lldiv
ldiv t
lldiv t _ lldiv ( long long numerator, long long divisor
( void ) ;
);
Memory Functions
The Following are memory allocation Functions.
calloc void * calloc _ _
( size t num elements, size t element size ) ; _ _
Free
malloc
void
void
free
malloc
( void * );
_ __
( size t nilm bytes);
_ _
realloc void * realloc ( void * stge ptr, size t elemcnt size );
Program Control
The Following Functions control the program flow.
abort void abort (void );
_
atexit
exit
mt
void
at ex it
exit _
( void ( * ) function name (void ));
( int exit code );
Exit void Exit _
( int exit code ) ;
System Communication
The Following Function communicates with the operating system.
system int system _
(const char * system command ) ;
Conversion Functions
I he Following functions convert data From one type to another.
atof double' atof ( const char * string);
atoi int atoi (const char * string) ;
atol long atol ( const char * string) ;
atoll long long atoll ( const char * string) ;
strtod double strtod (const char* str, char** next str );_
strtof float strtof .
(const char* str char* * ncxt str ) ; _
strtol long strtol _
( const char * str, char** next str, int base);
_
strtold
strtoll
long double
long long
strtold
strtoll
(const char* str, char * * next str );
__
(const char * str, char * * next str, int base);
strtoul
strtoull
unsigned long
unsigned long long
strtoul
strtoull _
( const char* str, char** next str, int base );
( const char* str, char* * next str, int base);
Memory Functions
The Following Functions manipulate block data in memory.
__
memchr void * memchr _ _
(const void mem , int a char, size t bytes );
1
mememp int mememp ( const void * mem I , const void * mem 2, size t bytes ) ;
memepy void * memepy _ _
( void * to mem , const void * Fr mem , size t bytes );
memmove void * memmove _
( void * to mem , const void * fr _ mem , size_ t bytes ) ;
AppendixP Function Libraries 1069
String Functions
I IK Following functions manipulate strings.
*
strlen size t
char to_str. const char fr_str, size_t bytes );
strlen ( const char 4 str ) ;
strncat char 4 strncat ( 4
strncmp int strncmp (const char strl const char str 2 , size_ t bytes );
4 4
_ a _char)
wcscat wcscat str const ;
\
_
wcschr
_ strl. vvcharjwcharj
(const \vchar t 4 str
4
\ vchar t wcschr ;
4
str ) ;
vvcscmp
vvcscpy
int
wcharj 4
vvcscmp
vvcscpy _ __ . const vvchar_t fr__str 2
( const \vchar
( vvchar t 4 to
t4
str
, const
4
);
t 4 str );
vvcscspn sizej vvcscspn
_t strl , wchar 2 _
( const \vchar t4 const
vvcslcn sizej vvcslcn
_ __ const vchar_t fr__ size bytes
(const wchar 4
str );
); 4
2 size_ t bytes
vvcsncat wcharj 4 strncat ( \vchar t 4 to str, \ str, t
4 );
_str, const vchar_ fr__ _ bytes)
t strl , const wchar t
4 str ,
( const \vchar
vchar _
wcsncmp int wstrncmp
wcsncpy wcharj4 wcsncpy (\ t 4 to \ t4 str, size t ;
4
const wcharj strl const wchar t 2 str );
wcharj a _char);
4
wcspbrk wcharj4 wcspbrk ( ,
4
wcsrchr wcharj4 wcsrchr (const wcharj str,
4
wcsspn sizej wcsspn ( const wcharj4 strl , const wcharj str 2 ) ;
wcharj 4
wcsstr (const wcharj 4
strl , const wcharj 4
str 2 );
wcsstr 4
( wcharj strl , const wcharj str 2 ;
4 )
vvcstok wcharj 4 vvcstok
r
F.7 Time Library
The following functions are found in time.h.
clock clockj clock
difftime
( void ) ;
( timej: time start , timej time end ;
) _ _
difftime double
(struct tm caljime );
4
gmtime struct tm
4
gmtime
( const timej numjime );
4
Compilation
Source
Preprocessor
h
Translation
> Translator
•
H
*
Object
Program Module
Unit
1071
1072 Section G . 2 Macro Definition
# include <filename.h>
# include "filepath.h "
Figure G - 2 shows the situation of the source file before and after the pre-
processor as included the header files.
} // main } // main
Before After
G. 2 Macro Definition
The second preprocessor task is expanding macro definition . A macro defini -
tion command associates a name with a sequence of tokens. The name is
Appendix G Preprocessor Commands 1073
The body is the text that is used to specify how the name is replaced in
the program before it is translated. Before we discuss different applications of
the macro definition, let us clarify two important issues:
1. Macros must be coded on a single line. If the macro is too long to fit on a
line, we must use a continuation token. The continuation token is a
.
backslash ( \ ) followed immediately by a newline If any whitespace is
present between the backslash and the newline, then the backsla
sh is not
a continuationtoken, and the code will most likely generate an error. An
example of a macro continuation is shown below:
#define PREND \
printf ( " Normal end of program PA 5 01./n ); -
"
# define ANS = 0
shown below:
compile error when it is used
as
creates a
num = ANS;
// W e needed num - 0;
num =- 0;
wanted.
which is not what we
—
is to define a constan
t . As we saw in
#define SIZE 9
#define ROWS 5
#define COLS 4
int main ( void ) int main ( void )
{ } // main
int ary[ ROWS][ COLS ]; int ary[ 5 ][ 4 ];
} // main } // main
Before After
main flush
\ \ hile functions are called when the program is executed , macro defini-
tions are inserted into the code during the preprocessing. The preprocessor
replaces the macro name in the program with the macro body. The macro
definition is simpler than the function , however, because we do not have to
write the function declaration , the function header, or the return statement.
It is also more efficient because the overhead associated with a function call
is not required ; the substituted code simply becomes statements in the pro-
gram . Figure G - 5 shows how we can use a macro definition to simulate the
function in Figure G - 4.
#d e f i n e FLUSH \
w h i l e ( getchar ( ) i = \n ' )
i n t main ( void ) i n t main ( void )
{ {
FLUSH ;
while ( getchar ( ) ! = \n ' ) ;
} / / main
} / / main
After
Before
#define \
PRODUCT( x , y ) x *
int main ( void ) int main ( void ) int main ( void )
{ { {
p = PRODUCT ( 4 , 5); p = x * y; p = 4 * 5;
1. The body of the macro replaces the macro call with the same actual
parameters used in the macro definition .
2 . The actual parameters are replaced with formal parameters.
p = x * y;
p = a + 1 * b + 2 ; // We need ( a +1 ) * ( b+2)
# define PRODUCT( x , y ) ( x ) * ( y )
Appendix G Preprocessor Commends 1077
Alter the first and second step, we have the correct result :
EXAMPLE G- l Macros can replace many simple functions. One good example is the func-
tion we wrote in Chapter 14 to rotate a number n hits. This function can be
written as a macro as shown below. Note the use of backslash to continue the
definition on the next line.
_
# d e f i n e ROTATE LEFT ( x , n ) \
( ( (x) « (n) ) | ( (x) » ( 32 -x) ) )
tdefine POWER 2 ( x ) 1 « ( x )
)
tdefine getchar ( ) getc ( stdin
Nested Macros ga
nested macros by simply rescannin
It is possible to nest macros. c handles an expansion results in a new state-
, if
line after macro expansion. Therefore will be properly expanded . For exam -
macro
ment with a macro, the second
below.
pie, consider the macros shown
a) * ( b)
tdefine PRODUCT(a, b) (CT ( a , a )
tdefine SQUARE(a) PRODU
The expansion of
x = SQUARE ( 5 ) ;
n :
results in the following expansio
;
x = PRODUCT(5 , 5 )
1078 Section G. 2 Macro Definition
x = (5 ) * (5);
Undefining Macros
Once defined, a macro command cannot be redefined. Any attempt to rede-
fine it will result in a compilation error. I lowever, it is possible to redefine a
macro by first undefining it, using the ttundef command and defining it again
as shown below:
#define SIZE 10
#undef SIZE
#define SIZE 20
Predefined Macros
The C language provides several predefined macros, fable G- l lists some of
them. Note that these macros cannot be undefined using the undej command.
Command Meaning
DATE Provides a string constant in the form "Mmm dd yyyy"
containing the date of translation.
FILE Provides a string constant containing the name of the
source file.
Results:
line 10
file ApG-Ol.c
date May 23 2005
time 20:36:28
ISO compliance 1
line 15
#
_
define PRINT VAL (a) printf (
#a » contains: %d\n
following example
, as shown in the
When called in a program
# define FORM(T , N ) T# # N
int FORM ( A , 1 ) = 1;
float FORM ( B , 3 ) = 1.1;
char FORM ( Z , 8 ) = ' A';
we get
int Al = 1;
float B3 = 1.1 ;
char Z8 = ' A' ;
#define PI 3.14
in Figure G - 7.
# if expression
Appendix G Preprocessor Commands 1081
Code to be included if
“expression" is false.
#endif
#if expression
# if expression
code for non-zero code for non-zero
expression expression
# else # endif
# endif
a. else Section Empty
# if Expression
# if expression
code for zero
#else expression
code for zero # endif
expression
# endif
b. // Section Empty
EXAMPLE G - 4 Let us see how a conditional command can help us in program development .
Imagine we need to include a very large file in our program . However, we
want to test the syntax of our program gradually, first without the included
file and then with it . One way we can do this is to include the file in our pro-
gram using conditional commands as shown below:
# if 0
# include " large.h "
# endif
When we are ready to test the syntax for the entire program , we change
the condition to true .
# if 1
# include " large.h "
# endif
As we see in a later example, there are easier and better ways to do this.
EXAMPLE G - 5 Imagine we need to include three files in our program : filel.h, file2.h,
file3.h. These files contain generalized code that are used in projects.
Each file must therefore be able to stand alone. When combined , the result
may he multiple inclusion of common macros. If the macros are duplicated ,
we get a compilation error because the preprocessor does not allow duplica -
tion of macros.
To prevent this error we have a project standard : whenever we need to
define a macro, we define it in a conditional command using the defined
operator we discussed in “The defined Operator ” in Section G . 2 . So.
instead of
#define GO 0
we use
# if Idefined ( GO)
#define GO 0
# endif
Now we are safe. Even if all three files have used the same conditional
definitions, only the first one appears in the program ; the rest are skipped by
the preprocessor. When the first definition is encountered , GO is not defined ,
so the expression is true and it becomes defined . In the second and third files ,
( > ( ) is already defined , so the conditional
compilation expressions are false,
and the define command is bypassed .
Appendix 6 Preprocessor Commonds 1083
, it is common to
EXAMPLE G -6 When we decompose a large project into different programs
data types and struc-
create a library hie that contains all of the application
tures . Each program in the project then
includes the library file, ensuring
descriptions.
that everyone has exactly the same data names and
, when multiple files are compiled
This design creates a problem , however , which results in
included for the data
together. Each file has the same library
solution is to enclose the data
compile errors with the duplicate names. The the next example.
library file in a defined command as shown
in
_
# ifndef PROG LIB
__
—
# define PROG LIB
# include "proj lib.h
"
# endif
named pROG_LIBhas y
cuted only if
case , we
„ and
a constant
tQ shovv the
^ usp version of
has been
inclusions, always
use the
include command is used. " suffix, as a defined name.
uppercase
# ifndef STDIO
# define STDIO
Rest of the code
# endif
30 > // for
31 return 0;
32 } // main
production pro-
Program G- 2 Analysis Study this proqram carefully; you will find it useful when you write two of the tech-
6-8 This uses
grams. First, study the macro definition in lines
. macro
chapte r We begin by display ing the source file
niques we discussed earlier in the .
. Becaus e we have
line number so that we know where we are within the program each Then we use the
unique ly identify one .
multiple debugging displays, we need to the variabl e identifi er and its
ion operator ( see Section G.2) to display
string convers
contents.
In the body of the program we display
the values of the variables x and i. In the
more displays to track the execution
of
debugging of a large program, we would use
the program.
#if expression!
Code to be included if
"expression!” is true.
#elif expression2
Code to be included if
"expression2" is true.
#else
Code to be included if
both expressions are false.
#endif
m G- 3 Analysis First, we create a unique header file for each installation. Then, using conditional
commands, we select which include file to use based on defined flags for each site. In
this example, we have selected San Jose, so only its code is included in the program.
Command Meaning
#if expression When expression is true, the code that follows is included
for compilation.
# endif Terminates the conditional command.
# else Specifies alternate code in two-way decision.
# elif Specifies alternate code in multi-way decision.
# ifdef name Abbreviation for #if defined name.
# ifndef name Abbreviation for #if /defined name.
Line Command
in two formats, can set the
line number
The line command , which is used
and the filename lor a program. For
example ,
.... . .IZ*--;* ^
# line 100
“the program
nc >
The second
of ,K r
. For example,
«
»
«
c
“d — 3 i
..
sW he next line number
» 100 »d
be checked by the predebned
macro
_ “—“ for the program that can
the concept .
Program G - 4 demonstrates
1088 Section G.4 Other Commands
Results:
line 106
file myprogram.c
line 108
Error Command
I he error command is of the form
terror message
Results :
Error : preprocessor terror command
- .
ApG 04 c l i n e 8 terror You need to defined FALSE too .
Pragma Command
The pragma command
tpragma tokens
t
and does not generate a compilation error.
is considered a null command
Command-Line Arguments
In all the programs we have written , main has been
coded with no parame-
ters. But main is a function, and as a function, it may have parameters.
When
main has parameters, they are known as command - line
arguments.
Command - line arguments are parameters to main when the program
starts. I hey allow the user to specify additional information when the
pro-
gram is invoked. For instance, if you write a program to append two files,
rather than specif ) the names of the files as constants in the code, the user
could supply them when the program starts. Thus, a UNIX user in the Korn
Shell might execute the program with the command line shown below.
1091
1092 Section H.l Defining Command-Line Arguments
// Statements // Statements
} // main } // main
Without command - line arguments With command-line arguments
program
name
\0
& \0
—
\0
6 —
\0
*
argc
hd
- * \0
& \0
ml
argv
C:>cmdline
:1
The number of arguments
program: CMDLINE
The name of the
command .
' add hello to the run
For the second run , let s
1094 Section H.2 Using Command-Line Arguments
C:>cmdline hello
The number of arguments: 2
The name of the program: CMDLINE
User Value No. 1: hello
To make the exercise more interesting, let’s run the program with a
phrase on the command line.
But what if our intent is to read a phrase? C looks at the user values as
strings. This means that the spaces between the words separate each word
into a different element. If we want an element to contain more than one
.
word, we enclose it in quotes just like a string in code
to void concept . It
demonstrate the pointer
EXAMPLE 1- 1 Let us write a simple program to , a floating- point number, and a void
contains three variables: an integer the pointer can he set
to the
in the program - shows
pointer. At times
different in 1 2
floating point value. Figure
-
of the integer value or ol the
address
the situation .
Program 1 - 1 uses a pointer
to ind that
.
we can use to pr int either
an inte -
void* Pr -
K
int i;
float f;
P
P = &i;
P = &f;
*
P
Results:
i contains 7
f contains 23.500000
Program 1- 1 Analysis The program is trivial, but it demonstrates the point. The pointer p is declared as a
void pointer, but it can accept the address of an integer or floating-point number.
However, we must remember a very important point about pointers to void : a pointer
ApgendjxLPointers to Void and to functions 1097
main
code for:
int main (void )
fun — code for:
void fun (void)
pun — code for:
f2 int pun (int, int)
sun — code for:
f3 double sun (float)
pointer to Memory
function variables
f 1 is a pointer to a
function with no parameters
s. that returns void
^
// Local Declarations
void (*fl ) (void );
int ( f 2 ) ( int, int );
double ( *f 3 ) (float );
// Statements
fl = fun;
f2 pun;
f3 sun ;
The solution is to write simple compare functions for each program that
.
uses our generic function Then, when we call the generic compare function,
we use a pointer to function to pass it the specific compare function that it
must use .
EXAMPLE I- 2 We place our generic function, which we call larger , in a header file so
that it can be easily used. The program interfaces and pointers are shown in
Figure I- 5 . The code is shown in Program 1- 2.
main larger
X X
i 7 j 8 lrg 8 ] |
1
TI | dataPtrl
I
—
i
l
compare
i
i
t
- 1
dataPtr2
I
ptrl
I
ptrl
a
compare
21 return 0;
22 } // main continued
ion 1.2 Pointer to Function
Results:
Larger value is: 8
EXAMPLE 1 - 3 Now, let ’s write a program that compares two real ( float ) numbers. We can use
our larger function, but we need to write a new compare function. We repeat
Program 1 - 3, changing only the compare function and the data-specific state -
ments in main . The result is shown in Program 1- 4.
Results:
Larger value is: 81.7
Appendix J
Storage Classes and
Type Qualifiers
of data types, storage classes and
In this appendix, we discuss two attributes
type qualifiers. Storage classes
specify the scope of objects. Type qualifiers
.
specify processing limitations on objects
Object
Attrilbutes
Linkage
Extent
Scope
— automatic — internal
U- block — external
— file
I — static
FIGURE J- l Object Storage
Attributes
1103
1104 Section J.l Storage Closses
Scope
Scope defines the visibility of an object; it defines where an object can be refer-
enced . In C , an object can have four levels of scope: block, file, function , and
function - prototype. In this text , we do not discuss the function and function
prototype scopes.
Extent
The extent of an object defines the duration for which the computer allocates
memory lor it . In C , an object can have automatic , static extent , or dynamic
extent . The extent ol an object is also known as its storage duration .
Automatic Fxtent
An object with an automatic extent is created each time its declaration is
encountered and is destroyed each time its block is exited . For example, a
variable declared in the body ol a loop is created and destroyed in each itera -
tion . Note however, that declarations in a function are not destroyed until the
function is complete; when the function calls a function , they are out ol
scope but not destroyed .
Static Fxtent
A variable with a static extent is created when the program is loaded for exe -
.
cution and is destroyed when the execution stops This is true no matter bow
many times the declaration is encountered during the execution .
Appendix J Storage Classes ondjype Qualifiers 1105
Dynamic Extent
Dynamic extent is created by the program through the malloc and its related
library functions. We do not discuss static memory in this text.
Linka ge
A large application program may be broken into several modules, with each
module potentially written by a different programmer. Each module iis a sepa-
rate source file with its own objects. Different modules may be related when
the program is link edited .
We can define two types of linkage: internal and external. As we discuss
later, the linkage characteristic differs from scope characteristic, although
they look the same at the first glance.
Internal Linkage
An object with an internal linkage is declared and \isible only in one module.
Other modules cannot refer to this object.
External Linkage
An object with an external linkage is declared in one module but is visible
in
declare with a special keyword , extern , as we discuss
all other modules that it
shortly.
Storage Class
Specifiers
extern
register static
auto
Has two uses.
Auto Variables
A variable with an auto specification has the following storage characteristic :
In other words, the variable must be declared in a block. Each time the
declaration is encountered , it is re-created. It is visible only in the source file
in which it is declared . Most of the variables we have declared so lar are auto
storage class. T his type of specifier is so prevalent that C allows us to omit the
specifier.
Initialization
An auto variable can be initialized where it is defined or left uninitialized . II
initialized , it receives the same initialization value each time it is created . II it
is not initialized , its value will he undefined every time it is horn . When the
auto variable is not initialized , its value is unpredictable. Program J - l shows
the use ol the auto variables.
Result:
Value of x in iteration 1 is: 2
Value of x in iteration 2 is: 2
Value of x in iteration 3 is: 2
.
The keyword auto does not have to be explicitly coded Each time a variable of
block scope with no specifier is encountered, it defaults to auto .
Register Variables
A register storage class is the same as the auto class with only one difference.
I he declaration includes a recommendation to the compiler to use a central
processing unit ( CPU ) register for the variable instead of a memory loca -
tion . Ibis is done for efficiency. The time required to access a CPU register is
significantly less than the time required to access a memory location. The
compiler, however, may ignore the recommendation. Registers are scarce
resources, and the compiler often needs all of them for the program to run
efficiently.
vari-
There is one restriction on the use of a register variable; a register
- we can ’t use the
able address is Lnot available to the user. This means that
address operator and the indirection operator with a register. It also disallows
register with mixed
implicit conversions, such as might occur when using a
mode arithmetic.
in other modules. Note that the specifier static must he explicitly coded in
this case; it cannot he omitted .
Initialization
Astatic variable can be initialized where it is defined , or it can be left unini -
tialized . If initialized , it is initialized only once. If it is not initialized , its value
will be initialized to zero. Note however, that it is initialized only once in the
execution of the program .
In a static variable with block scope, static defines the extent of the variable.
Result:
Value of x in iteration 1 is: 2
Value of x in iteration 2 is: 3
Value of x in iteration 3 is: 4
Scope: file
Appendix J Storage Gosses and Type Qualifiers 1109
Extent: static
have the follow
Linkage: internal
1
Program J -3 shows the use ol the static variable with file scope.
Result:
1110 Section J.l Storage Glosses
The declaration of a static variable with file scope must be in the global
area of the file ( outside any function). If there is another declaration with the
same identifier in the global area, we get an error message. On the other
hand, if the identifier is used with another declaration inside a function, the
new variable is an auto variable or static variable with block scope. In other
words, in this case, we are defining a new variable that overrides the global
variable making it not in the current scope.
External Variables
External variables are used with separate compilations. It is common, on
large projects, to decompose the project into many source files. The decom-
posed source files are compiled separately and linked together to form
one unit .
A variable declared with a storage class of extern has a file scope; the
extent is static, but the linkage is external.
An extern variable must be declared in all source files that reference it,
but it can be defined only in one of them. External variables are generally
declared at the global level. The keyword extern is optional.
I bis design can create a problem: if three source files use the extern
specifier for a variable, which one is the defining file and which are simply
declaring the variable ? The rules differ depending on whether the variable is
initialized or not .
.
1 II it is not initialized, then the first declaration seen by the linkage editor
is considered the definition and all other declarations reference it .
No ion
‘‘extern” if extern ;e
% K
/ /,/^exterr?
#
int a ;
#i
ired
extern int a ;
Ik
extern int a;
int main ( void ) int funl ( void) int fun2 ( void)
{ { {
The keyword extern must be explicitly used in source files that use an external
.
variable It is omitted in the source file that defines and initializes the variable.
Summary
We have summarized what we discussed about storage classes in Table J - 1 -
Type
Qualifiers
When a storage class, such as static, and a type qualifier are both needed,
the storage class comes before the type qualifier.
Constants
The keyword for the constant type qualifier is const . A constant object is a
read-only object; that is, it can only be used as an rvalue. A constant object
must be initialized when it is declared because it cannot be changed later. A
simple constant is shown below.
PI = 3.142 ; // Invalid
In addition to the simple types, arrays, structures, and unions can also be
defined as constant . In these cases, all their component elements are con-
stants. For example, in the string constant shown below, none of the individ -
ual elements can be changed.
int a;
i n t b;
i n t * const p t r = &a;
ptr = & b;
/ / Error : p t r i s constant
i n t a = 5;
i n t b = 3;
const i n t * const p = &a;
*p = 5; / / Invalid : object of p i s a
constant
P * & b; / / Invalid : p i s a constant
Volatile
I he volatile qualifier tells the computer that an object value may be changed
by entities other than this program. Normally, objects used in a program
belong only to the C compiler. When the compiler owns an object, it can
store and handle it in any way necessary to optimize the program . As an
example, a C compiler may think that it is more efficient to remove an object
from HAM memory and put it in a register.
and
However, sometimes objects must be shared between your program
the C program , for example , some input /output
some other facilities outside
routines. To tell the compiler that the object is shared
, we declare it as type
that this object may be refer-
volatile. In this case, we are telling the compiler
enced or changed by other entities.
to an integer can be
The following shows how an integer or a pointer
declared volatile.
volatile int x;
volatile int* p t r;
1114 Section J .2 Type Qualifiers
Restricted
The restrict qualifier, which is used only with pointers, indicates that the
pointer is only the initial way to access the dereferenced data. It provides
more help to the compiler for optimization . Let us look at the following pro -
gram segment:
int* ptr ;
int a = 0;
ptr = & a ;
* ptr += 4 ;
* ptr += 5 ;
Here, the compiler cannot replace two statements * ptr + = 4 and * ptr
+= 5 by one statement * ptr + = 9 because it does not know il the variable a
can be accessed directly or through other pointers.
Now, let us look at the same program fragment using the restrict qualifier:
* ptr += 4;
* ptr += 5 ;
I lere, the compiler can replace the two statements by one statement ptr
1
The memory copy function requires that the source and destination area
not overlap ; the memory move function does not . The restrict qualifier in the
first function guarantees that there is no overlap between the source and des-
tination area . The second function does not ; the user must be sure that there
is no overlap.
Appendix K
Program Development
In C hapter 2 , we briefly discussed how a source
file is changed to an execut-
able hie. In Appendix G we discussed the role of the
preprocessor in this pro-
cess. In this appendix, we show, with more details, how
a program , simple or
complex, is developed from one single source file, a source file and several
user header flies, or several source files, into one single executable program.
In the first section , we discuss the process of development , which is i com -
mon to all platforms. In the second section , we discuss how the process is
coded in some common platforms.
K. l Process
hirst , let us discuss the process, which creates a single executable program
from one or more source files. Although we use four different exhaustive
cases, the source program that we have chosen is a simple one. It is a simple
program that calculates
Y = 2X
1115
1116 Section K.l Process
<
>
main
// main
( void ’
Compiler Linker
K scant
printf
We have only one source file that contains only one function, main. The
source file is first compiled to create an object file, the binary encoding ol
the main function. The linker adds two library functions, scanf and printf , to
the user ’s object file to create an executable file. The compiler instructs the
linker to do so when it encounters the function calls for these two functions.
I he scan ) and printj functions have already been compiled and stored as
.
object library modules in the standard library stdio h . No compilation is
needed lor these two functions. Note that the linker can only link ( add
together) object files. The executable file contains the binary code lor these
three functions.
Program K - l shows this situation. We have written only one main func -
tion to calculate the power of two of any unsigned integer.
Results:
Enter the power: 14
16384
5Fh
include <stdio.h>
^
int main (void]
{
main
} // main power2
main
unsigned power2 (...)
{
} // power2
Compiler
K power2
Linker scanf
printf
We still have one user’s object file and two library module files
, but the
binary code for two functions: main and
user ’s object file now contains
Hie con-
power2. The linker still links only three object files. The executable
tains the code for four functions.
with two functions.
Program K - 2 contains the modified source program
Results:
Enter the power: 14
16384
\
main
} // main
power 2
unsigned power 2 (... J
{
] / / power 2
Compiler
h
main
power 2
Linker
H scanf
printf
2
PROGRAM K - 4 Source File for power power 2 function .
the
1 /* This is the
source file for
2 Written by:
3 Date:
4 */ unsigned int exp )
5 unsigned int power2 (
6 {
7 // statements
exp ) ;
8 return ( 1 «
9 > // power2
1120 Section K.l Process
Source File
#include <stdio.h>
#include "power2.h"
int main ( void! Compiler main
Library Object Files
scanf
b
printf
.
<
} // main zr main
<
} // power2
Compiler
j-^lpowerfj Linker |-^ scanf
printf
Although the resulting executable file has the same four binary-coded
modules, the process is different. Each source file is compiled separately and
two independent object files are made from them. The object files are only
related when they are linked together. Once again, because the code is the
same the output is identical.
Program K- 5 contains the source file for main .
2 Written by:
compilations power2 -
3 Date:
4 */
5 unsigned int power2 (unsigned
int exp)
6 {
7 // Statements
8 return ( 1 « exp);
9 > // power2
K.2 Coding
Now that we have discussed the process, let 's look at how we code the differ-
ent steps in the process.
UNIX
UNIX, and its derivative Linux, are popular operating systems. They contain
several tools with many options. In this appendix, we briefly describe the
basic options and explain how to compile and link UNIX programs.
File Types
UNIX uses different extensions to distinguish between different file types
source files, object files, and executable files, as shown in Figure K- 5.
.
In UNIX, a source file in Standard C has the extension c, an object file has the
extension o, and an executable file has no extension.
.
Note that the U N I X prompt depends on the shell being running. It can
he $ or % or any other customized prompt. The command c 99 invokes the C
compiler with the C99 standard implementation ( other versions are usually
available lor backward compatibility). The option can he either -c for compil-
ing only, or - o lor either linking or compiling and linking. The - o option indi -
cates that we need an executable module. The intent of the -o option is
conveyed through the parameters:
1 . II we want to compile only, we define only source files; the executable file
name is not used.
.
2 II we want to link only, we define only the object files; the source files are
not used.
Examples
Let ’s look at some examples.
EXAMPLE K - l Compile, Link, and Execute: We need to compile and link to create an executable file
Irom a source file named myprogram. c. The following code shows the com -
mands. In the first line, we create an executable file named myprogram; in
the second line we run that program. After these two lines of commands
have been executed, we have two files in our directory: myprogram. c and
myprogram.
Appendix K Program Development ] ] 23
$ c 99 o myprogram myprogram
$ myprogram
c .
$ c 99 -c first . c second . c
EXAMPLE K - 3 Create Fxecutoble File: We need only to create an executable file from the first .o
file ( linking all library' files to it ). After the linker executes , we have a execut-
able new file in our directory, first .
$ c 99 -o first first o .
$ c 99 -c myprogram . c
$ c 99 -c power 2 .c
$ c 99 -o mybigprogram myprogram . o power 2 . o
$ mybigprogram
$ c 99 temporary . c
.
$ a out
1124 Section K.2 Coding
Note that because the name of the file is system defined , it remains in
the system until we created another executable file, which the system needs
to call a . out . This method is used only for temporary testing and is not rec-
ommended when we need to keep the executable file.
Other Platforms
In general, every hardware platform comes with its own application develop-
ment environment . In addition , there are numerous compilers and develop -
ment support tools available. To find the documentation or your local
(
support environment , ask your support staff or check with the help system
installed with your compiler.
Appendix L
Understanding Complex
Dedarations
I he declarations have become increasingly more complicated throughout
this text. Sometimes the declarations are difficult to interpret, even for some-
one experienced in the C language.
To help you read and understand complicated declarations, we provide a
rule that we call the right -left rule. Using this rule to interpret a declaration,
you start with the identifier in the center of a declaration and read the decla-
ration by alternatively going right and then left until you have read all enti-
ties. Figure L- 1 is a representation of the basic concept.
[ identifier ] 0
t t t
6 4 2
t
start here
t t t
1 3 5
We will begin with some simple examples and proceed to the more
complicated .
1 . Consider the simple declaration
int x
Section
int x #
T T T
2 o 1
int P # #
T T T T T
4 2 0 1 3
Note that we keep going right even when there is nothing there until all
the entities on the left have been exhausted.
.3. In the next example, we have an equal number ol entities on the right
and the left.
int table f 41 T 5 1
T T T
2 0 1
I. The pound sign ( # ) is just a placeholder to show that there is no entity to he considered. It is
ignored when read.
Appendix L Understanding Complex Declarations 112 /
ptrToAry
aryOfPtrs
int ( *ptrToAry # ) [5 ]
T T T t T
4 2 0 1 3
to an array of 5 integers.
This declaration is read "ptrToAry is i a pointer
. Here , we see a simple
7 . This example deals with function declarations
an integer .
prototype for a function that returns
returning an integer .’
“ is ai function
This declaration is read as dolt .
returning a pointer to an integer
8 . The final example shows a Lfunction
array
^of
^ e
ala l
pointers
'
atomic data type : a set of atomic data having iden - bitwise operator: any of the set of operators that
tical properties that distinguish one type Irom operate on individual hits.
another. Contrast with derived type . blackbox testing: testing based on the system
( into : the default storage class for a local variable . requirements rather than a knowledge ol the
auxiliary storage : any storage device outside main program . Contrast with whitebox testing .
memory; permanent data storage ; external storage. block: in C , a group of statements enclosed in
braces {...}.
B block comment: a comment that can span one or
more lines. Contrast with line comment .
base case: in recursion , the event that does not block scope: see scope .
involve a recursive call and therefore ends the recur-
sion . body ol loop: the code executed during each itera -
’
tion of a loop.
batch update: an update process in which transac -
tions are gathered over time for processing as a unit . Boolean: a variable or expression that can assume
Contrast with online update. only the values true or false .
bi - directional communication : communication braces: the { and > symbols.
between a calling and called function in which data breadth-first traversal : a graph traversal in which
flows in both direction , that is , from the calling func - nodes adjacent to the current node (siblings) are pro-
tion to the called function and then from the called cessed before their descendents. Contrast with
function to the calling function . depth - first traversed .
big- O analysis: the analysis of an algorithm that break: a C statement that causes a switch or loop
measures efficiency using big- O notation rather than
statement to terminate .
mathematical precision .
bubble sort : a sort algorithm in which each pass
big- O notation: a measure of the efficiency ol an
algorithm in which only the dominant factor is through the data moves ( bubbles ) the lowest element
to the beginning of the unsorted portion of the list .
considered .
binary: a numbering system with base 2. Its digits buffer: ( 1 ) hardware , usually memory, used to syn -
are 0 1 .
chronize the transfer of data to and from main mem -
ory. ( 2 ) memory used to hold data that have been
binary expression : any expression containing one read before they are processed , or data that are wait -
operator and two operands. ing to he written .
binary file : a collection of data stored in the inter- buffered input /output : input or output that are
nal format ol the computer. Contrast with text file .
temporarily stored in intermediate memory while
binary search : a search algorithm in which the being read or written.
search value is located by repeatedly dividing the list
in half .
buffered stream: C term for buffered input/output .
binary search tree : A binary tree in which : ( 1 ) the bug: a colloquial term used for any error in a piece
keys in the left subtree are all less than the root ol software.
key. ( 2 ) the keys in the right subtree are greater than byte: a unit of storage , 8 bits.
or equal to the root key, and ( 3 ) the subtrees are all
binary search trees.
C
binary stream: a stream in which data is repre -
sented as it is stored in the memory of a computer. C standard library: any of the libraries that con -
binary tree : a tree in which no node can have more tain predefined algorithms delivered with the system,
than two children . such as standard library, ( stdlib . h ) and standard
bit : acronym for Binary digIT. The basic storage unit input and output ( stdio . h ) .
in a computer with the capability of storing only the called function : in a function call , the function to
values 0 or 1 . which control is passed .
Glossory ] ) 31
calling function: in a function call , the
that invokes the call. function cohesion: the attribute of a module
how closely the processes within a that describes
cast: a C operator that changes the type of related to each other. module are
an
expression .
coincidental cohesion: the cohesion level in
ceiling: the smallest integral value that is larger which totally unrelated processes
than a floating- point value. a module. are combined into
central processing unit (CPU ): the part of column: in a two-dimensional table, the
computer that contains the control components
a
that is, the part that interprets instructions. In a
sonal computer, a microchip containing a controlper-
— representing vertical columns in the array.
withrow.
dimension
Contrast
comma expression: the last ( lowest precedence)
unit and an arithmetic-logical unit .
expression in C; used to combine multiple
expres-
central transform: the modules ol a program sions into one expression statement.
that
take input and convert it to output . See also afferent comma operator: in C, the operator that connects
module and efferent module. multiple statements in one expression.
changeability: the quality factor that addresses the command-line argument: in C , a parameter spec-
ease vvith which changes can be accurately Red on the run - time execute statement and passed
made to a
program. ‘
to the program for its use.
character: a member of the set of values that are comment: in a C program , a note to the program
used to represent data or control operations. reader that is ignored by the compiler.
See Unicode .
coniniunicational cohesion: the processes in a
character constant: a constant that defines one module are related only in that they share the
character enclosed in two single quotes same data .
character set: the set of values in a computer s ’ compilation: the process used to convert a source
alphabet . See Unicode . program into machine language.
child: a node in a tree or a graph that has a compiler: system software that converts a source
predecessor. program into executable object code; traditionally
associated with high-level languages. See also assembler.
chronological list : a list that is organized by time;
that is, in which the data are stored in the order in complement: in a set of values with two symmetric
which they were received . See also FIFO and LIFO. sections, the operation that gives the counterpart of
the current value, that is, the value in the other sec-
classifying function: a standard C character tion . For example, the complement of a number is a
function that classifies a character according to U s number with the same value and the sign reversed.
character taxonomy, such as printable or
“alphanumeric ” I he complement of a binary digit is the other binary
. digit. The complement of a logical value ( true or false )
't lear box
testing: see u hitebox testing . is the other value.
client: in a client/server network, the computer that com plex type: a data type consisting ol a real and-
provides the basic application computing; the com - an imaginary type and representing a complex num
puter residing in the user’s physical area. ber in mathematics.
client/server: a computer system design in which composite data: data that are built on other data
down into
two separate computers control the processing o t e structures; that is, data that can be broken
application , one providing the basic application com discrete atomic elements .
puting (the client) and the other providing auxi ,ar compound statement: a sequence of statements
services, such as database access ( the server .
> enclosed in braces. See also block .
compo-
close function: the function that concludes the computer hardware: the set of computer
consisting of at least an input device , an out -
writing of a file by writing any pending data to u nents,
and then making it unavailable lor processing. put device, a monitor, and a central
processing unit .
1132 Glossary
computer language: any ot the syntactical lan - coupling: a measure of the interdependence
guages used to write programs for computers , such between two separate functions. See also: content
as machine language , assembly language , C , cou fling , control coupling , data coupling , global
COBOL, and FORTRAN . coupling , and stamp coupling .
computer software : the set of programs, consist - CPU: see central processing unit.
ing of system and application programs, that provide
instructions for the computer. D
computer system: the set of computer hardware
and software that make it possible to use a computer. dangling else: a code sequence in which there is
no else statement for one of the if statements in a
conditional expression: in C , shorthand lorm of a
two-way selection .
nested if
connected graph : a graph is connected if , when data : the part of a structure that stores application
direction is suppressed , there is a path from any ver- information .
tex to any other vertex. data coupling: communication between modules
in which only the required data are passed . Consid -
const : a storage class that designates that a fields
contents cannot he changed during the execution of
ered the best form of coupling.
the program . data encapsulation: the software engineering con -
constant: a data value that cannot change during cept in which data are hidden from an application
the execution of the program . Contrast with variable . program’s access.
content coupling: the direct reference to the data data flow': in a structure chart , an indicator identi -
in one module by statements in another module. 1 he fying data input to or output from a module.
lowest lorm of coupling and one to be avoided . data hiding: the principle ol structured program -
continue : in C , a statement that causes the remain - ming in which data are available to a
function only il
it needs them to complete its processing; data not
ing code in a loop iteration to be skipped .
needed are hidden ’ from view. See also encapsida -
"
depth -first traversal: a traversal in which all ol a during the execution of the program (The heap ).
node’s descendents are processed before any adjacent
nodes (siblings). Contrast with breadth -first traversal . E
dequeue: delete an element from a queue.
dereference: access of a data variable through a edit set: a list of characters used to validate charac -
pointer containing its address. ters during the formatted reading of a string.
dereference operator: in C , the asterisk ( * ) . Used efferent module: a module whose processing is
to indicate that a pointer’s contents ( an address) are directed away from the central transform ; that is, a
to be used to access another piece ol data . module that predominately disposes of data by report-
derived type: a composite data type constructed ing or writing to a file.
from other types ( array, structure, union , pointer, and efficiency: the quality factor that addresses the
enumerated type). Contrast with atomic type . optimum use of computer hardware or responsive-
descendant: any node in a path from the current ness to a user.
node to a leaf . edge: a line in an undirected graph. Contrast
descending sequence: a list order in which each with arc.
element in a list has a key less than or equal to its
predecessor. else - if: a style (as opposed to syntax) convention nonin -
used to implement a multiway selection fora
directed graph : a graph in which direction is indi - tegral expression. Each expression in the series must
cated on the lines (arcs ). evaluate the same variable .
disk: an auxiliary storage medium used to store data but that
and programs required for a computer. empty list: .a. list that has been allocated list
contains no data . Also known as a null .
enumeration constant: the identifier associated extern : the storage class that specifies that an object
with a value in an enumeration declaration . has static extent and that its name is exportable to
enqueue: insert an element into a queue. the linker.
EOF: end of file. In C , a flag set to indicate that a
file is at the end . F
error report file : in a file update process , a report factorial efficiency: a measure of the efficiency of
of errors detected during the update. a module in which the run time is proportionate to
error state : one of three states that an open file the number of elements factorial .
may assume . An error state occurs when a program false: the Boolean value used to indicate that an
issues an impossible command , such as a read com -
expression is not true.
mand while the file is in a write state , or when a
physical device failure occurs. See also read state , fan out: an attribute of a module that describes the
write state . number of submodules it calls.
error stream : in C , the standard file used to display field : the smallest named unit of data that has
errors, often assigned to a printer ( stderr ). meaning in describing information . A field may be
escape character: in C , the backslash ( \ ) charac -
either a variable or a constant .
ter used to identify a special interpretation lor the field specification: a subcomponent of the format
character that follows it . string used to describe the formatting of data , in the
escape sequence : any group ol keystrokes or char- formatted input and output functions.
acters used to identify a control character. In C , the field width: in a field specification , the specifica -
backslash ( \ ) used to indicate that the character that tion of the maximum input width or minimum out -
follows represents a control character. put width lor formatted data .
event-controlled loop: a loop whose termination FIFO: first in - first out .
is predicated upon the occurrence ol a specified file : a named collection ol data stored on an auxil -
event . Contrast with counter-controlled loop . iary storage device . Compare with list .
exchanging values: the logic used to exchange the file mode : a designation of a file’s input and /or out -
values in two variables. put capability; files may be opened for reading, writ -
exclusive or : a binary' logical operation in which the ing, appending, or updating.
result is true only when one of the operands is true file state : the operating condition of a file: read
and the other is false . state , write , or error state .
executable file : a file that contains program code in file table: in C , the predefined standard structure ,
its executable form ; the result ol linking the source
FILE used to store the attributes of a file.
code object module with any required library modules.
,
explicit type conversion: the conversion of a filename: the operating system name ol a file on an
value from one type to another through the cast oper- auxiliary' storage device.
ator. Contrast w ith implicit type conversion . first in - first out: a data structure processing
exponential efficiency: a category of program / sequence in which data are processed in the order
module efficiency in which the run time is a function that they are received ; a queue .
of the power of the number of elements being fixed - length array: an array in which the num -
processed . ber of elements are predetermined during the
expression: a sequence of operators and operands declaration /definition .
that reduces to a single value. fixed- length string: a string whose size is constant
expression statement: an expression terminated regardless of the number of characters stored in it .
by a semicolon . Hag: an indicator used in a program to designate the
expression type: one of six expression attributes presence or absence of a condition; switch .
that broadly describes an expression s format: flag modifier: the input/output format specification
primary, postfix, prefix , unar)> binary, and ternary.
} that suppresses input or modifies output formatting.
Glossary 1135
flexibility: the quality factor that addresses the ease
with which a program can he changed to meet user function declaration: in C, statement
describes a functions return type that
requirements. , name, and formal
parameters.
flipping hits: changing a bit from a 0 to 1 or a 1 to 0.
function definition: in C, the implementation
-
floating point number: a number that contains
both an integral and a fraction .
function declaration. of a
function header: in a function definition , that
floor: the largest integral number smaller tha n a of the function that supplies the return part
floating- point value. type, func-
tion identifier, and formal parameters
with Junction body.
. Contrast
flowchart: a program design tool in which standard
graphical symbols are used to represent the logical functional cohesion: a module in which all of the
flow of data through a function . processing is related to a single task. The highest
for loop: a counter-controlled , pretest loop in C. level of cohesion.
force to change: the bit manipuation concept used
to force selected hits to change from 0 to I or from
1 G
to 0.
force to one: the bit manipulation concept used to general case: all the code in a recursive function
force selected bits to 1 . that is not a base case.
force to zero: the bit manipulation concept used to general - purpose software: software, such as a
force selected hits to zero. spreadsheet, that can be used in multiple
applications.
formal parameter: the parameter declaration m a
function prototype used to describe the type ol data global coupling: communication between differ-
to be processed by the function . Contrast with ent modules that uses data accessible to all modules
actual parameter. in a program. Considered to be a very poor communi-
cations technique for intraprogram communication .
formal parameter list: the declaration of |wra mo-
ters in a function header. global declaration section: the declaration and/or
definition of a variable or function outside the
fornnt C n r . s r n8: ln a formatted input /output
fun t i ' ii . °| ° *
i u siting ’that is used for formatting data.
—
boundaries of any function that is, before main or
between function definitions. Contrast with local
formatted input/output: in C , any of the standard declaration .
library functions that can reformat data to and from global variable: a variable defined in the global
ext bile they are being read or written .
"
frequency array: an array that contains the num - outside a function block.
—
declaration section of a program that is , defined
.
—
Q) ntrast with soft copy
function body: the part ol a 1 unction that contains , j are; any ol the physical coml
the local definitions and statements; all of a funct on
hajrdwar
StlSSTw. -
^ printer,
he keyboard or a Pr
(
except the header declaration. Contrast with
, ration: that part of a function ia
(
ider file : in C , a file consisting ot prototype infinite loop: a loop that does not terminate.
ements and other declarations and placed in a information hiding: a structured programming
ary' for shared use . concept in which the data structure and the imple-
id pointer: a pointer that identifies the first cle- mentation of its operations are not known hy the user.
at of a list .
initialization: the process of assigning values to a
ip: see heap memory. variable at the beginning of a program or a function .
ip memory: a pool ot memory that can he used
lynamically allocate space for data while the pro- initializer: in C , any value assigned to a constant or
111 is running . variable when it is defined.
tadecimal: a numbering svstem with base 16. Its inorder traversal : a binary tree traversal in which
its are 0 1 2 3 4 5 6 7 8 9 A B C D E F. the root is processed after the left subtree and before
;h - level language : a ( portable ) programming the right subtree. Contrast with preorder and
guage designed to allow the programmer to con - postorder traversal .
ltrate on the application rather than the structure input device: a device that provides data to he read
1 particular computer or operating system . by a program .
itogram : 1 graphical representation of a fre -
<
input stream : in C , the How of data from a hie to
ency distribution . See also frequency array. the program.
inquiry: a request lor information from a program .
I
inquiry all : an algorithm that determines if all data
jntifier: the name of an object . In C , identifiers items in a list meet some criteria .
a consist only of digits , letters, and the underscore.
inquiry any: an algorithm that determines il any
.. else : a decision statement in C . data item in a list meet some criteria .
laginary type : the data type in C that can repre-
nt and imaginary number in mathematics . insertion sort : a sort algorithm in which the first
element from the unsorted portion of the list is
iplicit type conversion: the automatic conversion inserted into its proper position relative to the data in
data from one type to another when required within
the sorted portion of the list .
2 program. Contrast with explicit type conversion .
elude : in C , a preprocessor command that speci - integer: an integral number; a number without a
es a library file to be inserted into the program . fractional part .
degree : in a tree or graph , the number ol lines integral type : a data type that can store only whole
itering a node. numbers.
dentation: a coding style in which statements intelligent data name : a software engineering
•pendent on a previous statement , such as / / or
principle that requires the identifier to convey the
Wile, are coded in an indented block to show their meaning or use of an object .
lationship to the controlling statement .
idex: the address of an element within an array. interoperability: the quality factor that addresses the
IQ also subscript .
ability of one system to exchange data with another.
id ex range checking: a feature available in some iteration : a single execution of the statements in
impilers that inserts code to ensure that all index a loop .
ferences are within the array.
idexing: a process used to identify one element in
J
l array.
idirect pointer: a pointer that locates the address justification : an output formatting parameter that
data through one or more other pointers ; pointer to controls the placement of a value when it is shorter
ainter. than the specified output width .
Glossary 1137
limit test expression: in a loop, the expression athat logical data: data whose values
can be only true or
determines if the loop will continue or stop . Sec so
false. See Boolean.
ternii nati ng condit ion . ( & & , ||,!) used in
of the efficiency of a logical operator:: a C operator
linear efficiency: a measure the a logical expression .
module in which the run time is proportionate to to repeat one or more
number of elements being processed. loop: the construct used while , for , and do...
program . The
, statements in a
linear list: a list structure in which each element while statements in C.
except the last , has a unique successor.
1 Glossary
intable character: a character value that is asso- quadratic loop: a loop that consists of two or more
rted with a print graphic. loops , each of which have a linear efficiency, result -
ing in a loop with quadratic efficiency.
•
inter: an output device that displays the output
i paper. quality: see software quality.
rocedural cohesion: a module design in which query: see inquiry.
ie processing within the module is related by con - queue: a linear list in which data can he inserted
ol flows. Considered acceptable design only at the only at one end , called the rear, and deleted from the
gher levels of a program . .
other end called the front .
rocess-control loops : a continuous loop used to
rntrol operating equipment , such as a building air
Dnditioning/heating system . R
rocessing unit : see central processing unit . ragged array: a two-dimensional array in which the
rogram development : the system development number of elements in the rows is not equal .
ctivity in which requirement specifications are con - random number: a number selected from a set in
erted into executable programs. which all members have the same probability of
•rogram file : a file that contains an executable being selected .
• rogram . random number seed : the initial number passed
> rogram testing: the process that validates a pro- to a random-number generator.
gram ’s operation and verifies that it meets its design read mode: the attribute of a file that indicates that
equirements. it is opened for input only.
> rototype declaration : the function header that read state : one of three states that an open file may
Jeclares the return type, function name, and parame- assume. The state of a file during which only input
er number and types without defining the function operations may be performed. See also error state ,
tself . write state .
program documentation : comments placed real time : processing in which updating takes place
A ithin a program to help the reader understand the
at the time the event occurs .
purpose of the program or a portion ol its
implementation . real type: a data type representing a number with a
fraction.
promotion : an implicit type conversion in which
the rank of an expression is temporarily increased to rear : when used to refer to a list ; a pointer that
match other elements of the expression . identifies the last element .
prototype statement: in C , the declaration of a record : see structure .
function that provides the return type and formal recursion : a function design in which the function
parameter types. calls itself .
pseudocode: English - like statements that follow a register : the storage class that requests that , if pos -
loosely defined syntax and are used to convey the
design of an algorithm or function . sible, a variable be allocated to a CPU register.
push : the stack insert operation . reliability: the quality factor that addresses the
confidence or trust in a system s total operation.
reserved words: the set of words in C that have a
Q
predetermined interpretation and cannot be used in
the definition ol an object .
quadratic ef ficiency: a measure of the efficiency
of a module in which the run time is proportionate to return : the C statement that causes execution of a
the number of elements squared . Quadratic effi - function to terminate and control to be resumed by
ciency is one of the polyonymic factors. the calling function or the operating system .
Glossary 1141
1
reusability: the quality factor that addresses th e ease selected and placed at the end of the sorted portion
with which software can be used in other programs. of the list .
reusable code: code that can be used by more than selection statement: a statement that chooses
one process. between two or more alternatives. In C , the if ...else
rewind: the C function used to position the file or switch statements.
marker at the beginning of an open file . self - referential structure: a structure that con -
right-to-left associativity: the evaluation of an tains a pointer to itself .
expression that parses from the right to the left. Con - sentinel: a flag that guards the end of a list or a file.
-
trast with left -to righl associativity. The sentinel is usually the maximum value for a key
right justification : the orientation of variable length field and cannot he a valid data value.
data in an output format such that leading null values separate compilation: a capability within a pro-
are inserted and the last data character is at the right gramming language that allows parts of a program to
end of the print area . Contrast with left justification . be compiled independently and later assembled into
—
right left rule: a method of reading complex dec -
larations or definitions that starts with the identifier
an executable unit .
sequential cohesion: a module design in which
and alternately reads right and left until the declara - the processing within the module flows such that the
tion has been fullv read . data from one process are used in the next process.
row: in a two-dimensional array, elements along a sequential file: a file structure in which data must
horizontal axis . Contrast with column . be processed serially from the first entry in the file.
root: the first node of a tree; the node with no sequential search : a search technique used with a
predecessor. linear list in which the searching begins at the first
rvalue: an expression attribute indicating that the element and continues until the value of an element
expression can be used only to supply a value for equal to the value being sought is located , or until
another expression. the end of the list is reached .
serial search: any search technique that starts at the
beginning of the list and continues toward the end.
s server: in a client/server system , the centralized
scanset: the set of allowed or disallowed characters computer that provides auxiliary services.
when reading a string. short : the C type for short integer.
scope: an attribute of a variable that defines short integer: an integer format used for small
whether it is visible to or hidden from statements in a numbers, generally less than 32 ,768 .
program . See also temporal authority. side effect: a change in a variable that results from
search: the process that examines a list to locate the evaluation of an expression ; any input/output
one or more elements containing a designated value performed by a called function.
code
known as a search argument. sign flag: the flag value (+ ) in the conversion
of a print format string that indicates that .
positive
secondary storage: see aiexilian storage .
security: the quality factor that addresses the ease
numbers must be printed with a plus sign
a numeric
or difficulty with which an unauthorized user can signed: a type modifier indicating thative. Contrast with
value may be either positive or negative
access data .
unsigned
seed : the variable used in a random number genera conversion code of
tor to calculate the next number in the series . In , siz.e: an indicator ( h , 1, L ) in the
integer and float types.
the seed is set by the library function sranii . a format string that modifies
formatting token that modi -
seek file: in the processing of a disk file , the action Jze modifier: the data
that moves the index marker to a new position in the lies the conversion code.
memory locations added
file; in C , the fseek function , slnck bytes: inaccessible to force a hardware
between fields in a structure
selection sort: the sort algorithm in which the alignment .
requiired boundary
smallest value in the unsorted portion of a list > s
/
1142 Glossary
soft copy: computer output written to a nonperma - statement: a syntactical construct in C that repre -
nent display such as a monitor. Contrast with sents one operation in a function .
hard copy. statement section: the section in a program where
software: the application and system programs nec - the executable statements are written .
essary' for computer hardware to accomplish a task static : a storage class that designates ( a ) that a local
including their documentation and any required field must maintain its contents throughout the exe -
procedures. cution of a program ( contrast with auto ) , or ( b) that a
software quality: an attribute ol software that global variable’s name must not be exported to the
measures the user’s total satisfaction with a system . linker (contrast with extern ) .
sort : the process that orders a list or file. static memory: memory whose use (e.g., lor a vari -
sort pass : one sort loop during which all elements able ) does not change during the running ol a
are tested . program.
source file : the file that contains program state - static memory allocation: memory whose loca -
ments written hv a programmer before they are con - tion is determined by the compiler and therefore pre-
verted into machine language ; the input file to an set before run time . Contrast with dynamic mentor)'
assembler or compiler. allocation .
space flag: the flag value in the conversion code of stderr: see standard error stream .
a print format string indicating that positive numbers stdin: see standard input stream .
must be printed with a leading space.
stdout : see standard output stream .
-
sparse array: a two dimensional array in which the
stepwise refinement: a design methodology in
rows are only partially filled .
which a system or program is developed from the top
spatial authority: an attribute ol a field that deter- down ; starting with the most inclusive, each module
mines which functions within a program can refer to is decomposed and refined until the meaning of a
it or change its contents.
component is intrinsically understood .
square brackets: the [ and ] symbols.
storage class: an attribute of a field that deter-
stack: a restricted linear list in which data be mines its spatial and temporal usage . See also auto ,
inserted and deleted at only one end , called the top . extern , register , static .
stack memory: in C , the memory management stream : in C , the flow of data between a file and the
facility that is used to store local variables while they program ; a sequence of bytes in time.
are active .
string: in C , a variable- length sequence of charac -
stamp coupling: the communication technique ters delimited by a null character.
between modules in which data are passed as a
structure; often results in unrequired data being string constant: a constant comprised of a
passed . sequence characters enclosed in double quotes.
standard error stream: the text file automatically strongly connected graph: a graph in which eacf
opened by C lor display of error messages ( stderr ) . there is a path from every node to every other node ;
contrast with weakly connected graph .
standard input stream: the text file automatically
opened by C for input to a program ( stdin ) . structure: a named collection of fields grouped
standard library: any of a collection of libraries together lor processing; see record .
containing functions required by the C standard pro - structure chart: a design and documentation tool
vided by an implementation of the C language. that represents a program as a hierarchical flow ol
standard output stream: the text file automati - functions.
cally opened by C for output from a program ( stdout ) . structure variable : in C , a structure definition tha
standard type : one of the intrinsic C types that arc cannot be used as a type .
considered atomic ; that is , that cannot he broken stub: a temporary and incomplete function used tc
down ( void , char , ini , float ) . test function interfaces during program developmen
Glossary 1143
subscript: an ordinal number that indicates the
position of an element within an array. See also index. ternary expression: in C, an expression
tains three operands (the that con -
summation : an algorithm that adds a list of the only ternary expressionconditional
in C).
expression is
data items.
testability: an attribute of software that
suffix: a modifier to a numeric constant that indi - the ease with which the soltvvare can he measures
cates its type when used in an expression , operational system . tested as an
switch: see flag . testing: see program testing , blackbox testing , or
switch statement: the C implementation of the white box testing .
multiway selection . text editor: software used to create and
change text
symbolic language: a computer language, one lilcs, such as a word processor or a source program
level removed from machine language, that has a editor.
mnemonic identifier lor each machine instruction text file: a file in which all data are stored as
and has the capability of symbolic data names. characters. Contrast with binar)' file.
syntax: the grammatical rules of a language. In C, timeliness: an attribute of software that measures
the set of keywords and formatting rules that must be responsiveness of a system to a user’s time
followed when writing a program. requirements.
system development life cycle: a model of the time -sharing environment: an operating system
steps required to develop software that begins with concept in which more than one user has access to a
the need for the software and concludes with its computer at the same time.
implementation. token: in C, a syntactical construct that represents
system development software: any computer tool an operation or a flag, such as the assignment
used to develop software, such as but not limited to token ( = ).
compilers, debuggers, and documentation tools. top: in a stack , the next element to be removed .
system software: any software whose primary pur- top-down design: a program design concept in
pose is to support the operation of the computing which a design progresses through a decomposition
environment . Contrast w ith application software . of the f unctions beginning with the top of the struc-
system support software: software used for non - ture chart and working toward the lowest modules.
application processing, such as system utilities. See also stepwise refinement .
transaction file: a file containing relatively tran -
sient data that is used to change the contents of a
T master file .
tagged structure: a C structure in which the struc - transform analysis: an analytical process that cre-
ture is given an identifier that can he used to dec are ates a program design by classifying modules as
variables of the structure type. input , process, or output.
tape storage: an auxiliary storage medium that transform module: the function in a program that
stores data as a sequential file on a magnetic recorc takes input from the afferent modules and prepares it
ing surface . for processing by the efferent modules.
file
temporal authority: an attribute of a field that translation unit: in C, a temporary compilation
determines when a field is alive and active during tlle used to store modified source code.
language
execution of a program . translator: a generic term for any of theand compiler.
temporal cohesion: a module design in which pro- conversion programs. See also assembler
each ele-
t
cesses are combined because they all neet to c pro traversal : an algorithmic process in which
once and only once.
cessed in the same time sequence. ment in a structure is processed
that an
t. terminating condition: in a loop, the condition true: a boolean value used to indicate
that stops a loop. See also limit test expression. expression is true.
1144 Glossary
two-dimensional array : an array in which each ele - variable: a memory storage object whose value can
ment contains one array. See also multidimensional be changed during the execution ol a program . C on -
array. trast with constant .
-
two way selection: a selection statement that is variable- length array: an array in which the num -
ber of elements can be changed during the run time.
capable of evaluating only two alternatives. In C , the
if ...else statement . Contrast with multiway selection . Contrast with fixed - length array.
type: a set of values and a set of operations that can variable structure : a data structure in which two
be applied on these values. or more types of data may occupy the same positions
type qualifier: a modifier used in the definition ol within the structure . In C , a union .
an object. visibility: the temporal authority for a field that des -
ignates where in the program it can be accessed or
changed; see scope .
U
vertex: a node in a graph .
unary expression : an expression that consists of void : the absence ol data .
one operator and one operand .
void functions: functions whose return value
unarv minus: in C , the operator that complements
* is void .
the value of an expression .
void pointer: a generic pointer type that can hold
unbuffered input/output: input or output that is the address of any data type or structure.
transmitted directly to or from memory without the
use of a buffer. volatile : an attribute of a variable that indicates that
it may be accessed or changed by functions beyond
underflow: an event that occurs when an attempt is
made to delete data from a data structure and it the control of the compiler.
is empty.
Unicode : The internation character set used to W
define valid characters in computer science . Each
character is represented using a 32- bit unsigned waterfall model : a system development life cycle
in which each phase of development , such as
nu mber.
requirement development and design , is completed
union : C term for variable structure. before the next phase starts.
unsigned : a type modifier indicating that a numeric weakly connected graph : a graph in which there
value may be only positive. Contrast with signed . is at least one node with no path to any other node:
update: ( 1 ) in array processing, the process that contrast with strongly connected graph .
changes the contents of an element ; ( 2 ) in file pro- weighted graph: a graph whose lines are weighted .
cessing, a mode that allows a file to be both read and
written . while loop: a sentinel-controlled , pretest loop in C.
update mode : a file process mode that specifies whitebox testing: program testing in which the
that the file will be both read and written . internal design of the program is considered; clear
upward communication: data flow from the hox testing . Contrast with blackbox testing .
called function to the calling function . whitespace : in C , the space , vertical and horizontal
user-defined function : any function written by the tabs , newline, and form -feed characters.
programmer, as opposed to a standard library f unction . width modifier: in a format string, a conversion
user prompts: monitor messages to a user that code modifier that specifies the input maximum or
request the user input one or more values . output minimum size.
write mode: the attribute of a file indicating that it
is opened for output only.
V
w rite state : one of three states that an open file can
L
value error: an input value that does not satisfy assume. A file in the write state can only be used lor
specified criteria . output . See also error state , read state .
Symbols American Standard Code for Informa - assignment expression 104
lion Interchange. See ASCII compound 105
simple 104
^/ define 51
continuation 712
analysis 14
ancestor 968 assignment suppression flag 63
64, 408
<ctype. h> 264 , 265 and 233 associativity 94 , 106
<limits. h > 41 , 344 . 385 , 856 ANSI C 31
Icft - to- right 108
<math.h> 153, 18 /
append tile 42 -
right -to left 108, 109
<$tdbool. h > 34 application software 4
“
atan 2f 1063
<stddef . h> 433 , 562 arc 981 atan 2 l 1063
<stdin . h> 882 argc 1092 atanf 1063
<stdio. h > 33, 187 , 397, 402, 423, argv 1092 atanl 1063
562 , 629 array 460 atexit 1068
<stdlib. h> 187 , 268 , 628, 649 , 704 column 511 . 514, 520 atol 1068
<string. h> 691 , 733 constant 477 atoi 1068
<time.h> 193 declaration 463 atol 1068
caret 676 fixed - length 463
_ underscore
A
atomic
_bool 232 37 frequency 481
histogram 482
data 986
type 986
Exit 1068 in structures 767 auto 1106
index 464 automatic extent 1104
index as offset 465 auxiliary storage 394
A initialization 465 device 3
a _char 1062 1063 memory layout 514^ auxiliary symbol 1019
,
abort 268, 1068 multi-dimensional 519
abs 187 , 1067 of pointers 633 B
abstract data type 728, 986 , 987, 988 of structures 770
abstraction 988 passing 476 , 517 , 623 B Language 30
actual parameter 165 passing row 516 ^ base 2 1034
add 103 passing values 474 base 10 1034
additive expression 103 plane 520 base 16 1036
pointer 612 base 256 1037
address operator 559 to binary 1040
adjacent vertices 981 printing 469
ragged 639 weight and value 1038
Adler, Mark 922 base case 352
afferent 910 range checking 470
row 511 , 514 . 520 basic latin 1005 Plane 1006
ALGOL 30
size 463 Basic Multilingual
algorithm efficiency 365
string 688 batch update 859
algorithmics 366 BCPL 30 ^
big-0 notation 370 subscript 460 big endian 786
dimensional 509
exponential efficiency 372 two big-0 notation 370
factorial efficiency 372 variable-length 463 binary
linear efficiency 372 array of pointers 633 -
block i/o 8 3
linear logarithmic efficiency 372 ASCII 39 , 48 , 1005 file 823
logarithmic efficiency 372 character code table 1009 binary expression 101
polyonymic efficiency 372 extended 1005 addit Ve l ( 3R82
' !
quadratic efficiency 372 asctime 1069 bitwise and
—
alphabet ASCII 39, 48 assembler language
f
1156 Index
vertex 981
782
type qualifier 1111
const 52 , 477
union
imluh / er 82
referencing members /8
_ void 38
void function
with parameters 156
const pointer 1112 structure 83
"
without parameters 156
restrict 1114 switch statement 786 void pointer 581
volatile 1113 universal character set 1005 volatile 1113
typedef 746, 754 UNIX 30 , 1091 , 1121
identifier style 46
"
!•
3 SJCD 00358 198N
s
s ,
Argument Type Size Specifier Code
1
integral hh ( char ), h ( short ) , none ( int ), 1 ( long ). 11 ( long long ) l
integer h ( short ), none ( int ), 1 ( long ). 11 ( long long ) d
unsigned int hh ( char ), h ( short ), none ( int ), 1 ( long ), 11 ( long long ) u
character octal hh ( unsigned char ) o
integer hexadecimal h ( short ), none ( int ), 1 ( long ), 11 ( long long ) x
real .
none ( float ) 1 ( long double ), L (double ) f
real ( scientific ) none ( float ), 1 ( long double ), L ( double ) e
real ( scientific ) none ( float ), 1 ( long double ), L ( double ) g
real ( hexadecimal ) none ( float ), 1 ( long double ), L ( double ) a
character none ( char ), 1 ( wchar t ) _ c
string _
none ( char string ), 1 ( wchar t string ) s
pointer
P
integer ( for count ) none ( int ), hh ( char ), h ( short ), 1 ( long ), 11 ( long long n
set none ( char ), 1 ( wchar t ) _ [
Sizes, and Conversion Code for scanf Family
FEATURES
DATA STRUCTURES
o Updated to comply with the C99 standard, o Uses a large number of examples,
including discussions of complex ranging from code snippets to complete
arithmetic and Unicode. implementations requiring several
functions, providing the student with a
o Features a new full-color page design that
range of techniques to study and practice.
enhances figures and presents programs
as they might be shown in the compiler. o Contains extensive end-of-chapter Data Structures:
pedagogy, including a summary, A Pseudocode
o Offers a groundbreaking visual approach,
keywords, tips, and a robust problem Approach with C
including a wide variety of figures, tables, Second Edition
set featuring review questions,
and programs.
exercises, problems, and projects. Richard F. Gilberg &
Behrouz A. Forouzan
0-534 - 39080-3
ABOUT THE AUTHORS
Behrouz A. Forouzan has more than 32 years of electronics and computer science experience
in industry and academia. His industry experience includes designing electronic systems . After
leaving industry, he joined De Anza College as a professor of computer science. In addition to
this text , he has authored and co-authored nine other textbooks, including Data Structures: A
Pseudocode Approach with C and Foundations of Computer Science .
Richard F. Gilberg has more than 40 years of computer science experience in industry and
academia. His industry experience includes the development of large application systems,
database administration , and system testing. After leaving industry, he joined De Anza College
as a professor of computer science. In addition to this text, he has also co-authored several
others , including Data Structures: A Pseudocode Approach with C .
;V COURSEE TECHNOLOGY -- - -
- -
ISBN 13: 176 0 534 41135 1
-
ISBN 10: 0 534 41135 4 - -
Learning 30 DO >
CENGAG '