Prentice - Hall.the.c.puzzle - book.1982.SCAN DARKCROWN
Prentice - Hall.the.c.puzzle - book.1982.SCAN DARKCROWN
Prentice - Hall.the.c.puzzle - book.1982.SCAN DARKCROWN
PUZZLE
BOOK
Puzzles for the € Programming Language
QA ALAN R. FEUER
76. 73
«Cis
F48
1982
PRENTICE-HALL SOFTWARE SERIES
Digitized by the Internet Archive
in 2021 with funding from
Kahle/Austin Foundation
https://archive.org/details/cpuzzlebookO000feue_w2u4
QA /6.73.Clo F486 1382
Feuer, Alan R.
ny THD book
DATE DUE
ee ee
Eee as
ah ae
a eee
i sia a
Alan R. Feuer
Bell Laboratories
Murray Hill, New Jersey
PRENTICE-HALL, INC.,
Englewood Cliffs, NJ 07632
Library of Congress Cataloging in Publication Data
Feuer, Alan.
The C puzzle book.
(Prentice-Hall software series)
Includes index.
1. C (Computer program language) 2. UNIX (Computer
system) I. Title. Il. Series.
QA76.73.C15F48 001.64'24 82-5302
ISBN 0-13-109934-5 AACR2
ISBN 0-13-109926-4 (pbk.)
LOG 95 8) 7 Gr a 4 SZ
UNIVERSITY OF COLORADO
COLORADO SPRINGS
AUL 27 2009
We
KRAEMER FAMILY
LIBRARY
Tsen O=-L3=,049S8=5
ISBN 0-13-1099%eb-4 f{pbk-}
LEE ACE eens teens secctensnicol Sea cse wise COME ae eC c om oisdesetenaey esas page vii
PUZZLES
LOT8YS) 9) cc oceoobnace cote cache aaa een Soe emacaascd sadbrataG nadca coun pgocahictsnntee page 1
1. if Statement 27
2. while and for Statements 29
3. Statement Nesting 31
4. switch, break, and continue Statements 33
rer page 51
Miekseawrons: niaccennees
Pointers and ‘Atrays::.c,.csccces-wstsstevsvarccoruvesirsacce
1. Simple Pointer and Array 53
2. Array of Pointers 55
3. Multidimensional Array 57
4. Pointer Stew 59
Str Gturesi ccs creo cane atvc eres baat taec eure wieecnaeaicese coneutoney eaese he tae erage page 61
1. Simple Structure, Nested Structure 63
2. Array of Structures 65
3. Array of Pointers to Structures 67
PRE PROCOSSOT vsccccecacecsnasrpeaaesiy
veetieoncai satheoasnthecaar aia: oaposadauateneveneees page 69
SOLUTIONS
BaSiGel VpeSouucccesentocccrescvetevevsaeceactuvteosin
toc eestrs etree waycigeeetagVariaeeNes page 97
ASOMIT OLVELOW ssccc cecurenceugeret te at cence cay heat amen eevecenesers sera ceeee ee rate page 105
RRO SCAT Dut1G cet ovate terri ie occeviestass acer eenceneeemtoneemercera page 117
DSLOTARE CLASSESr vunsie cece gaonuterdenetacearertee
ceric otace. teasers Sen eater page 123
Pointers And ATraVS: ox, Mavtecatwe cevenansthissatsnadesgnceaeateroue
ehennen uate ances page 129
PSELTICE ECS 2rryc5 cmitreceu deeds oweaecsane tony vos trer ent ere ca coe gseredear rane a aeeontes page 141
PEC DROCCRSOL wc, crewed <agxve evetaesancie sentegeer crane rman orteemvcenesMesteecae
rene page 158
APPENDICES
Te Precedence: Ta Dlenr.ceccsesvncsessecn,
chine touereeneon oeeetteoemee eects page 165
2a AOPETACOL SoUMUIMLALY, “lOlC mertvcverres vcsnies een ereueme teenie ecneenet page 167
Pe PEL PRROUM csnivcocavieriscndGels) cocuancansuctnrveneyeaeedel tediscan Adee area page 171
H.,. TEypee Five tary Bart cisccavasisaasziden
seOoraenesha eevenna nee tees page 173
PREFACE
C is not a large language. Measured by the weight of its reference manual, C could even be
classified as small. The small size reflects a lack of confining rules rather than a lack of power.
Users of C learn early to appreciate the elegance of expression afforded by its clear design.
Such elegance might seem needlessly arcane for new C programmers. The lack of restrictions
means that C programs can be and are written with full-bodied expressions that may appear as
printing errors to the novice. The cohesiveness of C often admits clear, but terse, ways to
express common programming tasks.
C is still an evolving language. Depending upon the vintage of your local compiler, some of
the features explored here may not be implemented and some of the implemented features may
not be explored here. Fortunately, the evolution of C has proceeded uniformly, so it is very
unlikely that your compiler will have a feature implemented in a different way than described
here.
book is divide sections with one major topic per section. Each section comprises C
programs that explore different aspects of the section topic. The programs are sprinkled with
print statements. The primary task is to discover what each program prints. All of the
Vii
viii PREFACE
programs are independent of one another, though the later puzzles assume that you understand
the properties of C illustrated in earlier puzzles.
The output for each program is given on the page following the text of the program. Each of
the programs was run from the text under the UNIXt Operating System on Digital Equipment
Corporation PDP 11/70 and VAX 11/780 computers. For the few cases where the output is
different on the two machines, output is given from both.
The larger portion of the book is devoted to step-by-step derivations of the puzzle solutions.
Many of the derivations are accompanied by tips and caveats for programming in C.
ACKNOWLEDGEMENTS
The first C puzzles were developed for an introductory C programming course that I taught at
Bell Laboratories. The encouraging response from students led me to hone the puzzles and
embellish the solutions. A number of my friends and colleagues have given valuable
comments and corrections to various drafts of this book. They are Al Boysen, Jr., Jeannette
Feuer, Brian Kernighan, John Linderman, David Nowitz, Elaine Piskorik, Bill Roome, Keith
Vollherbst, and Charles Wetherell. Finally, I am grateful for the fruitful environment and
generous support provided me by Bell Laboratories.
Alan Feuer
main()
{
sis S38
OUTPUT:
11 (Operators 1.1)
(Operators 1.2)
0 (Operators 1.3)
1 (Operators 1.4)
main()
OUTEUT:
10 (Operators 2.1)
40 (Operators 2.2)
1 (Operators 2.3)
1 (Operators 2.4)
main()
{
ane ety avon 4s
5S St A Wee oe hee ar ae
x= x && y i4 2; PRINT(x); (Operators 3.1)
PRINT(sx hi tf yy S&S 2) ); (Operators 3.2)
= iy = 1s
Ze= x +t = 1s PRINT ())5 (PRINT (z); (Operators 3.3)
Z += = X ++ + ++ y; PRINT(x); PRINT(z) 3 (Operators 3.4)
Zi eexcee7 echtee x se PRN T(z); (Operators 3.5)
8 PUZZLES
OUTRUT:
1 (Operators 3.1)
1 (Operators 3.2)
2 (Operators 3.3)
0
3 (Operators 3.4)
0
? (Operators 3.5)
main( )
{
apes oy. Say 248
OUTPUT:
iT Ww (Operators 4.1)
S053 (Operators 4.2)
7 ads
PP
—- alee = 1 (Operators 4.3)
al — N N (Operators 4.4)
coed
Pal
eer,
Lem
3| * i] = (Operators 4.5)
WwW
-- = =-1 (Operators 4.6)
* * " ° (Operators 4.7)
=-8 (Operators 4.8)
Nl \ @ (Operators 4.9)
= ?
|Seti (Operators 4.10)
main()
{
Lee =a le ay =a eziselus
x t= Gy 4s) 23
IDIRIENON(C Se << Sh P a Ss Se is (Operators 5.1)
PRINT ( GS < yo P x +4 3 yo +4 9)
PRINT(x); PRINT(y); (Operators 5.2)
PRINT
(y) PRINT (zs (Operators 5.3)
X=33; y=z=4;
mine (~ Sa s7 Se o) P 1 8 Oe (Operators 5.4)
msueue GA Se 5, INS 4 SS se )) 2 (Operators 5.5)
12 PUZZLES
OUTPUT:
main( )
{
aaniite xX, YY, Z;
y=2zes1;
it ++y && ++2Z3 PRENTSi(s0,
yan Zs (Operators 6.1)
YouaS 2) = 1s
Vee eats
PRIENTolexnyen
zon (Operators 6.3)
Y= Zi 1;
&& ++y 11 +4+Z3 PRINT3(x,y,2Z);3 (Operators 6.4)
yY = 20= =1;
it ++y && ++2Z3 PRINT3(x,y,Z)3 (Operators 6.5)
Ree Beads
&& ++y && +42; PRENTS
(xc yaerze es (Operators 6.6)
14 PUZZLES
OUTPUT:
C has a comparatively small set of primitive types. The types may blindly be
mixed in expressions, the results governed by a simple hierarchy of
conversions. This hierarchy is illustrated in Appendix 4.
For some of the puzzles in this section you will need to know the
corresponding integer value of some characters. The tables in Appendix 3
show the values for the characters in the ASCII set. A few of the puzzles yield
a different result on the VAX than on the PDP 11. For those puzzles, output
from both machines is given.
PUZZLES S/
#include <stdio.h>
int integer = 5;
char character = ’5’;
Charewasiixing. = Sia:
main( )
{
PRINT(d,string); PRINT(d,character); PRINT(d,integer) ;
PRINT(s,string); PRINT(c,character); PRINT(c,integer=53);
PREEN (iclen Game me) ke (Basic Types 1.1)
{
Init ess =) —Sis
unsigned ux = -8;
PRINT(0,sx); PRINT(o,ux);
PROEN TCO) ssce
> Sim) iss REGEN
LE COr, mnt c> >see
PRUNT (di sx >> Sm) ise PRUNING) eeusc>
> 3) rs (Basic Types 1.2)
}
18 PUZZLES
OUTPUT:
spe =e VOR
I SPER ING. (Basic
Types 1,.2-VAX)
Wx = ST
a Le
Sxe=S SS B70 TR LLL? OF OFF
TPL IS die
ibipceoeeysd as nl /Balak
ayiMay,
sx>>3 = -1 or 536870911
ux>>3 = 536870911
#include <stdio.h>
main( )
{
double d;
float f;
PornGguels:
nLighe alg
i if =d (double) (100000/3) ;
(Basic Types 2.5)
OUTPUT:
#include <stdio.h>
main( )
{
doubilie d=37 2.5.95
alge Sky AY
OUTPUT:
Each of the remaining programs in this book begins with the preprocessor statement
#include "defs.h"
When the programs are compiled, the preprocessor replaces this line with the contents of the
file defs.h, making the definitions in defs.h available for use. Here is a listing of
adersiin:
#include <stdio.h>
defs.h begins with an include statement of its own, calling for the insertion of the file
stdio.h, as required by the standard C library. The rest of defs.h comprises macros for
printing. As an example, to print 5 as a decimal number, the PRINT1 macro could be called
by the expression
PRINT1(d,5)
which expands to
PR(d,5), NL
which further expands to
popealrmere (WS) ee Sache!
5((S)))) 4 joiechacie((
“Na” ))
The PRINT macros point out a feature of the preprocessor that often causes confusion. A
macro name that appears inside a string (i.e., enclosed within double quotes) will not be
expanded. However, argument names within the body of a macro will be replaced wherever
they are found, even inside strings. Notice that the macro PR takes advantage of the latter
property. See the Preprocessor Section, beginning on page 69, for a more detailed description
of macro substitution.
23
Wall Me6er G ae > iat a]
> wb @«¢: @o6 occ @ Ghee dass
%e
=—=»G : en) ee — ee |
a
NN yates
ly<t|\ Gee
3
7
7 7 ~~ > Caress oy +
ae 1a)A lapas an eee ieee oe
; eo aeee
at © £0rh pg
aes 2
— = ee
7 — 7 =>
7 id Ma # ns
°°. & — a
; a
Control Flow
1. if Statement
Statement Nesting
woN
A switch, break, and continue Statements
25
PUZZLES 27
#include "defs.h"
main()
{
sie Pay Sly £40
ee (eyi== )) Sesh
else x=5;
PRINT1(d,x); (Control Flow 1.2)
x=1;
nell YAsO) i ES SE) )) seeshe
elsie x=573
PRENT Cdyn) (Control
Flow 1.3)
el Gea= a) es).
PR UND 2dr Ze) (Control
Flow 1.6)
28 PUZZLES
OUTPUT:
#include "defs.h"
main( )
{
FMS bee S7A LAR
x=y=0;
Whaley < 10) Sysco =n ys
PRENG2 (dey) 5 (Control Flow 2.1)
x=y=0;
while( y<10 ) x += ++y;
PRINT
2 (@,20,y:) 5 (Control Flow 2.2)
YeA
while( y<10 ) {
X = yt; Z = ++Y3
}
PRENTSAdp
Gay. zis (Control Flow 2.3)
OUTPUT:
#include "defs.h"
#define ENUF 3
#define EOS ’\0’
#define NEXT(i) inputli++]
#define FALSE 0
#define TRUE 1
main( )
{
charnec:
In teaonee hag. 37.0 nL Ow.
i=low=in=high=0;
while( c=NEXT(i) i= EOS )
Te ( te<2 0") tow++:
else if( c>’9’ ) high++;
else in++3
PRINT3(d,low,in,high) ; (Control Flow 3.1)
i=low=in=high=0; done=FALSE;
while( (c=NEXT(i))!=EOS && !done )
Pe c<0-)) low++:
else if( c>’9’ ) high++;
else in++;
if( low>=ENUF |i high>=ENUF ii
done = TRUE;
PRINT3(d,low,in,high) ; (Control Flow 3.2)
i=low=in=high=0; done=FALSE;
while( (c=NEXT(i))!=EOS && !done )
if( c<’0’ ) done = (++low==ENUF);
else if( c>’9’ ) done = (++high==ENUF) ;
else done = (++in==ENUF) ;
PRINT3(d,low,in,high) ; (Control Flow 3.3)
32 PUZZLES
OUTPUT:
#include "defs.h W
main()
{
eos. legs ehp
BOX; (ela
ee ( Geayoenellalt))) le?\O? Sg shes) a
switch (ae) 4
case ane putchan(
4a )conta nue.
-
OUTPUT:
Much has been written about programming style, about which constructs to
avoid and which to imitate. A cursory conclusion from the seemingly diverse
advice is that good style is largely a matter of personal taste. A more reasoned
conclusion is that good style in programming, as elsewhere, is a matter of good
judgement. And while there are many good style guidelines, there are few
always appropriate, always applicable style rules.
With this in mind, the following puzzles illustrate a few common style
blunders. The solutions given are not so much answers, as in other sections,
but rather alternatives. If there is an overall key to good style, it is a
recognition of the final two steps in writing a readable program:
e Establish a clear statement of the idea to be coded.
e Develop the structure of the code from the structure of the idea statement.
3D
4 9 @p-eaey pet = >
°) GF EDS, MM eae
oy) OSs Gos (he,
Marea ee 6! Aang
hb eer et ™:
ee re
2 2@) CO eG. ow
0 © e® 62 cht Gamsae i :
@& st Be — i.
1D S| 4 gone, ay 3 re
Deel 6 tileed} rE Wes)
iF
~
PUZZLES 37
while(A) {
if(B) continue;
Cc;
} (Programming Style 1.1)
do {
if(!A) continue;
else B;
C;
} while(A); (Programming Style 1.2)
if(A)
obee ((is)))
ste((()}) yg
else;
else;
else
cia (G5)
if(C) E;
else F;
else; (Programming Style 1.3)
while( (c=getchar())!=’\n’ ) {
cinta (aC = — rn) mC One anwer:
fC == Nt) Cont aniter:
if( c<’0’ ) return(OTHER) ;
sine(( (eges“ Sy) saxsrerbivera((oeepagay))2
1f( ce<7a’ ) return (OTHER) ;
if( e<=’z’ ) return(ALPHA) ;
return(OTHER) ;
} return(EOL); (Programming Style 1.4)
38 PUZZLES
done=i=0;
while( i<MAXI && !done
af (xc7=2))>) )
done++;
} (Programming Style 2.1)
{
if(A) { B; return;
SECC) )eeD seee Culenis
if(E) { F; return;
Gy return);
} (Programming Style 2.2)
plusflg=zeroflg=negflg=0;
if( a>O ) ++plusflg;
if( a== ) ++zeroflg;
else if( !plusflg ) (Programming Style 2.3)
i=0;
while((c=getchar())!=EOF) {
if(el="\n’sSc!l="\t’
){sli++ l=c; continue; }
Df(c=—— N\n)ibreaks:
se (eRe
we NOeo “se
sli++l=c;} (Programming Style 2.4)
se(( Sells) )}
stsel( Sjeeis ) SeSa/ere8
else y=k/x;
else
if( j>k ) y=j/NEARZERO;
else y=k/NEARZERO; (Programming Style 2.5)
40 PUZZLES
1. Blocks
2. Functions
3. More Functions
4. Files
Each variable in C possesses two fundamental properties, type and storage class.
Type has been covered in an earlier section.
Storage class determines the scope and lifetime for a variable, scope being that
part of a program in which a variable is known and lifetime being that portion
of an execution during which a variable has a value. The boundaries of scope
and lifetime are blocks, functions, and files.
41
PUZZLES 43
#include "defs.h"
aate akOe
main()
{
AUTOM nite el = 1
PREN T(r) is
{
aes sh Aie
PRIN Ed. )s
{
i += 1;
PRINT1(d,i);
}
PRINT1(d,i);
u
pseeisns| (Cel. at )) 2 (Storage Classes 1.1)
44 PUZZLES
OUTPUT:
ee
=ee Gd
ORS
Co
ai
Sk
#include "defs.h"
#define LOW 0
#define HIGH 5
#define CHANGE 2
int i=LOW;
main()
{
auto int i=HIGH;
weasel a/72 8 Tite
ee 4](cl. aL) 2
masaie( shos/72 ))8 ihieeae|(el..5h
))
Bh SS seaciena( shy/7y js iistaueme a(t. ak)) 2
workover(i)
alisha Bt
{
ci ei (G15) eka (cies)
4 (02 acl) tA)
PRINT1(d,i);
sareyeiwleasey((
SL})8
int reset(i)
plerditeam lars
{
i = i<=CHANGE ? HIGH : LOW;
return(i);
46 PUZZLES
OUTPUT:
"
BOR
oP
oH
Be WO
hw:
of
a
#include "defs.h"
int i=1;
main()
{
Wie alice Sha Sfh
i = reset();
POrte jets. J<=35) je te 0 t
RUS
OA (Che at 55]))2
PRINT1(d,next(i));
PRINT1(d,last(i));
PRINT1(d,new(i+j)); (Storage Classes 3.1)
Init reset()
return(i);
next(j)
33
return( j=it++ );
last(j)
33
Sealeceea nit a — Or
return( j=i-- );
new(i)
at
OUTPUT:
#include "defs.h"
main( )
{
eubwee) alee Bb, 5/8
i = reset();
for( j=1; Wee5R sie j) xf
PREEN
L 2iCusrlisnyi as
PRINT1(d,next(i));
PRINT1(d,last(i));
PRINT1(d,new(i+j)); (Storage Classes 4.1)
int last()
{
return( i-=1 )3
}
int new(i)
cighe obR
{
Sitatae eint j= 5's
raeran(( sleaGjabest ))e
50 PUZZLES
OUTPUT:
last) = 10
new(i+j) = 7
i= 1 4) =e
Rest (ci) =e
rarcits (215)) (= 110
new(it+j) = 10
= iS
nextGa)) = ail
haste Gis) =) 10
new(i+j) = 14
Pointers have long been abused by programmers and thus maligned in style
guides. Specifically, pointers are criticized since, by their nature, it is
impossible to identify fully a pointer’s referent without backing up to where the
pointer was last defined; this adds complexity to a program and makes
verification much more difficult.
The C language, rather than restricting the use of pointers, often makes them
the natural choice for use. As the following puzzles will illustrate, pointers and
arrays are very closely related. For any application using array indexing, a
pointer version also exists. The warnings against the dangers of pointer misuse
apply as strongly to C as to any language.
51
,re
(nas ws OF beNA 4
apo dt @) 06 eaten GF oe a heme?
Gal we
PUZZLES 53
#include "defs.h"
mame El ISO,
W525 Sacer s
main( )
{
algae ahs Eels
OUTPUT:
ep = 4 ‘nf = 3 +p = 1 *p = 0
(Pointers and Arrays 1.5)
pl=i)}c= pl-il] pl-i] =" 1 pl-il = 0
(Pointers and Arrays 1.6)
alp-al = 4 alp-al] alp-a] = 1 alp-a] = 0
(Pointers and Arrays 1.7)
#include "defs.h"
main( )
{
PRINT2(d,a,*a) ;
PRINT3(d,p,*p,**p) ;
PRINT3(d,pp,*pp,**pp); (Pointers and Arrays 2.2)
NL;
pp++; PRINT3(d,pp-p,*pp-a,**pp) ;
*pp++; PRINT3(d,pp-p,*pp-a,**pp) ;
*++pp; PRINT3(d,pp-p,*pp-a,**pp) ;
++*pp; PRINT3(d,pp-p,*pp-a,**pp) ; (Pointers
and Arrays 2.3)
NL;
PP=P;
**ppt++; PRINT3(d,pp-p,*pp-a,**pp) ;
*++*pDp; PRINT3(d,pp-p,*pp-a,**pp) ;
++**pp; PRINT3(d,pp-p,*pp-a,**pp) ; (Pointers
and Arrays 2.4)
56 PUZZLES
OUTPUT:
#include "defs.h"
tntpalsilsi = {
Aliso Sook
tf Ub Gi Wop ee
ee Ce hae
iG
Wms GoelSi) B
ahoks calitd. ala)
};
inte ep = alOnl (Pointers and Arrays 3.1)
main()
{
aiielic) Eb2
OUTPUT:
1
alill2-i] = *alil *(*(a+i)+i) (Pointers and Arrays 3.2)
alil(2-il] = *alil *(*(a+i)+i)
#include "defs.h"
char acl] = {
"ENTER",
"NEW",
"POINT",
BUR Sian
};
char *«*cpl] = { ¢c+3, c+2, c+1, c };
char ***cpp = cp; (Pointers and Arrays 4.1)
main( )
{
print£("%s", **++cpp );3
printf("%s ", *--*++cpp+3 );
Printe("X%s"™, *seppl-21+3) );
Peince(“%se\n". cppl—-110-11+1)- (Pointers and Arrays 4.2)
60 PUZZLES
OUTPUT:
61
PUZZLES 63
#include "defs.h"
main( )
{
Staticestruct S41)
char cl4], «s;
jars (Set ADC "dete yc
Staticms tructmsG ou +
char, xCps
StesCG Caronmes Silne
fos oeaets Mog MedMee Mma ws ot ub (Structures 1.1)
OUTPUT:
#include "defs.h"
struct $1 {
char *s;
oleae, al 8
Seemche Si] oe ese
OUTPUT:
#include "defs.h"
struct si {
char *s;
StGue ees ies lip:
a
main( )
{
static struct S1 al] = {
{-"abeda", a+1 },
xf Uerecin’Y , Gee Ir.
te ake a oh
};
struct $1 *«pl3]; (Structures 3.1)
aipene Bhp
swap(#*p,a);
PRINT3(s, plOJ]->s, (*p)->s, (*p)->sip->s); (Structures 3.3)
swap(pl[0], pl0OJ]->sip);
PRINT3(s, plOJ]->s, (*++pl0]).s, ++(*++(*p)->sip).s);3
(Structures 3.4)
}
swap(p1,p2)
struct Si-*xpi, «p23
{
char «temp;
temp = pi1->s;
pil->s = p2->s;
p2->s = temp;
68 PUZZLES
OUTPUT:
Though in a strict sense the preprocessor is not part of the C language, few C
programs would compile without it. Its two most important functions are
macro substitution and file inclusion.
69
a
ay (2 Gest on Po
rei ete &1e
a 660 tn a NH | ;
-_
- > asPron
— &® GP Uni peers 5
s@u eames * ~ aa
=a 4) Gp i. ape
eres oer oeOe at
PUZZLES 71
#include <stdio.h>
#define FUDGE(k) k+3.14159
#define PRiCa) ee prance a= 4d Nt Gam cl Gaye)
#define PRINT(a) PRi¢ay) se putchar (Xn)
#define PRINT2(a,b) PR(a); PRINT(b)
#define PRINT3(a,b,c) PR(a); PRINT2(b,c)
#define MAX(a,b) Catia be may)
main( )
{
Inte s=2)3
PRINT( x*FUDGE(2) ); (Preprocessor 1.1)
anitacenss
for( cel=03 cel<=100; cel+=50 )
MiG x= y= 203
PRINT3( MAX(x++,y),xX,y )3
PRINT3( MAX(x++,y),xX,y )3 (Preprocessor 1.3)
72 PUZZLES
OUTPUT:
#include <stdio.h>
#define NEG(a)-a
#define weeks(mins) (days(mins)/7)
#define days(mins) (hours(mins)/24)
#define hours(mins) (mins/60)
#define mins(secs) (secs/60)
#define TAB(c,i,oi,t) cifea CC — a NV
OT Gt='S'— (Gl — On —alh)) Or Omics)IN
ohh
eon ol- al CnGmnan
#define PR(a) eee)
(Mey no CNG ((elataitce)
(cc)
#define PRINT(a) PR(a); putchar(’\n’)
main( )
{
{
Nee =a1/-2
PRINT( -NEG(x) ); (Preprocessor
2.1)
}
{
PRINT( weeks(10080) );
PRINT( days(mins(86400)) ); (Preprocessor
2.2)
}
{
Siac Cac hala mpuit a =n ct winch eatin
Chidieac!.
alsayic, ah5 copilelsi, ASehwmjeye
OUTPUT.
Operators 1.1
About printf. Printf is the formatted print routine that comes as part of the standard C
library. The first argument to printf is a format string. It describes how any remaining
arguments are to be printed. The character % begins a print specification for an argument.
In our program, %d told printf to interpret and print the next argument as a decimal
number. We will see other print specifications in later programs. Printf can also output
literal characters. In our program, we “‘printed’’ a newline character by giving its name
(\n) in the format string.
78 BASIC ARITHMETIC OPERATORS
Operators 1.2
x= 3+ (4%5)
-6 Following precedence
x = (3+(4%5)) - 6 and associativity
x = ((3+(4%5))-6) leads to
(x=((34+(4%5))-6) ) this. (The modulo, %, operator yields the
remainder of dividing 4 by 5.)
(x=(7-6))
(x=1)
1
Operators 1.3
x= ((-3)*4)
% (-6) 45 *, %, and / are all at the same precedence level,
and they associate from left to right.
x = (((-3)*4)%(-6)) 75
x = ((((-3)*4)%(-6))/5)
(x=((((-3)*4)%(-6))/5))
(x=(((-3%4)%-6)/5)) Evaluating from the inside out.
(x=((-12%-6)/5) )
(x=(0/5))
(x=0)
0
BASIC ARITHMETIC OPERATORS 79
Operators 1.4
About programming style. As mentioned in the Preface, the programs in this book are not
models to be copied. They were designed to make you think about the mechanics of how C
works. But the puzzles do contain messages about program style. If a construct always
forces you to consult a reference to find out how some detail is handled, then the construct
is either not well written or it should be accompanied by a comment that provides the
missing details.
The message from this first set of puzzles is to use parentheses in complex expressions to
help the reader associate operands with operators.
80 ASSIGNMENT OPERATORS
Operators 2.1
initially x=2
x*=3+2 Again follow the precedence table.
x #= (3+2) As we Saw earlier, the assignment operators have lower
precedence than the arithmetic operators. (*= is an assignment
operator.)
(Ce¥=1(3)-2))))
(x*=5) Evaluating.
(Sis Sap 15) Expanding the assignment to its equivalent form.
(x=10)
10
Any line in a C program that begins with the character # is a statement to the C
preprocessor. One job done by the preprocessor is the substitution of one string by another.
The define statement in this program tells the preprocessor to replace all instances of the
string PRINTX with the string printf("%d\n",x).
ASSIGNMENT OPERATORS 81
Operators 2.2
initially x=10
x*=y=z=4
(x*=(y=(z=4)))
(x*=(y=4) ) Evaluating.
(x*=4)
40
Operators 2.3
(x=(y==z))
Operators 2.4
(x==4) Evaluating.
FALSE,
or 0 The value of the expression is 0. Note however that the
value of x has not changed (== does not change its
operands), so PRINTX prints 1.
LOGIC AND INCREMENT OPERATORS 83
Operators 3.1
More about define. The define statement that begins this program is a little fancier than
that in the previous program. Here, PRINT is the name of a macro with arguments, not just
a simple string. The preprocessor performs two levels of substitution on macros with
arguments: first the actual arguments are substituted for the formal arguments in the macro
body, and then the resulting macro body is substituted for the macro call.
For example, in this program PRINT has one formal argument, int. PRINT(x) is a call
of PRINT with the actual argument x. Thus, each occurrence of int in the macro body is
first replaced by x, and then the resulting string, printf("%d\n",x), is substituted for
the call, PRINT(x). Notice that the formal parameter int did not match the middle
letters in printf. This is because the formal arguments of a macro are identifiers; int
only matches the identifier int.
84 LOGIC AND INCREMENT OPERATORS
Operators 3.2
initially
x=1, y=1, z=0
xa Wyk hez
x ion Cly) && z Binding operands to operators.
xii ((ly)&&z)
(xit((ly)&&z) )
Operators 3.3
(z=0)
LOGIC AND INCREMENT OPERATORS 85
Operators 3.4
z+= ((-(x++))+(++y) )
(z = 0+0)
(z=0)
0
Operators 3.5
Z=x/ (++x)
Z= (x/(++x) )
(z=(x/(++x)))
You may be tempted at this point to begin evaluating this expression as before, from
the inside out. First the value of x would be retrieved and incremented to be divided
into the value of x. One question that might be asked is what value is retrieved from x
for the numerator, 3 or 4? That is, is the value for the numerator retrieved before or
after the increment is stored? The C language does not specify when such a side
effect! actually occurs; that is left to the compiler writer. The message is to avoid
writing expressions that depend upon knowing when a side effect will occur.
1. A side effect is any change to the state of a program that occurs as a byproduct of executing a
statement. By far the most common side effects in C relate to storing intermediate values in
variables, such as with the increment operator as above or with an embedded assignment operator.
86 BITWISE OPERATORS
Operators 4.1
Operators 4.2
(03102) In binary,
OF LOMO
& 1 . 110
~~ 0000010
3
10
Feoried
abso
Operators 4.3
(x*(02&~01))
(08302)
4 In binary,
10
ali
01
88 BITWISE OPERATORS
Operators 4.4
initially
x=03, y=02, z=01
x& y && Zz
((x&y )&&z )
((038&02)&&z)
(028&z)
( TRUE&&z )
( TRUE&&0
1)
( TRUE&& TRUE )
TRUE, or 1 && yields TRUE whenever both operands are
TRUE.
Operators 4.5
initially x=0 1
Wisc ue
Gia ns)
(( 1!TRUE)
ix)
(FALSE:01)
(0101)
,
BITWISE OPERATORS 89
Operators 4.6
initially x=01
~xX 0x
((~x) 1x)
(~01101)
=I In binary,
loo
o WIG
re .001
A)Ae
(The answer is the same for all values of x. Actually, it is -1
on a two’s-complement machine, like the PDP-11. On a
one’s-complement machine 1...1 would be -0. For the few
cases in this book where it matters, two’s-complement will be
used.)
Operators 4.7
initially x=01
ia
GOnia OK)
0 In binary,
OFS 7 Onl
Fee Oteenet OM
7505200
(The answer is the same for all values of x.)
90 BITWISE OPERATORS
Operators 4.8
initially x=01
See ees
x = 01<<3
x=8 In binary,
CVG s. 04
<< 3
0...01000, whichis 8
Operators 4.9
initially y=-01
y <<=3
y = -01<<3
y= —8 In binary,
TOGA sae
<< 3
Tee VOUS) Bree
Operators 4.10
initially y=-08
Y +e2 3
y = -08>>3
It is tempting at this point to assume that y = -1. Unfortunately this is not always the
case, since the computer may not preserve the sign of a number when shifting. C does
not guarantee that the shift will be arithmetically correct. In any case, there is a much
clearer way to divide by 8, namely y=y/8.
RELATIONAL AND CONDITIONAL OPERATORS 91
Operators 5.1
((x<y)?(y):(x))
(FALSE? (y): (x) ) First the condition is evaluated. Then either the
true part or the false part is evaluated, but not
both.
eon) In this problem the value of the condition is
FALSE, thus the value of the conditional
expression is the value of the false part.
Operators 5.2
((x<y)?(x++):(yt+))
Operators 5.3
(z+=((x<y)?(x++)
3 (y++)))
(z+=(FALSE? (x++):(y++)))
(z+=((y++) ) The result of the conditional expression is
the right-hand side of the assignment.
(z=4)
Ss
Operators 5.4
(((z>=y)>=x)?(1):(0))
GCIRUE> ==) PC 11) 20.0) The condition is evaluated from the inside
out.
(FALSE? (1):(0))
((0))
0
RELATIONAL AND CONDITIONAL OPERATORS 93
| Operators 5.5
((z>=y)&&(y>=x) )
(TRUE&&(y>=x) ) Evaluating from left to right.
( TRUE&&TRUE )
(TRUE)
1
94 OPERATOR PRECEDENCE AND EVALUATION
Operators 6.1
++ x11 ++ y S& ++ Zz
TRUE,
or 1
Operators 6.2
(TRUE!
1 (++z) )
TRUE, or 1 z is not affected.
About evaluation order and precedence. For most operators, the order of evaluation is
determined by precedence. As can be seen from the puzzles in this section, there are a few
exceptions to this general rule:
e Pre- increment and decrement operators are always evaluated before their operand is
considered in an expression.
e Post- increment and decrement operators are always evaluated after their operand is
considered.
e Logical AND and OR are always evaluated conditionally from left to right.
OPERATOR PRECEDENCE AND EVALUATION 95
Operators 6.3
(((++x)&&(++y) )&&(++z) )
((28&&2)&&(++z)), and x=2, y=2
( TRUE&& (++z) )
(TRUE&& TRUE) , and z=2
TRUE,
or 1
Operators 6.4
Operators 6.5
((+4+x) tt ((++y)&&(++z)))
(FALSE: 1 ((++y)&&(++z))),
and x=0
(FALSE! (FALSE&&(++z))),
and y=0
(FALSE!
| FALSE)
FALSE, or 0
Operators 6.6
(((++x)&&(++y) )&&(++z) )
( (FALSE&&(++y) )&&(++z)), and x=0
(FALSE&&(++z) )
FALSE, or 0
About side effects in logical expressions. As you have surely learned by now, the evaluation of
a logical expression can be tricky in C because the right-hand part of the expression is
evaluated conditionally on the value of the left-hand part. Actually, conditional evaluation is
a useful property of the logical operators. The trouble arises when the right-hand part of a
logical expression contains a side effect; sometimes the side effect will occur and sometimes
it won’t. So, while in general it is good practice to use side effects carefully, it is vital in
logical expressions.
CHARACTER, STRING, AND INTEGER TYPES 97
PREND (din G@: 5. >5)))) One last time. ’5’ has the integer value 53 which is
greater than the integer 5.
1. The value given here is that for the ASCII character code (see Appendix 3). The ASCII code is but
one of several codes used by computers to represent characters. It will be used in this book for those
few cases where it matters.
98 CHARACTER, STRING, AND INTEGER TYPES
i=l=f
=d= 1000/3
(i= (l= (f£= (d= (100/73)) ))) Evaluation is from right to left.
(Gia l= a(er—s id= iso) me) ee) Since both 100 and 3 are integers,
the division is integer division and
thus the quotient is truncated.
(i= (l= (£=(double)33) )), and d=33 Recall that the value of an
assignment expression is the value
of the right-hand side cast in the
type of the left-hand side.
(C= (l= (Gf Loa)
is si) meander =3'5
Gaon
g sis rand l= 313
(integer)
33, and i=33
33, an integer
33,
a double
100 INTEGER AND FLOATING POINT CASTS
i=l=f=d=100/3.
33, a double
INTEGER AND FLOATING POINT CASTS 101
d=fel=i= 100000/3
(d=(f£=(Long)-32203)) )
and 1=-32203 The result of an operation that leads to
overflow is a legitimate number, just
not the number expected. The 33333
is lost, regardless of future type casts.
-32203, a double
About numbers. The treatment of numbers is not one of C’s strong points. C does not
provide a way to catch arithmetic errors even if the hardware so obliges. The range of the
numerical data types is fixed by the compiler writer; there is no way to specify a range in the
language. To achieve range checking, about the best one can do is explicitly test the value
of variables at critical points in a calculation.
MORE CASTS. 103
(x=2)
Qreandes—2
imitiallyad=Sr)2)u=2
y = (x=d/i)*2
(y= (x=1.6)*2)
(y=1.6%2),
and x=1.6 Since x is a doubl1le, the result of the assignment is a
double.
(y= d* (x=2.5/d) )
(y= "d*2..57d ), and x=2.57d x is a double, so the precision of
2.5/4 is retained.
(y=2).5)
2, ange =2 y gets 2.5 truncated.
(x="de (y=(2+7
2.1) 7a) } Type cast has higher precedence than
+.
About mixing types. By now you have seen enough examples of how mixing floating point
and integer values in expressions can lead to surprises. It is best to avoid arithmetic with
operands of mixed type. If you do need it, make the type conversions explicit by carefully
using casts.
IF STATEMENT 105
initially y=1
aia ( 7 tS) )) Seebys The first step is to evaluate the condition.
(ya 08)
(n=O)
TRUE Since the condition is TRUE, the true part of
the if statement is executed.
ee)
initially y=1
itary = 0m) eS) em serac=) 5)
FALSE
pe
ey 1S Execute the false part of the if statement.
106 IF STATEMENT
initially y=1
x=
Dl v0) LEC yo0) mass
else x=5;
initially y=1
oie ( PABi
a s10)e)) Bac
else =
if ( y==0 ) x=5;
)
else x=7;
( z=FALSE )
initially y=1
if( z=(y= )) )) seeath.e secs)
whie(43 (ye ) ) dt seeSg } seoBig The true part of an if is the single
statement or block following the condition
for the if.
( z=FALSE )
FALSE, and z=0
initially y=1
Je ((xezey jy x=3)3
( x=(z=1))
(ce) pea ne zi=a0
TRUE eanGdisc=—i
y = 0 through 9 in the loop y=0 the first time in the loop. Each time
through the body y is incremented by 1.
y = 10 on exit When y= 10 the loop condition evaluates
to FALSE and the iteration terminates.
x= 10
WHILE AND FOR STATEMENTS 109
initially y=1
< WW 11 on exit
110 WHILE AND FOR STATEMENTS
for yea ls y< 10) yr ) xeys The for statement aggregates the
controlling factors of the loop.
y<10 Loop condition.
y>=10 Exit condition.
al Initial value.
yt+t Effect.
y = 1 through 9 in the loop
x = 1 through 9 x gets the value of y in the body of the
loop.
y = 10 on exit
yo Loop condition.
y<=1 Exit condition.
y=1000 Initial value.
y/=10 Effect.
y = 1000, 100, 10 in the loop
<e=eS Onvexit
112 STATEMENT NESTING
input="PI=3.14159, approximately n
input="PI=3.14159, approximately"
switch(’w’) { c=’W’.
default: putchar(’W’); continue As before, W is printed.
= Oem oe a M is printed.
The need for a continue statement can often be eliminated by altering a test condition.
The resulting code is sometimes remarkably cleaner.
For this problem, simply negating the test to the if statement will do.
while(A)
ska
(([132))) (OR
In this problem, the if and do...while are redundant; they are effecting a while.
do { First, eliminate
the continue.
np ail(YW as a = eo
} while(A);
or,
te es)
4e( ASE
OC YDS
else if( !A && C ) E;
else af( WA 6&& TC) F:
There are usually many ways to express an idea in C. A useful guideline is to group ideas
into chunks. C provides a hierarchy of packaging for these chunks:
e the lowest level ideas become expressions;
In this problem there is a two level idea hierarchy. At the lowest level are the expressions
B, D, F, and G. They are related as the mutually exclusive cases of a multiway switch. A
cohesive representation for a general multiway switch is the if ...else if construction.
rig ((Z\)) Uste
else if(C) D;
else if (E) F;
else G;
return;
120 CHOOSE THE RIGHT CONSTRUCT
The key observation in this problem is that the underlying structure is a three-way switch
with mutually exclusive cases.
tf jek.) vous 37 (ela fox 2 NEARZERO) < In this problem it is quite clear
else y = & / (xl=0 ? x 4° NEARZERO); that x !=0 is not the primary
idea; the test simply protects
against division by zero. The
conditional nicely subordinates
the zero check.
R= MASlesiie)) of Cs LaOn ie akan) Ni Ain blReis A case can be made that the
assignment to y is the primary
idea, subordinating both tests.
(MAX returns the greater of its
two arguments.)
BLOCKS 123
PRUEN Dilla ale) When two variables have the same name, the innermost variable is
referenced when the name is given; the outer variable is not
directly accessible.
Block level is now 2.
inter=2\ 22212
The storage class of i.2 is auto, the default storage class for
variables defined in block 1 or deeper. The scope of i.2 is block 2
and its lifetime is the duration of execution of block 2.
PREN Tadd) rs
i. The block level at any point in the text of a programis the count of left braces ({) minus the count of
right braces (}). In other words, it is the number of textually open blocks. The outermost level of a
program, i.e., no blocks open, is block level 0.
You might ask why the storage class of i is not explicitly declared here using the extern keyword.
Unless declared otherwise, the storage class for variables defined at block level 0 is extern. Tagging
a variable with extern does not define the variable. Instead, it tells the compiler that the variable
has been defined elsewhere at block level 0.
124 FUNCTIONS
{
auto int i=HIGH; vi) IPs
reset (1-25 The function reset is called with the value i.1/2, or
2. Its execution has no effect on i.l.
PRINT Caan) ¢
reset lai 2). reset is again called with i.1/2. This time i.1 is
assigned 2 as a side effect of the function call. Again,
reset has no effect on i.l.
PRIENT
Gade 2tel ie
ii=Tesec
(ual 72s) ¢ i.l gets the value returned by reset called with
i.1/2. We will expand the function call in line.
int reset(1) The type of the value returned by a function is
specified in its declaration. reset returns a value of
type int.
PRENT
(de dade)
workover(i.l);3 workover is passed the value of i.1; i. is not
affected by the call. We'll expand workover since it
includes a PRINT.
workover(5) If not otherwise specified, functions return an int.
{ (int i=5;) i.workover = 5.
i.workover = 0 * whatever; i.workover = 0.
PRINT1(d,i.workover) ;
main()
{
aueonn Gray. s i.l and 35.1 are defined, but not yet set.
{
return(i.0); As reset has neither a parameter nor a
local variable named i, the reference to i
must refer to i.0. reset returns 1, so
stgi) 34,
}
eepe(( SpilSds spilsske sil )) x J.—
I —
PRINT
2 (0052.1 5.5.15);
PRINT1(d,next(i.l));
TONS
soKEb e185((41),
PRINT1(d,last(i.l));
lnitcelialsit (a)
}
PRINT1(d,new(i.l+j.1));
int new(2)
}
for( J.le=t; 4.1<3; 4.14% 34 4.1 = 2.
Back to the for statement. For this
iteration we will generalize about the effect
of each statement.
RELN
A (i tel guelede ne The effect of executing the loop body is to
increment j.1 by one. The loop has no
effect on the value of i.1.
PRIUNT
1 (di enext (i.0 ))s next ignores the value it is passed and
returns the current value of i.0. Asa side
effect of executing next, i.0 is
incremented by one.
PRINT
1 (dad, last (d,.1).)% last also ignores the value of its passed
argument. It returns the current value of
its local static variable, i.last. Asa
side effect of executing last, i.last is
decremented by one.
PRINT1(d,new(i.l+j.1)); new returns the value of its argument plus
10. There are no lasting side effects.
FILES 127
main( )
ole=eresie tio)
}
eopel( Spills siallesie gipileses: jak j.1 = 1.
PRINT2\G,
4.15 gelt)s
PRINT1(d,next(i.l));
static int i=10; The second source file begins with an external
definition of a variable named i. This definition
might appear to be in conflict with the external
variable i defined in the first file. The designation
static, however, tells the compiler that this i is
known only within the current file. In other words, it
is only known within the functions next, last, and
new. We will reference it by i.nln; i.nln = 10.
next ()
}
PRINT1(d,last(i.1));
Lase()
{
return(i.nln-=1) ; i.nln = 10 and last returns 10. last references
the same i previously incremented by next.
128 FILES
PRINT1(d,new(i.l+j.1));
new(2)
}
for( j.1=1; j.1<3; j.l++) { 4.1 = 2.
In this iteration we will generalize about the
effect of each statement.
PRENT
Zi(de calnmyalaie The effect of the loop is to increment j.1 by
one.
PRINT1(d,next(i.l)); next increments i.nln and returns the
resulting value.
for( p=&al0],i=1; i<=5; i++ ) p points to the start of the array a. i takes
on the values 1 through 5.
PR(d,plil); plil] successively refers to the elements of
a. pl[5] points outside of the array.
About arrays and indices. Though by far the most common use of [] is to-represent array
subscripting, [] actually is a general indexing operator. xl[iJ] is defined to be *(x+i),
where x is usually an address and i is usually integral. The rules of address arithmetic
apply, so i is in units of sizeof (base type of x). (it should by now be clear why array
indices begin at 0. An array name is actually a pointer to the first element in the array. An
index is the offset from the array start. The offset to the first element from the array start is
0.) In this last problem, i is used to index off p. pli] = *«(p+i) = *(a+i) = alil.
i goes from 1 to 5. When i=5, p+i points just beyond the end of the array, hence the
value at p+i is unknown. This is such a common mistake, it is worth noting again: an
array with n elements has indices of 0 through n-1.
for( p=a+4,i=0; i<=4; i++ ) P points to the last element of a, i goes from
0 to 4.
Figure 2.1
ARRAY OF POINTERS 133
#DD++ *(ppr+rt)
Unary operators group from right to left. First the
increment is bound, then the indirection. The bold
arrow in Figure 2.3-2 shows the effect of the
increment.
#++DDp *(++pp)
(Figure 2.3-3)
++4Dp ++(#pp)
(Figure 2.3-4)
134 ARRAY OF POINTERS
pp PP
p p
a a
pp n Ppp
Ppp
Figure 3.1
MULTIDIMENSIONAL ARRAY 137
About array addresses. We have noted several times that the address of an array and the
address of the first element in the array have the same value. In this past puzzle, we saw
that a and al0] evaluated to the same address. One difference between the address of an
array and the address of the first element in the array is the type of the address and, hence,
the unit of arithmetic on an expression containing the address. Thus, since the type of a is
pointer to three-element-integer-array, the base type of a is three-element-integer-array and
a+1 refers to the next three-element-integer-array in memory. Since the type of al0] is
pointer-to-integer, the base type of al 0] is integer and al01]+1 refers to the next integer
in memory.
138 POINTER STEW
Figure 4.1
POINTER STEW 139
About pointers. If you can work this puzzle correctly then you know everything you will ever
need to about the mechanics of using pointers. The power of pointers lies in their
generality: we can chain them together to form an endless variety of complex data
structures. The danger of pointers lies in their power: complex pointer chains are seldom
readable and even more seldom reliable.
140 POINTER STEW
cpp
cp
Structures 1.]
s1
ble]a
OQ
nN
Figure 1.1
142 SIMPLE STRUCTURE, NESTED STRUCTURE
Structures 1.2
Figure 1.2-1
Figure 1.2-2
SIMPLE STRUCTURE, NESTED STRUCTURE 143
Structures 1.3
s1
= alien
: dje] £/8|
Figure 1.3-1
Figure 1.3-2
144 SIMPLE STRUCTURE, NESTED STRUCTURE
Structures 1.4
Structures 1.5
Structures 2.1
Figure 2.1
146 ARRAY OF STRUCTURES
Structures 2.2
Figure 2.2-1
ARRAY OF STRUCTURES 147
Figure 2.2-3
148 ARRAY OF STRUCTURES
Structures 2.3
Figure 2.3-1
ARRAY OF STRUCTURES 149
Figure 2.3-2
150 ARRAY OF STRUCTURES
Structures 2.4
Figure 2.4-]
ARRAY OF STRUCTURES 151
Sei Ss
Figure 2.4-3
152 ARRAY OF POINTERS TO STRUCTURES
Structures 3.1
plo]
Pid
pl2]
Figure 3.1
ARRAY OF POINTERS TO STRUCTURES 153
Structures 3.2
(pl0])->s, (*p)->s, (**p).s These are all ways of saying the same
thing. (Figure 3.2-2)
p
plo] al0O]
pi 14
pl2] al1]
aiezs
p
plol al 0]
pl 1]
pl 2) a4]
aih2
Figure 3.2-2
154 ARRAY OF POINTERS TO STRUCTURES
Structures 3.3
p
plo] al0] 4 |a]b] c} a]
pit sip
pl2] al 1] s -e| f]a]nh]@)
sip
al2] s i] 5]«} 2]8
sip
Figure 3.3-1
ARRAY OF POINTERS TO STRUCTURES 155
plo]
D1)
al] ~s
sip
aelepaye
p[2] allah -& fel£{/g|h|Q|
sip
bf2] 5
= 189)
EIGIEIESC
Figure 3.3-2
pl[0]
pL.el
D102
Figure 3.3-3
156 ARRAY OF POINTERS TO STRUCTURES
Structures 3.4
p
plo] al0O]
pit)
Dies a let
al2]
p
plo] al0]
8 ee
0 9aeaad Ne Sas
at2]
Pp a |
plo] al0] s anieen
ca og
mile sip
pie! afi] s felfig{h/Q
sip
al2] sip
ESEIESESLS
Figure 3.4-3 (*(++(pl0]))).s
Preprocessor 1.1
Trt Bose. §
PRINT( x*FUDGE(2) ); To understand the effect of a
preprocessor macro, expand it in place.
(Ive
eek2-3 5. 159") Replace the formal parameter k by the
actual parameter. Surprise! First
multiply, then add (then truncate).
Beware! Macros can be a source of subtle trickery. Expanding a macro is strictly a matter
of replacing one string by another. The macro preprocessor knows next to nothing about C.
Most surprises can be avoided by adhering to a few conventions.
The unwanted interaction between the replacement string and its context in this problem is
avoided if FUDGE(k) is defined to be (k+3.14159).
THE PREPROCESSOR DOESN T KNOW C 159
Preprocessor 1.2
The call to PRINT2 may look like a single statement, but it expands to three. Only the first
PR is contained within the for loop. The second PR is executed following the loop, with
cel 150;
For this problem, using commas in place of the semicolons in the body of the PRINT
macros satisfies Convention 2.
160 THE PREPROCESSOR DOESN T KNOW C
Preprocessor 1.3
int x=1,yee3
PRINT3( MAX(x++,y),X,y )3
(1<2
? y: x++),
and x=2 Finally, evaluate.
(y)
2
(x++<y ? y : x++),X,y
(x++)
3, and x=4
x++ appears only once in the macro call but twice in the expansion, causing x to be
incremented sometimes by one and sometimes by two. The burden of protecting against
such unfortunate side effects can be placed either with the macro writer or the macro user.
Convention 3: Avoid macro bodies that can cause obscure or inconsistent side effects.
Convention 3A: Avoid expressions with side effects in macro calls.
In general, the problem of side effects in macros is quite tricky. Following Convention 3
often means copying arguments into local variables within the macro; this extra overhead
reduces the speed advantage of macro calls over function calls. Following Convention 3A
requires knowing when a routine has been coded as a macro rather than a function; at best,
this violates the notion of the routine as an abstraction, and at worst, the routine may be
rewritten causing the assumption no longer to be valid.
Preprocessor 2.1
Tee Sree
4]8
PRINT( -NEG(x) );
--a First substitute the macro replacement string
for the macro call. (As before, the PRINT
macro will not be expanded.)
--x, and x=0 Then substitute the argument in the call for the
one in the replacement string.
The macro replacement string is exactly those characters that follow the closing parenthesis
of the argument list. The trick in this puzzle is that the -a immediately follows the
parenthesis. Still, following Convention 1 by defining NEG(a) to be (-a) produces the
expected expansion. It is also a good practice to begin each replacement string with either a
tab or a space.
Preprocessor 2.2
PRINT( weeks(10080) )
((hours(10080)/24)/7)
(((10080/60)/24)/7)
1 Evaluate.
PRINT( days(mins(86400)) )
(hours(mins (86400) )/24) Expand the leftmost macro.
((mins(86400)/60)/24)
(( (86400/60)/60)/24)
1 Evaluate.
162 CAUTION PAYS
Preprocessor 2.3
TAB includes an open if statement. On expansion, the if consumes the following else.
For this problem, appending a null else clause to the TAB macro alleviates the difficulty.
(Notice that enclosing the macro replacement string in braces, i.e., making it a block, does
not solve the problem.)
About macros and functions. Very often a routine can be implemented using either a macro
or a function. The advantage of using a macro is that it will be executed faster since the
runtime overhead of a function call is avoided. The advantages of using a function are that
none of the tricky situations we’ve seen in the puzzles with macros will occur, and if the
routine is called several times, the implementation will probably require less memory. This
leads us to the final convention for using macros:
Convention 5: Keep macros simple. If you can’t keep a macro simple, make it a function.
APPENDICES
APPENDIX 1: Precedence Table
OPERATOR
primary: () [] ->.
TIUGUNE | sete re (an?) tS. Sal raSore
multiplicative: * / %
logical: 8.8
logical: 1
assignment: = += -= etc. right to left
left to right
The precedence table illustrates the relative precedence of operators. Precedence determines
the order in which operands are bound to operators. Operators receive their operands in order
of decreasing operator precedence.
To determine the relative precedence of two operators in an expression find the operators in the
OPERATOR column of the table. The operator higher in the list has the higher precedence. If
the two operators are on the same line in the list, then look at the corresponding
ASSOCIATIVITY entry. If it indicates ‘‘left to right’’, then the operator to the left in the
expression has the higher precedence; if it indicates “‘right to left’’, then vice versa.
165
ae
altet worobeniel ° rae
- es. re
5 A
é e
2 eee
pa is Fi j
=a % a On '
: Leo 4
ss , en a
j
ng Con a
Par
e ° > vat
. 2a
a!
@euc <s i
paw - 7 a a |
p moles | “7 ;
ne 1a -
@ 7 ) aac 40464 ‘i
5 §
APPENDIX 2: Operator Summary Table
operator restrictions
x+y sum of x and y if either operand is a
pointer the other must
be integralt
x-y difference of x less y if either operand is a
pointer the other must
be integral or a pointer
of the same base type
e Multiplicative
operator restrictions
X*Y product of x and y x, y must not be
pointer
x/y quotient of x divided x, y must not be
by y pointer
x%*y remainder of dividing x x, y must not be
by y double, float, or pointer
-x arithmetic negation of x, y must not be
x pointer
e Incremental
operator restrictions
x++ (x--) x x must be a reference
x is incremented to a numeric value ora
(decremented) after pointer
use
+ Integral stands for the types int, char, short, long, and unsigned.
167
168 OPERATOR SUMMARY TABLE
Assignment operators
operator restrictions
x=y y cast in the type of x, x, y may be any type
x gets the value of y but array
x op= y x op (y) cast in the x, y may be any type
type of x, x gets the but array or structure
value of x op (y)
restrictions
x&y bit by bit AND of x and
y; AND yields a 1 for
each place both x and
y havea 1, 0
otherwise
xIy bit by bit inclusive OR
of x and y; inclusive
OR yields a O for each
place both x and y
have a 0, 1 otherwise
ey bit by bit exclusive OR
of x and y; exclusive
OR yields a 0 for each
place x and y have the
same value, 1
otherwise
~x one’s-complement of
x; 1s become Os and
Os 1s
e Shift
operator restrictions
x<<y x left shifted y places, y must be positive and
the lowest y bits get 0s less than the number of
bits per computer word
x>>y x right shifted y places; y must be positive and
the highest y bits get less than the number of
Os for positive x, 1s or bits per computer word
Os depending on the
compiler for negative x
OPERATOR SUMMARY TABLE 169
operator restrictions
x&&y AND of x and y: 1 if result is of type int
both x and y are
nonzero, 0 otherwise
xily inclusive OR of x and result is of type int
y: Oif both x and y
are zero, 1 otherwise
e Equality
operator restrictions
x==y (x! =y) 1 if x is equal to (not result is of type int
equal to) y, 0
otherwise
e Conditional
operator restrictions
Kicavasrz y if x is nonzero, z
otherwise
170 OPERATOR SUMMARY TABLE
Address operators
operator restrictions
*X the value at the address x must be a pointer
contained in x cast in
the base type of x
&x the address of x x must be a reference
to a value
Type operators
operator restrictions
(type) x x cast in the type type x may be any
expression
sizeof x the size in bytes of x x may be any
expression
sizeof (type) the size in bytes of an
object of type type
Sequence operator
operator restrictions
x,y b x, y may be any
x is evaluated before y expression
APPENDIX 3: ASCII Table
In octal
In hexadecimal
ASCII (American Standard Code for Information Interchange) maps a set of control and
printable characters into a set of seven bit binary numbers. The tables above show the
correspondence between each character and its value. Generally, the characters below 040
octal (20 hexadecimal) are considered control characters and are not printable, though newline,
tab, formfeed, etc. are located here. 040 and above are the familiar printing characters. Digits
and letters are ordered in their natural way; 1 is before 2 and A is before B.
7
ety (Epis come
It
|ia s
vy “tw er ar Fe
an y® YA Or" Da
ba g é i<be p89 7
- a ae, @> 14 “i
aes 4 ate) Lee] &
S
> os +a
a 7
ins ,o%; el 2 _
ri |
p> 7 j rovirs
ee & »
Lp on a Puli a -@
rth & 7
a ; Parl: ¢@ 8
- ,
. “Th oe
_
APPENDIX 4: Type Hierarchy Chart
long
fl
unsigned
T
int. <— char, short
The type hierarchy chart illustrates the ordering of the arithmetic types. The execution of each
arithmetic operator in an expression yields a result with the type of its highest typed operand.
Similarly, when two quantities are compared by a relational operator, the lower typed operand is
cast in the type of the higher typed operand. The vertical arrows in the chart show the basic
ordering: double is the highest type, int the lowest. The horizontal arrows indicate the
automatic type conversions. That is, operands of type float are always converted to type
double before being considered in an expression. Likewise, operands of types char and
short are always converted to type int.
17S
ble ™alow. ia Geei oh edetar sti ieee
ae dutTceta oe «wee wind r® Of Sale neg
toed! ft) ee
(sper Gaewe § > CsI : o@
ewiien "y
ped AY ay, Teds Oh Of pete Gre GT yaeee So Z eae Lda
i @ Se 5028S BTSs 26! =a of! => oartane ® :
WD i ape ont aie ‘(a))y> are «€ wy —<—sz ;
as Ȏ5 oy UU Cae =i : ls ed Ԣ ay? Gy
>=) oe weae
Ornate am ORM,
‘y
if
PUZZLE BOOK
Puzzles for the C Programming Language
' ALAN R. FEUER
The puzzles in this book are designed to help the reader through step two. They will
challenge the reader’s mastery of the basic rules of C and lead the reader into seldom-
reached corners, beyond reasonable limits, and past a few open pits. In short, they
provide the reader with insight into C that is usually only gained through considerable
experience.
The C Puzzle Book is a workbook intended to be used with a C language textbook. The
book is divided into sections, each containing C programs that explore a particular aspect
of C. Accompanying detailed descriptions of how the programs work are tips and caveats
for writing successful C programs.
ISBN 0-13-109%5eb-4