You are on page 1of 218
AFHRMA BAB (3 FG) poo SECOND EDITION THE “si Coon ANSWER BOOK Solutions to the Exercises in The C Programming Language,second edition | by Brian W. Kernighan & Dennis M. Ritchie | at Wie Be f) JJ >) Rh fe S (S—hn) __CLOMIS TONDO SCOTT. GINPEL an + PRENTICE HALL an SEEey Not ~ tay The Answer Book Second Edition Solutions to the Exercises in The C Programming Language, Second Edition by Brian W. Kernighan and Dennis M. Ritchie _C Ei (he) ARS EO . ‘lovis-L2Tordo Kot E--Gipipel wna Prentice-Hall International, Inc. / GDREF 1585 / qe tn gf Spur ee The C answer book: solutions tothe exertises in The C programming lan- guage, second edition, by Brian W. Kernighan and Dennis M. Ritchie/ Colvis L. Tondo, Scott E. Gimpel. ~ 2nd ed. €1989 by PTR Prentice Hall, Original edition published by prentice Hall, Inc. , a Simon &. Schuster Com- pany. Prentice Hall 2S r] SAGE 427% oh ROGEL HE PB HK Ay C78 da TT SEK RY 1 AM a PH DA NT A BE EDA ADEA BS AE RSME BHR REDE AAD RRR Al. APBHDWA Prentice Hall MEM HRE KRESS. ACR RL BLS BIC. 01-97-1275 BB 2h B (CLP) RE C RFF RET TFB — Mi) >I IRAE» EX / (HI & (Tondo .C. L. ), (38) & DUAR (Gimpel »S.E. 3% . — EVAR . — AE AR Hh EAL» 1997. 11 KET FA AB ISBN 7-302-02728-5 1.C 1. OW Of 1.CHA- BF RTM IV. TP312-44 ft Bl AS PAL 5 CIP BB EF (97) 5 23936 F BERR: TRAE ACP ah RAL Ce SE EE BE A HB He 100084.) BUS SLE. www. tup. tsinghua. edu. cn EU a REA ET RAS: REDE REAR ARR FF A, 850X1168 1/32 Pak: 6.75 fi WK: 1997 E11 AS 1M 1998 4F 7 A 2 EDR 48 : ISBN 7-302-02728-5/TP + 1414 Fl %. 5001~10000 Oh: 12.00% HH AR BT Df BUNA AE BEI AE YT Be — PB A A BALAN A AT HE GE LT BE HE SP Es Se ES LSS HIREZ A ZC ES LARAMIE. TE PART fe TAB A SB SL FTE FS Fi By ER AAT A AY BEB SP SC EAL SR A FEI). AFL ERB A KPA KN FRE SWEAR. EK eT SUR RAY BY ALE BER Ay RS BER EL SW BER Z Sb SR TE Ee NRE TRB BR ER ST BER BY SEHR APES BER ee FRADE To 7 Ait EBL BLE Ar TB TP A HY BT EFT HR. PURE HH RBEAY 6 ASS SEB A AY RM A HR TR ER SDE SEE BIG. HE MAT AHH. Prentice Hall 205] AUR BA BH ALI OK A PEE BBB HE ZK OF BS SL A FREE POV A ABER TB FLAS ABD ek eT EBA BL SPER RM Te AE Prentice Hall 27] 1997.11 Preface This is an ANSWER BOOK. It provides solutions to all the exercises in The C Programming Language. second edition, by Brian W. Kernighan and Dennis M. Ritchie (Prentice Hall, 1988)* The American National Standards Institute (ANSI) produced the ANSI standard for C and K&R modified the first edition of The C Programming Language. We have rewritten the solutions to conform to both the ANST standard and the second edition of K&R. Careful study of The C Answer Book, second edition. used in conjunction with K&R, will help you understand C and teach you good C programming skills. Use K&R to learn C, work the exercises, then study the solutions pre- sented here. We built our solutions using the language constructions known at the time the exercises appear in K&R. The intent is to follow the pace of KAR. Later, when you learn more about the C language, you will be able to provide possibly better solutions. For example. until the statement A (expression) statement-1 else statement-2 is explained on page 21 of K&R, we do not use it. However, you could improve the solutions to Exercises 1-8, 1-9, and 1-10 (page 20 K&R) by using it. At times we also present unconstrained solutions. We explain the solutions. We presume you have read the material in KQR upto the exercise. We try not to repeat K&R. but describe the highlights of each solution. You cannot learn a programming language by only reading the language constructions. It also requires programming —writing your own code and study- *Hereafter referred to as K&R, ing that of others. We use good features of the language, modularize our code, make extensive use of library routines, and format our programs to help you see the logical flow. We hope this book helps you become proficient in C. We thank the friends that helped us to produce this second edition: Brian Kernighan, Don Kostuch, Bruce Leung, Steve Mackey, Joan Magrabi, Julia Mistrello, Rosemary Morrissey, Andrew Nathanson, Sophie Papanikolaou, Dave Perlin, Carlos Tondo, John Wait, and Eden Yount. Clovis L. Tondo Contents Preface v Chapter 1. A Tutorial Introduction 1 Chapter 2. Types, Operators, and Expressions 43 Chapter 3. Control Flow ES) Chapter 4. Functions and Program Structure Co) Chapter 5. Pointers and Arrays ” Chapter 6. Structures 151 Chapter 7. Input and Output 168 Chapter 8. The UNIX System Interface 188 Index 203 cHapteR1 A Tutorial Introduction Exercise 1-1: (page 8 K&R) Run the “hello, world” program on your system. Experiment with leaving out parts of the program to see what error messages you get include ¢stdio.h> main « printfC*hello, world"); > In this example the newline character (\n) is missing. at the end of the line. Winclude ¢stdio-h> main { printfC*hello, world\ > This leaves the cursor In the second example the semicolon is missing after print (2. Individual C statements are terminated by semicolons (page 10 K&R). The compiler should recognize that the semicolon is missing and print the appropriate message ‘include ¢stdio.h> main 4 printfCthello, world\n‘); > 2 The C Answer Book In the third example the double quote “ after \n is mistyped as a single quote. The single quote, along with the right parenthesis and the semicolon, is taken as part of the string. The compiler should recognize this as an error and com- plain that a double quote is missing, that a right parenthesis is missing before a right brace, the string is too long, or that there is a newline character in a string. A Tutorial Introduction == Chap. 1 3 Exercise 1-2: (page 8 K&R) Experiment to find out what happens when print ’s argument string contains \c, where c is some character not listed above. Finclude ¢stdio.h> main? 4 printfCthello, world\y")5 prantf(hello, world\7" prantf(*hello, world\7"; > ‘The Reference Manual (Appendix A, page 193 K&R) states If the character following the \ is not one of those specified, the behavior is undefined. The result of this experiment is compiler dependent. One possible result might be hello, worldyhello, world is a short beep produced by ASCII 7. It is possible to have \ followed by up to three octal digits (page 37 K&R) to represent a character. \7 is specified to be a short beep in the ASCII character set. 4 The C Answer Book Exercise 1-3: (page 13 K&R) Modify the temperature conversion program to print a heading above the table. finclude ¢atdio.h> /* print Fabrenheit-Celsius table for fahr = 0, 20, . . ., 300; floating-point version +/ main@? t float fahr, celsius; int lower, upper, step; lower + 0; /* lower limit of temperature table #/ upper = 300; /* upper limit ” step + 20; /* step size “ printf(*Fahe Celsius\n™); fahr = lowers while Cfahr ¢* upper) ¢ celsius = (5.0/9.0) © Cfahr-32.09; prantf("%3.0f 26.1f\n", fahr, celsius); fahr + fahr + step; , ‘The addition of printf("Fahr Celsius\n"); before the loop produces a heading above the appropriate columns. We also added two spaces between %3.0f and %6. 1 to align the output with the head- ing. The remainder of the program is the same as on page 12 K&R. ‘A Tutorial Introduction Chap. 1 5 Exercise 1-4: (page 13 K&R) Write a program to print the corresponding Celsius to Fahrenheit table. sinclude 7* print Celsius-Fahrenhert table for celsius * 0, 20, ..., 300; floating-point version +*/ main « float fahr, celsius; int lower, upper, step; lower = 5 /* lower Limit of temperature table */ upper = 300; /* upper Limit ” step = 20; /* step size s printf(*Celsius Fahr\n"); celsius = lower while (celsius <* upper) ¢ fahr = (9.0ecelsius) / 5.0 + 32.05 prantfO"n3.0f 26.14 \n", celsius, fahrd; celsius * celsius + steps » ‘The program produces a table containing temperatures in degrees Celsius (0- 300) and their equivalent Fahrenheit values. Degrees Fabrenheit are calculated using the statement: fahr + (9.0ecelsius? / 5.0 + 32.0 The solution follows the same logic as used in the program that prints the Fahrenheit-Celsius table (page 12 K&R). The integer variables lower, upper, and step refer to the lower limit, upper limit, and step size of the variable celsius, respectively. The variable celsius is initialized to the lower limit, and inside the whi Le loop the equivalent Fahrenheit temperature is calculated ‘The program prints Celsius and Fahrenheit and increments the variable celsius by the step size. The whi le loop repeats until the variable celsius exceeds its upper limit 6 The C Answer Book Exercise 1-5: (page 14 K&R) Modify the temperature conversion program to print the table in reverse order, that is, from 300 degrees to 0. Finclude ¢stdio.h> /* print Fahrenheit-Celsius table in reverse order ” mang) ‘ ant fahi for (fahr + 300; fahr >= 0; fahr = fahr - 20) printfC"%3d t6.1f\n", fahr, (5.0/9. 009 fahr-32)95 The only modification is: for Cfahr * 300; fahr >= 3 fehr + fahr - 20) The first part of the for statement, fahe = 300 initializes the Fahrenheit variable (fahr) to its upper limit. The second part, or the condition that controls the for loop, fahr >= 0 tests whether the Fahrenheit variable exceeds or meets its lower limit. The for loop continues as long as the statement is true. The step expression, fahr + fehe - 20 decrements the Fahrenheit variable by the step size. A Tutorial Introduction Chap. 1 7 Exercise 1-6: (page 17 K&R) Verify that the expression getchar(> !+ €0F is Qor | vinclude ¢staio.n> main « int es while Cc + getchar€> t= EOF) prantfC"ad\n", 5 printfO"%d - at EQF\n", ©); > The expression © + getchar(> t+ EOF is equivalent to c= Cgetchar@) t EOF) (page 17 K&R). The program reads characters from the standard input and uses the expression above. While getcher has a character to read it docs not return the end of file and getchar() != EOF is true. So 1 is assigned to «When the program encounters the end of file, the expression is false. ‘Then 0 is assigned to ¢ and the loop terminates. 8 The C Answer Book Exercise 1-7: (page 17 K&R) Write a program to print the value of EOF. sinelude ¢stdio.h> maine ‘ print€C"EOF is Kain", €OFDs , The symbolic constant EOF is defined in . ‘The EOF outside the double quotes in printf is replaced by whatever text follows adefine EOF in the include file. In our system EOF is —1, but it may vary from system to system. That's why standard symbolic constants like EOF help make your pro- gram portable. A Tutorial Introduction Chap. 1 Exercise 1-8: (page 20 K&R) Write a program to count blanks, tabs, and newlines, Ainclude J+ count blanks, tebs, end newlines main « int c, mb, nt, nls nd = 05 7+ number of blanks at = 05 J+ number of tabs nl = 05 J+ number of newlines while (Co + getchar€>) '*£0F> 4 Wh lee tt) venbs af Cee NED vents if Coss *NntD tents ? printf("%d 2d Xd\n, nb, nt, lds , ‘The integer variables nb, nt, and ni are used to count the number of blanks, tabs, and newlines, respectively, Initially, these three variables are set equal 100. Inside the body of the wh 1e loop. the occurrence of each blank, tab, and newline from input is recorded. All 1f statements are executed each time through the loop. If the character received is anything but a blank. tab, or newline, then no action is taken. If it is one of these three, then the appropriate counter is incremented. The program prints the results when the whi Le loop terminates (get char returns EOF). ” ” ” ” 10 ‘The C Answer Book The 1 f-e1se statement is not presented until page 21 K&R. With that knowledge the solution could be: finclude ¢atdio.h> 7* count blanks, tabs, and newlines main? 4 ant c, mb, nt, ni; nb = 0; J+ number of blanks at = 0; J+ number of tabs nl = 0; J+ number of newlines while (Ce + getchar(>) !=€0F> Af (one tf) renbs elseif (cos \t> vents else if (oo#* ‘\nd tens printf(*td Xd td\n", nb, nt, nds ” ” ” ” A Tutorial Introduction Chap. 1 " Exercise 1-9: (page 20 K&R) Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank include Catdio.h> sdefine NONBLANK ‘a’ /* replace string of blanks with a single blank ” main 4 int ¢, laste: leste = NONBLANK; while (Le = getcharG)) t+ EOF) ¢ ite te putchar(cd; afore 1) af Glaste #00 putcharCe): laste * 5 The integer variable e records the ASCII value of the present character received from input and Leste records the ASCII value of the previous character. The symbolic constant NONBLANK initializes laste to an arbitrary nonblank char- acter. ‘The first 1 £ statement in the body of the whi 1e loop handles the occurrence of nonblanks~it prints them. The second if statement handles blanks, and the third 1 statement tests for a single blank of the first blank of a string of blanks. Finally, laste is updated. and the process repeats. 2 The C Answer Book ‘The 1 f-e1se statement is not presented until page 21 K&R. With that knowledge the solution could be: Fincluce ¢stdio.h> ‘define NONBLANK * /* replace string of blanks with a single blank ” main@> « int c, laste; laste + NONBLAN while (Co = getchar(>) != EOF) ¢ fete ty putchar(eds else if Caste te 11) putchar(e); laste = cs > The logical 08 (11) operator is also not presented until page 21 K&R. With that knowledge the solution could be: #include ‘#define NONBLANK ‘a’ /* replace string of blenks with @ single blank ” main 4 int _c, laste; laste + NONBLANK; while (Ce * getchar()) t= EOF) ¢ fe tet 4 Gt laste te tt) putchar(e)s laste = cy A Tutorial Introduction Chap. 1 3 Exercise 1-10: (page 20 K&R) Write a program to copy its input to its output, replacing each tab by \t, each backspace by \b, and each backslash by \\. This makes tabs and backspaces visible in an unambiguous way #include ¢stdio.h> (+ replace tabs and backspaces with visible characters ” main « int ¢} while (Cc * getchar€)) != EOF) ¢ af Coe NED printfor\\t ys af Comm \bt printf Af Cee TD printfOry yy af Ce fe NBD if Ce te ED Af Ge be ND putcharce): Ns » The character received from input can be a tab, a backspace, a backslash, or anything else. If itis a tab we replace it with \t, a backspace with \b, and a backslash with \\. Anything else is printed as is. A backslash character is represented as *\\" in C. We print two back- slashes by passing the string "\\\\" to printf 4 The C Answer Book The 1 f-else statement is not presented until page 21 K&R. With that knowledge the solution could be: #include 7+ replace tabs and bactspaces with visible characters ” main) « int o5, while (Co = getchar(>) ' EOF) Af (eee NED prantfcnyty; else if Cc s* ‘\bt) PrintfOy\beDs else if Cows \\D printEceyy yi; else putchar(c); A Tutorial Introduction Chap. 1 6 Exercise |: (page 21 K&R) How would you test the word count program? What kinds of input are most likely to uncover bugs if there are any’? To test the word count program first try no input. The output should be: 0.00 (zero newlines. zero words, zero characters). Then try a one-character word. The output should be: 1 1 2 (one newline, one word, two characters—a letter followed by a newline character) ‘Then try a two-character word. The output should be: 1 1 3 (one newline, one word. three characters—two characters followed by a newline character). In addition, try 2 one-character words (the output should be: 1 2 4) and 2 one-character words—one word per line (the output should be 2 2 4) The kinds of input most likely to uncover bugs are those that test boundary conditions. Some boundaries are —no input —no words—just newlines —no words—just blanks, tabs, and newlines —one word per line—no blanks and tabs —word starting at the beginning of the line —word starting after some blanks 16 The C Answer Book Exercise 1-12: page 21 K&R) Write a program that prints its input one word per line. include ¢stdio.h> fdefine IN 4 74 inside @ word ” ‘#define DUT 0 7* outside @ word ” 7+ print input one word per line ” mainc> 4 inte, states state + OUT; while (Co + getchar(>) t* EOF) ¢ Af Core tt cee nt ttc ee NED ¢ if Cstate ++ IND 4 putchare*\n); /* finish the word / state = QUT; > Dd else if (state += OUT) ¢ state = IN; 7+ beginning of word +/ putchared; else ss inside a word of putcharte); , state is an integer boolean that records whether the program is currently inside a word or not. At the beginning of the program, state is initialized to OUT, since no data has been processed. ‘The first if statement Af Cone tbe ee Nat the we MRD determines whether c is a word separator. If itis, then the second 1 f statement, Af Cstete ++ IND determines whether this word separator signifies the end of a word. If so, a newline is printed and stote is updated; otherwise no action is taken. If c is not a word separator, then itis either the first character of a word Of another character within the word. If it is the beginning of a new word, then the program updates state. In either case, the character is printed. A Tutorial Introduction Chap. 1 W7 Exercise 1-13: (page 24 K&R) Write a program to print a histogram of the lengths of words in its input. It is easy to draw the histogram with the bars horizontal: a vertical orientation is more challenging. finclude ¢stdio.h> fdefine MAXHIST 15 7+ max length of histogram —+/ fdefine MAXWORD 11 s+ max length of 2 vord ” sdefine IN 1 J+ inside a word ” fdefine QUT 0 /* outside a word ” /* print horizontal histogram ” main « int c, 4, ne, state: ant lens y+ Length of each bar ” int maxvalue; s+ maximum value for wi(l —«/ int ovflow; y+ number of overflow words +/ int wif MAarWORD) ; 1+ word length counters ” state = OUT; ne = 05 y+ number of chars ina word +/ ovflow = 0; y+ number of words >= MAXWORD#/ for Ci + 05 1 6 MAKWORD; ¢+1) with) = 05 while (Cc * getchar ©) t= EOF) ¢ Af Come at eee Wnt thc ee NED state = OUT if (ne > 0 af (ne « MAXWORDD sewh Enc); else srovf low; nc = 0; D else if (state «= OUT> « state = Is ne = 15 7+ beginning of a new word = */ } else tenes 7+ inside a word "7 y mexvalue = 03 for (1 = 1; 1 € MAXWORD; +91) af GulC1) > maxvalued maxvatue = wifi 18 The C Answer Book for C= ty 4 © MAKWORDS edt | printfOsd - XSd 2", 1, wl lads af Guilal > 0) ¢ af CClen © wif) © MAKHIST / maxvalue) <= 0) lens ty d else len = 05 while Clen > 0) ¢ putchar (/#7)5 --Len; > putchar€*\n‘)5 > af Covflow > 09 . printf("There are td words >* Xd\n", ovflow, MAXWORD); A blank, newline, or tab marks the end of a word. If there isaword(ne > 0) and its length is less than the maximum word length (nc _« MAXWORD), then the program increments the appropriate word length counter (++witne}). If the length of the word is out of range (ne >» MAXHORD), then the program incre- ments the variable ov low that keeps track of the number of words greater than or equal to MAXWORD. ‘When al! words have been read in, the program determines the maximum value (maxvalue) from the atray wi The variable Len scales the value in w1C1 according to MAxHIST and maxvalue, When w1(41 is greater than 0, at least one asterisk is printed +include (stdio.h> fdefine MAXHIST 15 7+ max length of histogram */ #oefine MAXWORD = 11 7* max length of a word ” ‘define IN 1 7+ inside @ word ” fdefine OUT 0 /* outside @ word ” /* print vertical histogram ” mand? « Ante, 1, J) ne, states int maxvelues + maximum value for wif] */ int ovflow; 7+ number of overflow words */ int wI(M@xWORD); 7+ word length counters ” A Tutorial Introduction Chap. 1 3 state = OUT; ne = 05 {+ number of chars in a word wv ovflow + 0; 7+ number of words >= MAXHORD = +/ for (i = 0; 1 € MAXWORD; +419 wits) * 05 while Cle + getchar()) t* EOF) ¢ Af (ose 6 4 tbe ae \nt state = QUT; af Cnc > 09 Af Cre © MAKWORDD sewl ned: else eee tty ¢ trovtlows + else if (state =* OUT) ¢ state = IN; nes 15, 7* beginning of a new word ” > else tenes /* inside a word ” , maxvalue = for (1 © 1; 1 € MAXWORD; +4 if GwlCil > maxvalued maxvelue = wl(i); for (i * MAXHIST; 4 > 0; --1) ¢ for Cj #15 ) © MAXWORD; +44) Af GwlC}1 © MAXHIST / mexvalue >= 1) printfe © ">; else printfc* putchar*\n"); } for Ci = 15 4 € MAXWORD; +01? printfOead "95 putchar(‘\n?; for Gio= 15 i € MAXWORDS +44) printfomtad , wilt); putchar(*\n‘)5, af Cevflow > 09 printfC"There are Xd words >* Xd\n", ovflow, MAXWORD?; > This solution prints a vertical histogram. It is similar to the previous program until maxvelue is determined. ‘Then it is necessary to scale cach element of the array wi and verify whether an asterisk should be printed for each one of the elements. This verification is necessary since all bars are printed simulta- neously (vertical histogram). The last two fr loops print the index and value for each element of wi 20 Exercise 1-14: (page 24 K&R) The C Answer Book Write a program to print a histogram of the frequencies of different characters in its input include include fdefine MAXHIST ‘Adefine MAXCHAR 15 128 /* print horizontal histogram mainQ) « int iat, int, int ea len maxvalue; ec CMAXCHAR] ; for Ci = 0; i eclil = © MAXCHAR; 0; 7 max length of histogram /* max different characters freq. of different characters 7+ Length of each bar 7+ maximum value for ccl) /* character counters ee while (Ce + getchar)) = EOF) Af Co © MAXCHARD recto]; maxvalue * 0; for Ci = 15 2 ¢ MAXCHAR; we if Coclil > maxvalued maxvalue = celils for Ci = 4; 4 € MAXCHAR; af CisprintGi> PrintfCMASd - Xe - xsd: *, else printfonasd - af Geel) > 0) ¢ eer Ay by colisrs - Sd: a, colina; if C€len = cefi) # MAXHIST / maxvalued ¢* 0 len = 15 > else len + 05 while Cien > 0) ¢ putchar€*#*); -=Len; y putchar(*\n* ” ” ” ” ” ” A Tutorial Introduction = Chap. 1 a ‘This program is similar to the horizontal histogram in Exercise 1-13, Now we are counting the frequency of different characters. We use an array character counter of MAXCHAR entries and we ignore characters greater than or equal to MAXCHAR if they exist in the character set being used. The other difference is that we use a macro to determine if a character is printable. The include file is discussed on page 43 K&R. isprint is described on page 249 K&R (Appendix B: Standard Library) 22 The C Answer Book Exercise 1-15: (page 27 K&R) Rewrite the temperature conversion program of Section 1.2 to use a function for conversion #include float celsius(float fahr); J+ peant Fahrenheit-Celsius table for fahr = 0, 20, 300; floating-point version “ maing? 4 float fahr; int lower, upper, ste lower = 05 J+ lower limit of temperature table +/ upper = 300; Ze upper limit “ step > 20; + step size ” fahr = lowers while Cfahr <* upper) « printf("%3.0f 16.1f\n", fahr, celsius(tanrd); fahr = fahr + step; /* celsius: convert fahr inte celsius ” float celsius(float fanr? « return (5.0/9.0) © (fanr-32.0); » We use a function to convert Fahrenheit into Celsius. ‘The name of the function is celsius, it receives a floating-point value, and it returns another floating- point value. The function returns the value of the expression via the return statement. Sometimes the expression is a simple variable, as in the function power (page 26 K&R). Sometimes we use a more involved expression, as in the function celsius, because all the work can be done in the return state- ment. We declared float celsiusCfloet fared; because the function expects a floating-point value and returns another flo: point value after the conversion A Tutorial Introduction Chap. 1 Exercise 1-16: (page 30 K&R) 23 Revise the main routine of the longest-line program so it will correctly print the length of arbitrarily long input lines, and as much as possible of the text. include ¢stdio.h> sdefine MAXLINE 1000 J maximum input line size int getlineCchar Iinef}, int maxlineds void copyCchar tol], char from(1); /* print longest input line mainc) « int lens 7+ curcent line length int ma 7* maximum length seen so far enar Line(MAXLINE]; /* current input line char longest(MAXLINE]; /+ longest iine saved here max * 05 while CClen * getlineCline, MAXLINE)) >» 0) ¢ printf"ad, Xs", len, Lined; if Clen > max? ¢ mex + lens copy(longest, line); » > if Cmax > 09 Js there was a line praintf(™ts", longest); return 05 > 7+ getline: read @ line into s, return length Ant getline€cher s(1, int lim? 4 antic, 4, 45 105 for Ci = 0; Ce = getchar€)) 'EOF th c t= ‘Xn’; 94> AAG © Lam-2) 4 styles 7+ Vine still in boundaries wey ” ” ” ” 7 ” ” ” ” 24 The C Answer Book if Come mtd € styl + ey vas > aly) = N05 return a3 > /* copy: copy ‘from’ into ‘to’; assume to is big enough */ void copy Cehar tol], char from(1) « ant as A205 while CCtoCil = fromtild $= *\04) > The only revision in the main routine is printf("zd, Zs", len, lined; This prints the length of the input fine (1en) and as many characters as it is possible to save in the array line. ‘The function get 1ine has a few modifications. for (1 + 0; Ce * getehar()) t+ EOF sec He "Wnty e017 The test for the limit of characters is not performed in the for loop anymore. This limit is not a condition for termination because get1 ine now returns the Jength of arbitrarily long input lines and saves as much text as possible. The statement af G. © Lim-2> determines if there is room in the array (still within boundaries). The original test in the for loop was 4 limt It was changed because the last index of the array + is Lam-t since s has 1im elements and we already read the input character. So 1 Lime? A Tutorial Introduction Chap. 1 2 leaves room for a possible newline character sClim-2) * ‘\n‘é and an end of string marker atlim-1) = *\0+ The length of the string is returned in copied to the string s. +: ) keeps track of the number of characters 26 ‘The C Answer Book Exercise 1-17: (page 31 K&R) Write a program to print all input lines that are longer than 80 characters. Ainclude fdefine MAXLINE 1000 /* maximum input line size */ ‘define LONGLINE 80 int getiineCchar line(1, int maxlineds /* print lines longer than LONGLINE ” maine? « int lens (+ current line length +/ char linelMAXLINE); fs current input line */ while CClen = getlineCline, MAKLINED? > 0) af Cen > LONGLINED printfCts", Lined; return 0; , ‘The program invokes getiine to read an input line. getline returns the length of the line and as much text as possible. If the length is greater than 80 characters (LCNGL INE), then the program prints the input line. Otherwise, no action is taken, The loop repeats until get 1 ine returns length zero. ‘The function get ine is the same as in Exercise 1-16 A Tutorial Introduction Chap. 1 Exercise 1-18: (page 31 K&R) Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines. ‘include ¢stdio.h> define MAXLINE 1000 (+ maximum input line size +/ int getlineCchar line}, int maxlined; int removeCchar s(1); J+ remove trailing blanks and tabs, and delete blank lines +/ main « char Line(MAXL INE]; J+ current input line ” while CgetlineCiine, MAXLINE) > 03 1f CremoveCline) > 0) printfOmrs™, lined; return 0; , 7+ remove trailing blanks and tabs from cheracter string 3 */ Anat remove (char sti) « int is 2 05 while (stil '* ‘\nt) /# find newline character ” nay 7+ back off from ‘\n* 7 while Ci >* 0 bb Csi] ee 4 1 EE Stal we “RODD nas if >= 0d ¢ 7* as at a nonblank Line? ” sed stil \nty 74 put newline character back */ ey stil = ‘N05 /* terminate the string ” ? return 15 The remove function removes trailing blanks and tabs from the character string Line and returns its new length. If this length is greater than 0, Line has characters other than blanks and tabs, and the program prints the line. Other- wise, Line is entirely made up of blanks and tabs, and thus ignored. This ensures that entirely blank lines are not printed. 2 The C Answer Book The remove function finds the newline character and backs off one po- sition. The function then steps backward over blanks and tabs until it finds some other character or no more characters are available (1 < 0). Ifa > 0, then there is at least one character. remove puts back the newline and the end of string marker, then returns 1. ‘The function get Line is the sam in Exercise 1-16. A Tutorial Introduction Chap. 1 2 Exercise 1-19: (page 31 K&R) Write a function rever se(s) that reverses the character string 5. Use it to write a program that reverses its input a line at a time. finclude ‘define MAXLINE 1000 7+ maximum input line size */ ant getline€char line(}, int maxiined; void reverse(char s(1); J+ reverse input lines, a line at a time ” mang? « char Line(MAXLINE; 7+ current input line ” while (getline(line, MAXLINE? > 0) ¢ reverseCline); printecrxs", lineds > /* reverse: reverse string 3 “” void reverseCchar sf) « ant ayy char tem i= 05 while Cs(i} '* ‘\0') ss find the end of strings ¢/ ae 7* back off from ‘\0" ” if Galil s* 4\nt> nay 7* leave newline in place v 1705 7* beginning of new string 5 #/ while C) € 19 ¢ temp + s(j35 sty) + sts y+ swap the characters ” sti) = temps may eps , The reverse function first finds the end of string s. It then backs off one position from *\0*, so that the first character will not become an end of string 30 ‘The C Answer Book marker, If a ‘\n! exists, it backs off one more position, since like the *\0", it must remain at the end of the line. | is the index of the first character of the string, and 4 is the index of the last character of the string. While swapping characters, j is incremented (moves toward the end of the string). and 1 is decremented (moves toward the beginning of the string). The process continues while } is less than 1. The main program reads a line of input at a time, reverses it, and prints the reversed line. The function getline is the same as in Exercise 1-16. A Tutorial introduction Chap. 1 a Exercise 1-20: (page 34 K&R) Write a program detab that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns. Should n be a variable or a symbolic parameter? @include adefine TABINC 8 J+ tab increment size ” /* replace tabs with the proper number of blanks ” mainc> { int ©, mb, pos; nb = 05 7+ number of blanks necessary ” pos = 7* position of character in line */ while (Ce * getchar€>> != EDF) ¢ Af Ce ee NED 4 7+ tab character ” nb = TABINC - (pos - 1) % TABINC; while Gnb > 09 ¢ putchar(’ 7); *eposs ==nb5 , d else if (cs ‘\n’ ¢ /* newline character ” putcharceds pos = 15 > else ¢ Je all other characters +/ putcharced;, *sposs > ‘The tab stops are at every TABINC position. In this program TABINC is defined to be8. The variable pos is the position within a line of text where the program currently is. If the character is a tab, the program calculates the number of blanks nb necessary to reach the next tab stop. The statement nb * TABINC - (pos - 1) % TABINC determines this value. If the character is a newline, then it is printed out and pos is reinitialized to the beginning of the line (pos = 1). Anyother character is printed and pos is incremented (++pos). 32 ‘The C Answer Book TABINC is a symbolic constant. Later on, in Chapter 5, you will lean how to pass arguments to the main program and you may want to allow the user to set the number of columns between tab stops. Then you may want to change TABINC into a variable. The program deteb is extended in Exercises 5-11 and 5-12. A Tutorial Introduction Chap. 1 33 Exercise 1-21: (page 34 K&R) Write the program enteb that replaces strings of blanks by the minimum number of tabs and blanks to achieve the same spacing. Use the same tab stops as for detab. When either a tab or a single blank would suffice to reach a tab stop, which should be given preference? include ‘define TABINC 8 7+ teb increment size ¢/ /* replace strings of blanks with tabs and blanks ” main? { int c, nb, nt, poss nb = 05 70 @ of blanks necessary */ nt = 95 J+ # of tabs necessary */ for (pos * 1; Ce = getchar€>) = EOF; ++pos) afc OE if (pos % TABING t= 0) 7+ Anecement # of blanks #/ nb = 05 J+ reset # of blanks #/ tents 7» one more tab ” > velse ¢ for © 5 nt > 0; --nbd putchar(‘\t">; — /* output tabCs> ” if Cem NED 7+ forget the blank(s) —#/ nb #05 else 7+ output blankts> ” for ©; mb > 03 --nb> putchart? "9; putcharce)5 Af Ces \nt> pos = 0; else if Cc == ‘\tt) pos = pos + CTABINC - Cpos-1) % TABINC) - 1; > The integer variables nb and nt are the minimum number of blanks and tabs necessary to replave a string of blanks. The variable pos is the position within a line of text where the program currently is ‘The idea is to find all blanks. A string of blanks is replaced by a tab every time pos reaches a multiple of TABINC. “ The C Answer Book When the program finds a nonblank, then it prints the tabs and blanks accumulated followed by the nonblank character. The program resets nb and nt to zero, and resets pos to the beginning of the line if the current character isa newline. If the nonblank character is a tab, then the program prints only the ac- cumulated tabs followed by the current tab character. When a single blank suffices to reach a tab stop, it is easier to replace it with a tab because we avoid special cases. The program entab is extended in Exercises 5-11 and 5-12. A Tutorial Introduction Chap. 1 Exercise 1-22: (page 34 K&R) ‘Write a program to “fold” long input lines into two or more shorter lines after the last nonbiank character that occurs before the n-th column of input. Make sure your program does something intelligent with very long lines, and if there are no blanks or tabs before the specified column. finclude ‘define MAXCOL 10 7+ maximum column of input rdefine TABINC = 8 7+ tab increment size char line(MAXCOL}; y+ input Line int exptabCint pos); int findbInkCint pos); int newposCint pos); void printiCint pos); 7/* fold long input lines into two or more shorter lines main? t : int ©, poss pos + 05 /* position in the line while CCc + getchar€>) !+ EOF) ¢ Line(pos} = ci J+ store current character af tose Nt [+ expand tab character pos * exptab(pos); else if Cos ‘\nt> { printl(pos); /* print current input line pos * 0; D else if Cespos >= MAXCOL? « pos + findbink(pos); printl (pos); pos * newpos(pos); > 7 printl: print Line until pos column void printiCint pos? « ant a5 ” ” ” ” ” ” ” ” ” Fy ‘The C Answer Book for (1 = 0; 1 € poss e+d putcharClinelil> Af (pos > 09 7* ny chars printed ? ” putchar(/\n?); , /* expteb: expand tab into blents ” int exptabCint pos) € Line(post = * '; J* tab ta at least one blank +/ for (+spos; pos < MAXCOL 4&4 pos % TABINC f= 0; +pos) linetpos} = * Af (pos © MAXCOLD /* room left in current Line */ return pos; else ¢ /* current line is full ” printiCpos); return 0; /* reset current position — «/ > > 7* findbink: find blenk’s position ” Ant findbinkCint pos? { while (pos > 0 48 Jine(pos} te * +> --pos; tf (pos *= 09 7+ no blanks in the line 2? +/ return MAXCOL; else 7* wt least one blank ” return post; /* position efter the blank +/ > “+ -newpos: rearrange line with new position ” Ant newposCint pos) « amt ty js Af (pos €* 0 It pos >* MAxcaL? return 0; 7+ nothing to rearrange ” else 4 40; for Cj = poss } € MAXCOLs ++) 4 Lineal = Linety); wea; ) return 15 7+ new position in line ” A Tutorial Introduction Chap. 1 7 MAXCOL is a symbolic constant. It indicates the n-th column of input. The integer variable pos points to the position within a line of text where the program currently is. ‘The program folds input lines before the n-th column of input, ‘The program expands tab characters, prints the current input when it finds a newline, and folds the input line when pos reaches NAXCOL The function f indb Ink searches for a blank starting at the index pos. It returns the position after a blank or MAXCOL if a blank does not exist. printl prints the characters between position zero and pos-1. newpos rearranges a line, that is, it copies characters, starting at pos, 10 the beginning of the line, then returns the new value of pos. Exercise 1-23: (page 34 K&R) The C Answer Book Write a program to remove all comments from a C program. Don't forget to handle quoted strings and character constants properly. C comments do not nest. #include ¢stdio.h> void rcommentCint o3; void in_comment( void); void echo_quoteCint c); J* remove all comments from a valid C program maine? 4 inte, d while (Ce = getchar(>) != EOF? comment Cc); return 0; y /* rcomment: read each character, remove the comments void rcomment(int? <) 4 int d5 afte 7D Af (Cd © getchar€o) #* f#1> ” ” in_comment(); /*beginning conment+/ else if Cdas 174) ¢ putchar(e); reomment(d) delset putcharCe); 7+ not a comment putchar¢d); > else if Coe VO bh cee mr echo_quote(c); 7* quote begins putcharle?s 7+ not a comment Jeanother slash " ” ” ” A Tutorial introduction Chap. 1 39 /* An_comment: inside of @ valid comment ” void in_comment (void) ‘s prev character +/ {s cure character +/ thd t= 174) (fe search for end +/ d+ getchar Od; 7* echo_quote: echo characters within quotes ” void echo_quoteCint ¢) 4 int ds putchar(c); while (Cd * getcharQ)) t+ 6) (s+ search for end «/ putchartd); if (dae VD putchar(getchar(>); —/* ignore escape seqt/ > putchar(d); The program assumes that the input is a valid C program. rcomment searches for the beginning of a comment (/+) and when it finds it calls 1n_conment This function searches for the end of the comment. The procedure therefore ensures that a comment will be ignored. reomment. also searches for single and double quotes and if it finds them calls echo_quote. The argument to echo_quote indicates whether it is a single or double quote. echo_quote ensures that anything within a quote is echoed and not mistaken for a comment. echo_quote does not consider a quote following a backslash as the terminating quote (see the discussion on escape sequences on page 19 K&R and in Exercise 1-2). Any other character is printed as is. The program terminates when getcher returns an end of file. « ‘The C Answer Book Exercise 1-24: (page 34 K&R) Write a program to check a C program jor rudimentary syntax errors like un- balanced parentheses, brackets, and braces. Don’t forget about quotes, both single and double, escape sequences, and comments. (This program is hard if you do it in full generality.) @include ¢stdic.h> int brace, brack, paren; void in_quoteCint cj; void in_comment(void); void search(int cd; /* rudimentary syntax checker for C programs ” main@? « int es, extern int brace, brach, paren; while (Ce + getchar()) != EOF) 4 Af Gee 79 6 if (eo = getcharer? #= v6") in_comment (5 f+ anside comment — +/ else search(c); yelse if Cone AN th ce fry in_quotete); Is inside quote ” else search€e); Af Cbrace ¢ 0) ¢ fs output errors #/ prantfc"Unb brace = 0; else if Cbrack ¢ 0) ¢ printfC"Unbalanced brackets\n™); brack = 0; d else if Cparen < 09 ¢ printf("Unbalanced parentheses\n"); paren = 0; anced braces\n"); A Tutorial Introduction Chap. 1 a > /* search void 4 > Af (brace > 0) J+ output errors «/ Printf("Undalanced braces\n"); if Corack > 09 printf(*Unbalanced brackets\n"); if (paren > 0) printf("Unbalanced parentheses\n") rch for rudimentary syntax errors ” echCint ©) extern int brace, brack, pareny Af Cee td srbraces else if Co se OD >-brece; else if Cc * ssbrack else if Cc * ~-brack; else if Co ee 104) *oparens- else af Ce ee 00) ~-parens "D no 7 An_conment: inside of @ valid comment ” void in.comment (void? ‘ ante, di © = getchare>; J+ prev character «/ d= getchert>; J+ curr character = #/ while Co fe ‘#7 Id te 474) (Js search for end — #/ erd d= geteharcr; a ‘The C Answer Book 7+ in.quote: inside quote « void in.quoteCint 2) 4 int ds while’ Cd = getcher€)) != 6) 7s search end quote +/ Af doen NED getchar(); 7+ ignore escape seq */ > This solution is not done in full generality. ‘The program checks for three syntax errors: unbalanced parentheses brackets, and braces. Everything else is assumed to be valid. ‘The function search increments the variable brace when the character is a left brace (*«“) and decrements it when the characteris a right brace (‘> *) The variables bract and paren are handled similarly. During the search, it is legal for brace, brack, or paren to be positive orzero. It is an error if brace, brack, or paren ever becomes negative; the program prints an appropriate message. ( (( (brack equals 3) is legal for the moment because 3 balancing right brackets might be found later in the search. 33} (brack equals —3) is illegal because there were no previous left brackets to balance these 3 right brackets; if there were 3 left brackets to balance them, then brack should equal 0, The statements Af (brace ¢ 0) ¢ printfC"Unbalanced braces\n"); brace = 0; d else if Cbreck ¢ 0) ¢ printf("Unbalanced brackets\n"); breck = 0; d else if (paren ¢ 0) ¢ printfC“Unbalenced parentheses \n’ paren = 0; > are necessary because without them >¢ of 13){{{ or }}<{ would be considered balanced. ‘The main routine searches for comments, single and double quotes, and skips the characters within them. The braces, brackets, and parentheses inside comments and quotes need not be balanced. The program makes a final check upon EOF to determine if there are any ‘open braces, brackets, or parentheses. If so, the program prints an appropriate message. CHAPTER2 Types, Operators, and Expressions Exercise 2-1: (page 36 K&R) Write a program to determine the ranges of cher, short, int, and Long variables, both signed and unsigned, by printing appropriate values from standard headers and by direct computation. Harder if you compute them: determine the ranges of the various floating-point types finclude ¢stdio.h> #include 74 determine ranges of types ” main) ‘ /* signed types ” printf('signed char min = + SCHAR_MIND; printf('signed char max = SCHAR MAX); printfC'signed short min = SHRT_MIND ; printf("signed short max = SHRT_MAX); printf('signed int min = INT MIND 5 print#('signed int max = INT MAX); print#("signed long min = + LONG MIND; print#("signed long max = + LONG_MAXD; 7+ unsigned types “ printfCtunsigned cher mex UCHAR_max; printf(ungigned short max USHRT_MAX); printf(unsigned int max UINT MAX) ; printf(unsigned long mex ULONG_max9; “ The C Answer Book ‘The ANSI standard for C specifies that ranges be defined in <1imits..h>. ‘The ranges may vary from machine to machine because the sizes for short, int, and long vary for different hardware. sinclude ¢stdio.h> /* determine ranges of types ” main) < /* signed types " printfC"signed cher min = td\n™, ~(charCCunsigned char? “0 >> 1995 printfC"signed cher max = Xd\n",, (char (Cunsigned chard “0 >> 19); printfC"signed short min * Xd\n", ~Gshort €Cunsigned short) “0 >> 1995 "signed short max = %d\n", shor t)(Cunsigned short? “0 >> 19); signed int min = %d\n", ~CintdCCunsigned int) “9 >> 195 printfCsigned int max + %d\n", CintCCunsigned int? “0 >> 199; printfCsigned long min + Zid\n", ~Clong)(Cunsigned Long) “0 >> 12); "signed long max = X1d\n' Clong)(Cunsigned long) “0 >> 19); 7+ unsigned types ” printfC*unsigned char max = Xu\n", unsigned char) “02; printfC*unsigned short max = Zu\n", (unsigned short) “0); printfC"unsigned int max + tu\n", Gansigned int? “0d; unsigned long max * Xlu\nM, Cungigned long) “09; printf printf printf printf Another possible solution uses bitwise operators (page 48 K&R). For ‘example, the expression (char €Cunsigned char) “0 >> 1) takes a zero and converts each bit to one “0 Types, Operators, and Expressions Chap. 2 then converts the resulting quantity to an unsigned cher Cursigned char) “0 and shifts the unsigned char one position to the right to clear the sign bit Gunsigned char) “0 >> 1 and finally converts the quantity to cher Ccher )CCunsigned chard “0 >> 1) That is the maximum value for a signed character. “ The C Answer Book Exercise 2-2: (page 42 K&R) ‘Write a loop equivatent to the for loop above without using #4 or 11 Original: for Cis0; 16lim-1 46 Ceagetehar()? t= ‘\nt bac t= EOF; +64) Equivalent: enum loop { NO, YES >; enum loop okloop = YES; 10 while Cokloop =* YES? Af Ge Lam-t) /* outside of valid range? +/ ok loop = NO; else if (Ce = getchar(a) += ‘\n‘> okloop = NO: else if Co =* EGF) Js end of file 7 ” ok loop = NO: else ¢ sil =e; vets + Without #6 or 11 we have to break the original for loop into a sequence of 1 statements. We then change the tests. For example, in the original for loop 1 « Limet indicates that 1 still is within boundaries. In the equivalent statement 46 Limt indicates that 1 is out of boundaries and the loop should terminate. ‘ot Loop isanenumeration. Assoon as one of the conditions is met ot Loop is set to NO and the loop terminates, Types, Operators, and Expressions Chap.2 a7 Exercise 2-3: (page 46 K&R) Write the function htoiCs>, which converts a string of hexadecimal digits (in- cluding an optional 0x or 0x) into its equivalent integer value. The allowable digits are 0 through 9, » through f, and A through F. ‘define YES 1 ‘define NO 0 f+ htot: convert hexadecimal string s to integer int htoi€ehar sf) 4 int hexdigit, 1, inhex, m5 a2 05 if GOL) = 1070 4 7+ skip optional Ox or Ox ey af GeQi) ee txt Gt stad ee ed eras > nn 05 J+ integer value to be returned inhex * YES; 7+ assume valid hexadecimal digit for (5 inhex *= YES; +eid ¢ Af (sti) >= 70% ga sCi) e194) hexdigit = sf1) - ‘0° else if Gstil >= fat eb sil ce FD hexdigit * s(1) - ‘a’ + 10; else if Cali) > 7A bb SCA) ee CFD hexdigit © sfi) - ‘Ar + 105 else imhex = NO; /* not @ valid hexadecimal digit Af Cinhex += YES? ne 16 ¢ n+ hexdigits > return ny ‘The statement for € 3 inhex =* YES; #41) controls the function. ‘The integer 1 is the index for the array s.. While 5111 is a valid hexadecimal digit, 1nhex remains YES and the loop continues. The variable hexdigit takes a numerical value of 0 through 15 " o ” “” 43 ‘The C Answer Book ‘The statement Af Cinhex = YES? guarantees that a valid hexadecimal digit was at 31 and its value is in hexdi- git. When the loop terminates, hto1 returns the value of the variable n. “This function is similar to atoi (page 43 K&R) ‘Types, Operators, and Expressions Chap. 2 “9 Exercise 2-4: (page 48 K&R) ‘Write an alternate version of squeeze( 31,32) that deletes each character in ‘1 that matches any character in the string 22. /* squeeze: delete each char in a1 which 1s in a2 ” void squeezecchar si(J, char s2t)> 4 iota, je ts for Cis k= 05 SHCA] fe *\O%s doen ¢ For Cj + 0; s2Cj) f= OF Ga a2lj) f= stCits joer Af Gs2tjl ee N00 74 end of atring - no match #/ siCkeed © 10105 > sttkl = 4\075 ‘The first statement, for Ch = k #0; SICA] te ‘NO; dood initializes 1 and t, the indexes of #1 and the resulting string (also 51), respec- tively. Each character in s1 that matches any character in 92 is deleted. The Joop continues until the end of the string 1. The second for statement checks each character in 32 against the 3111 character. This loop terminates when s2 runs out of characters or there is a match. In the event that there is no match, 31 (11 is copied to the resulting string. If there is a match, the statement AF Cs2tyl e* 14000 fails, and 31(11 is not copied to the resulting string (it is squeezed out). 50 The C Answer Book Exercise 2-5: (page 48 K&R) Write the function any¢st ,s2), which returns the first location in the string ‘21 where any character from the string 32 occurs, or -1 if 31 contains no characters from 32. (The standard library function strpbrk does the same job but returns a pointer to the location.) 7+ any: return first location in st where any cher from s2 occurs+/ int anyCcher 31(1, char s2t1) 1 ant as gs for C1 = 0; sili] $+ *\O%s deed for Cj © 0; s2lj) '* ‘\0%s yoo Af GatCal e© 20y)> 7+ match found? ” return i5 7+ Location first match */ return -15 7+ otherwise, no match — +/ > ‘The statement for Ci = 05 S104) f= *NO7G deed controls the loop. When the loop terminates normally (+1 runs out of characters), eny returns -1 to indicate that no character of 52 was found in 1. ‘The second for statement, for C) © 04 s20j) f= ‘N04; peed is executed for each value of 1. It compares each character of #2 with s1(1). When a character in s2 matches s1 (1) the function returns i—the first location in the string 21 where any character from the string +2 occurs. Types, Operators, and Expressions Chap, 2 st Exercise 2-6: (page 49 K&R) Write a function setbits¢x,p,n.y) that returns x with the n bits that begin at position p set to the rightmost n bits of y, leaving the other bits unchanged. /* setbits: set n bits of x at position p with bits of y #/ unsigned setbitsCunsigned x, int p, int n, unsigned y) ‘ return x @ we O ce md ce Cpetendd I 0 To set n bits of x to the rightmost n bits of y we need to clear the n bits in x, clear all bits in y except the rightmost n bits and then shift them to position p, and OR the quantities together. To clear the n bits in x we AND them with n bits of zeros starting at position p and ones everywhere else. veer shifts all ones n positions to the left, leaving n zeros at the rightmost positions. “C0 «end places all ones at the rightmost n positions, zeros everywhere else. “CO Ce nd Ce (poten? shifts these » 1-bits to position p and “COO 6 nde Cpetendd 52 ‘The C Answer Book sets n bits to zeros starting at position p, leaving ones everywhere else. x8 -OOO Ce nd Ce poten? we AND this value with x to clear the n bits of x at position p. To clear all bits in y except the rightmost n bits we AND them with n bits of ones and zeros everywhere else. “C0 end places all ones at the rightmost positions, zeros everywhere else. y §7C0 cen? selects the rightmost n bits of y. And Cy 8 CO CO nd Ce peter places these n bits starting at position p. x8 -COO Ce md ce Cpetend? Tt &y & C0 Ce dd Ce Cpatend ‘we OR the two values to set the n bits of x starting at position p to the rightmost n bits of y, leaving the other bits unchanged Types, Operators, and Expressions Chap. 2 53 Exercise 2-7: (page 49 K&R) Write a function invert (x,p,n) that returns x with the n bits that begin at position p inverted (i.e., 1 changed into 0 and vice versa), leaving the others unchanged. J+ inverts inverts the n bits of x thet begin et position p */ unsigned invertCunsigned x, int p, int nd ‘ return x (C0 ce nd <« Cpetendds > co « ad shifts all ones n positions to the left, leaving n zeros at the rightmost positions. “0 «end places all ones at the rightmost positions, zeros everywhere else. COD end prin? shifts these n 1-bits to position p. x7 COO Ce nd Ce Cpetend? ‘The bitwise exclusive OR operator (*) produces a 1 when two bits are different, otherwise it produces a0. Since the objective is to invert the n bits starting at position p, it suffices to exclusive OR them with all ones starting at p for n bits (with zeros everywhere else). If the original bit is 0, exclusive OR with 2 1 produces a 1—it is inverted. If the original bit is a 1, exclusive OR with aL produces a 0—it is inverted. ‘The positions outside of the n-bit field are exclusive OR’ed with zeros: 070 (bits are the same) produces a 0—nothing changed; | ~0 (bits are different) produces a 1—nothing changed. Only the n bits are inverted. oe The C Answer Book Exereise 2-8: (page 49 K&R) Write a function ¢ ight rot (x, n? that returns the value of the integer x rorated to the right by n bit positions. /* rightrot: rotate x to the right by n positions ” unsigneé rightrotCunsigned x, int n> « int wordiength(void); ant ebity 7+ rightmost bit ” while (n-- > 0) ¢ rbit = Cx $1) €€ GwvordlengthO) - 195 xr ry de shift x 1 position right */ xe t orbits /* complete one rotetion ” y return x; , The variable rit takes the rightmost bit of x and shifts it to the leftmost position (wordlength() - 1 ‘Next, we shift x one position to the right and OR it one rotation. rightrot rotates x n times. wordlength() is a function that computes the word length on the host machine. rbit to complete /* wordlength: computes word length of the machine “” ant wordiength(void> 4 Amt ay unsigned v + Cunsigned) “Os for CL © ty Cyt v9 19> 05 108? return i; Types, Operators, and Expressions Chap. 2 cy This is a different solution to the same exercise: /* cightrot: rotate x to the right by m positions unsigned rightrotCunsigned x, int n? < int wordlength(void); unsigned rbits; af (Cn =n & wordlength(r? > 02 ¢ rbits © "CO COnd 8x; /# om rightmost bite of x 7+ rightmost bits to left rbits + rbits «¢ Cwordlength(> - nd; xe? nS /* x shifted n positions right xt x To rbits; 7* rotation completed » return x5 If the number of positions (n) to rotate to the right is the same as the number of bits in an unsigned integer, nothing changes because x is the same as before the rotation. If n is less, then it is necessary to rotate n positions. If n exceeds the number of bits in an unsigned integer, then the number of rotations is the remainder (modulus operator) of n divided by the length of the word. Asa result, no looping is necessary. “Oeen all ones are shifted n positions to the left leaving n zeros in the rightmost positions. “C0Cen) all ones are in the n rightmost positions. When we AND this value with x, the n rightmost bits of x are assigned to rbits. Then we move rbits to the leftmost position. We shift x n positions to the right. The new value of x is OR’ed with rbits to complete the rotation of the unsigned x, n positions to the right. ” ” “ ” ” 56 The C Answer Book Exercise 2.9: (page 51 K&R) In a two's complement number system, x = (x-1) deletes the rightmost 1- bit in x. Explain why. Use this observation to write a faster version of b1t- count. 7+ bitcount: count 1 bits in x - int bitcountCunsigned x) 4 ster version ” int bs for (b= sb return by x te 0; be etd » ‘Take a value for x-1, for example, the binary number 1010, which is 10 in decimal. (x-1) + 1 produces x: binary decimal 1010 x-1 10 +1 +4 1011 x u We take (x-1) and add J to it to produce x. The rightmost 0-bit of x-1 changes to 1 in the tesult x. Therefore, the rightmost I-bit of x has a corre- sponding 0-bit in x-1. This iswhy x & (x-1), in a two's complement number system, will delete the rightmost 1-bit in x. Consider an unsigned quantity of four bits. To count all 1-bits in this quantity, the original b1 tcount performs four shifts to check the rightmost bit. An alternative is to use the knowledge that x + Cx-1) turns off the rightmost L-bit of x. For example, if x equals 9, 1001 value 9 in binary ¢x) 1000 value 8 ¢x-19 1000x & (x-1) and the rightmost I-bit in x has been deleted. The resulting value is 1000 in binary, 8 in decimal. Repeating the process, 1000 value 8 in binary (x) 0111 value 7 ¢x-19 O000x & tx-1) ‘Types, Operators, and Expressions Chap. 2 ST and the rightmost 1-bit in x has been deleted. The resulting value is 0000 in binary, 0 in decimal. There are no more 1-bits in x and the process terminates. ‘The worst case is when all bits of x are ones—the number of AND's is the same as the number of shifts in bitcount. Qverall, this a faster version, 58 The C Answer Book Exercise 2-10: (page 52 K&R) Rewrite the function Lower, which converts upper case letters to lower case, with a conditional expression instead of if-else. J+ lower: convert ¢ to lower case CASCII only? ” int loweeCint e? ‘ return c= ‘AY bbc eH 'Z) 7 + fat A Cs > When the condition cn fat bbc ee 'ZF is true, c is an upper case letter (ASCII only). Then the expression ct tat far is evaluated and Lower returns a lower case letter. Otherwise, lower returns the character unchanged. CHAPTER 3 Exercise 3-1: (page 58 K&R) Control Flow Our binary search makes two tests inside the loop, when one would suffice (at the price of more tests outside). Write a version with only one test inside the loop and measure the difference in run-time. 7s binsearch: find x in vf0) ¢* vit] « . . int binsearchCint x, ant vil, int nd ‘ ant low, high, mids low = 0 high son = 45 mid = Clowshighd / 25 while Clow <= high @& x t= vimidl) ¢ if Ge € vimid)> high = mid - 15 else low * mid + 15 mid = Clowshigh? / > Af Gx e* vimidd> return mids '* found match else return -15 7+ no match ” ” bd The C Answer Book We changed the expression in the wh1 1e loop from low <= high to low <= high #& x f+ vimidl so we can use only one 1# statement inside the loop. This implies that we have to calculate mid before the loop starts and every time the loop is executed. We need to have a test outside of the wh le loop to determine if the loop terminated because x appears in the array v. If x appears in the array, bin- search returns mid, otherwise it returns -1 The difference in run-time is minimal. We did not gain much in perform- ance and lost some in code readability. The original code on page 58 K&R reads better from top to bottom Control Flow Chap. 3 61 Exercise 3-2: (page 60 K&R) Write a function escapeCs,t) that converts characters like newline and tab into visible escape sequences like \n and \1 as it copies the string t tos, Use aswitch. Write a function for the other direction as well, converting escape sequences into the real characters. 1s es) expand newline and tab into visible sequences te while copying the string t to s void escape(char s(J, char t(}) 4 int i, for Gm) = 0; tll te N04 reed awitch GOD ¢ case ‘\n': 7+ newline styred e tN\t5 styeel son's break: case *\tt: 7s tab styeed eye stjeel ett; break; default: 7+ all other chers stjerd © tlds break: > sty) = 1\0rs The statement for Ci w ye 0; thal te "0%; deed controls the loop. The variable 1 is the index for the original string t, and 4 is the index for the modified string s, There are three cases in the swi t chstatement: ‘\n’ for newline character, "\t/ for tab character, and default. If the character t (11 does not match fone of the two cases, escape executes the case labeled default: copy 111) to string ” ” . . . The function unescape is similar: 7+ unescape: convert escape sequences into a while copying the string t to void unescapeCchar sC1, cher t{1) « ant ts ds for (1 * ) #0; thal af CUED te ND styeod tds NOs ated else switchtieei1> ¢ case ‘n! ” atjesd e 1XneG breaks case ” atpeod © Nt5 break; default: io stpood eS atjeed = tGly atyd 4x07 The C Answer Book real characters */ . ” At ds @ backslashes real newline ” real teb ” all other chars #/ If the character in t(11 is a backslash, then we use a aviteh statement to convert \n into newline and \t into tab. ‘The defauit handles a backslash followed by anything else—copies a backslash and (11 to string 4. switch statements can be nested. Here is another solution to the same problem. Control Flow = Chap. 3 7+ unescape: convert escape sequenci ” while copying the string t void unescapeCchar s}, char t(1) « ant tg for CL = j= 0; Eid te "\OFs beer switch Ctl) ¢ case “\\': switch(tlosi)) ¢ case ‘n’: into to real characters : backslash real newline ” ” ” ” ” ” ” steer # ‘\n break; case ‘t* J+ real tab alyeod = Nts breaks default: 7* ell other chars styrod = N45 sCjeol = 0105 breal > breaks default: 7+ not @ backslash aCjerd 00s breaks > str Ne; > ‘The outer switch statement handles the backslash character and every- thing elsc (default). The backslash case uses another switch statement as in the solution above. cy The C Answer Book Exercise 3-3: (page 63 K&R) Write a function expand( 1,32 that expands shorthand notations like ez in the string 1 into the equivalent complete list abc...xyz in s2. Allow for letters of either case and digits, and be prepared to handle cases like e-b-c and e-z0-9 and -e-z. Arrange that a leading or trailing - is taken literally. 7+ expand: expand shorthand notation ins! into string s2 0 */ vold expandCchar si{1, char s2C1) € char ¢3 cnt ty ys Ay 0 while (Cos affisel) f+ 4\04) 7+ fetch a char from atl l+/ if CatCad = 1-4 a8 stCiet) re od ¢ sets while Co 31:12) /# expand shorthand " a2tjeed © eres ) else s2tyerd © cy J+ copy the character */ s2tj) = ‘NO > “The funetion takes a character from 31, saves it in c, and then checks the next character. If the next character is - and the character after that is greater than or equal to the character in c, expand proceeds to expand the shorthand nojation. Otherwise expand copies the character into 32. ‘expand works for ASCII characters. The shorthand a-z the equivalent list abc...xyz. The shorthand !-" expands into XYZ. sabe. .xyz. 1}. is solution was provided by Axel Schreiner of the University of Osna- bruck, West Germany. nds into ABC. . Contrel Figw = Ghap.3 6 Exercise 3-4: (page 64 K&R) In a two's complement number representation, our version of itoe does not handle the largest negative number, that is, the value of n equal to ~(2¥°"##=-), Explain why not. Modify it to print that value correctly, regardless of the machine on which it runs. fdefine abs(x? = (Gx) © 0-7-0): GD Js ttoa: convert n to characters ins - modified ” void itoaCint n, char (1) 4 ant i, signs void reverse€char 3015 sign = ny ys record sign ” 4505 do ¢ Fac generete digits in reverse ordere/ slitel = abstn 210) + 0%; /e get next digit + Dwhile (Cn /= 1039 t+ 095 y+ delete it ” Af Caign « 09 slated © tory stil + ‘No reverse(s);, d ~(Qrovtiae— 1) cannot be converted to a positive number as in neon because the largest positive number in a two's complement representation is: (qeorsine1) — ‘The variable sign saves the initial value of n. The macro abs finds the absolute value of n % 10. This avoids the — (rondo) problem because only the result of the modulus operator is made positive. cy The C Answer Book The conditional expression in the do-whi le statement has been changed from Gf 109 > 0 to Gf 10 to because if n remained negative throughout the loop, the loop would be infinite. Control Flow Chap. 3 o Exercise 3-5: (page 64 K&R) Write the function 1tob(n,s,b> that converts the integer n into a base b character representation in the string ». In particular, 1tob¢n, 16) formats as a hexadecimal integer i /sitob: convert a to characters ins ~ bt ” void itobCint n, char sf}, int b> ¢ int ty Jy signs void reverse(cher 30195 Af COsign =m) € 09 7+ record sign ” n= ons 7+ make n positive */ 1505 do ¢ 74 generate digits in reverse order */ pont bs J+ get next digit »/ Cased = Cy ce 99-7 porOr g yeotat-t05 while (Cn /* b> > 035 Jo delete it ” if Caign ¢ 09 atases st stil “No > ‘The contents of n are converted to base b so nib returns a value between 0 and b-1, and nied deletes that number from n. The loop continues while n/b is greater than zero. e The C Answer Book Exercise 3-6: (page 64 K&R) Write a version of 1 toa that accepts three arguments instead of two. The third argument is a minimum field width; the converted number must be padded with blanks on the left if necessary to make it wide enough. fdefine absx) Cx 07 -G GD? 7+ toa: convert n to characters ins, w characters wide */ void itoaCint n, cher s{], int w? ‘ Ant 4, signs void reverseCchar s(1); sign = ny 7* record sign ” ar) do ¢ /* generate digits in reverse ordere/ sliet) + abatn £102 + 70%; s* get next digit #/ Powhile On f= 109 t* O95 d+ delete it ” Af Csign € 07 stiee) 2 tary while G © wd J+ pad with blanks */ atieel ey stil = N04; reverse(s); y ‘This function is similar to 1 toe in Exercise 3-4. The necessary modification is while G ¢w) stiesl s 7 ry The white loop pads the string s with blanks if necessary. cHapten 4 Functions and Program Structure Exercise 4-1: (page 71 K&R) Write the function st rrindex(s, 12, which returns the position of the rightmost ‘Occurrence of t in s, or -1 if there is none. 7+ strrindex: returns rightmost index of t ins, -1 if none +/ int strrindexchar s€), cher t(1) 4 ant 1, J}, ks poss pos = -1; for Ci #05 sad fe 1\0%s 190d ¢ for Cymi, kaO; tlkT!e\O4 be sCpleetlels jee, keed if Ce> 0 bb tlk) ee N04) pos = 4; > return poss strrindex is similar to the routine strindex (page 69 K&R). When strindex finds a match it returns the position of the first element of t in s. On the other hand, strrindex records the position of t in » and continues searching because the function is looking for the last (rightmost) occurrence of tin Af Ce > 0 be tk) ee N07) pos * i; Another possible solution is: 70 The C Answer Book vinelude 7* aterindex: returns rightmost index of t ins, -1 if none */ Ant aterindexCchar (3, cher t€1) « int ts ys ki for C1 = strienCs? - strien(td; 4 >* 0; 1--) ¢ for Cjei, keO; tCkI'e\OF es sty teatle); jor, keod Af Ce > 0 68 tlk) =e N04) retuen 4; > return -1; > ‘This is a more efficient solution to the same problem. It begins at the end of the string » minus the length of the string t. If there is no match strrindex steps back one position and tries again. As soon as sirrindex finds t in » it returns 1; \ is already the rightmost position where 1 occurs. Functions and Program Structure Chap. 4 n Exercise 4-; (page 73 K&R) Extend atof to handle scientific notation of the form 123, 45e-6 where a floating-point number may be followed by © or € and an optionally signed exponent. include Js atof: convert string s to double ” double atof(char 31> 4 double val, power: int exp, 1, signs for Ci + 0; isspace(s(iJ>; is4) /# skip white space */ sign = (sti) 222 te Af (stil ee foe tt stad ee * for val = 0.05 ssdigit(s(i}); Le vel + 10,0 © val + (stil - /0795 Af (stad ee 09 sees for Cpower = 1.0; isdigit(s(ild; ised ( vel + 10,0 ¢ val © (stil - 10°95 power #= 10.0; , val = sign * val / powers Af Gold ee fet bt stil ee ED Sign + Csfeeid we 1-79 9 Af Gali) ee OF tt sti) ee for (exp = 0; tsdigit(stild; ised exp 2 10 * exp + Cail - 70795 Af Grign = 1) while Cexp-- > 02 /* positive exponent ” val #105 else while Cexp-- > 0) /# negative exponent ” val /= 105 > return val; ocd ‘The C Answer Book The first half of the routine is a duplication of atof (page 71 K&R). The function skips white spaces, records the sign, and calculates the number. At this point the original at of returns the value of the number and the modified version handles scientific notation. The second half of the function handles the optional exponent. If an exponent does not exist then atof returns the number stored in val. If an exponent exists then the sign of the exponent is stored in the variable sgn and the value of the exponent is calculated and stored in exp. The final operation if Gnign #10 while Cexp-- > 0) val += 105 else while Cexp-- > 02 val s= 105 adjusts the number according to the value of the exponent. If the exponent is positive, the number is multiplied by 10 exp times. If the exponent is negative, the number is divided by 10 exp times. val then contains the final number that is returned to the calling program. val is divided by 10 rather than multiplied by 0.1, since 0.1 is not an exact fraction in binary. In most machines, 0.1 is represented as slightly less than 0.1 and therefore 10.0 times 0.1 is rarely 1.0. Repeated division by 10 is better than repeated multiplication by 0.1, but there is still a Joss of accuracy Functions and Program Structure Chap. 4 Exercise 4-3: (page 79 K&R) Given the basic framework, it is straightforward to extend the calculator. the modulus (%) operator and provisions for negative numbers. Ainclude Ainclude J+ for atofO acefine MAXOP 100 /+ max size of operand or cperator ‘define NUMBER 0 /# signal that @ number wa: found Ant getoptchar (1); void push(double); double poptvoid); J+ reverse Polish caleulater maint? 4 int types double op2i char s(MAXOPI; while C(type * getop(s)) != EOF) ¢ switeh Ctype) ¢ case NUMBER: pushCatof(s)); break; case ‘+f: pushCpop(? + pope); breaks push(pop() * popt)); breaks op2 = popt); push(pop() - op2)5 break; case ‘/7 op2 = popt if Cop2 t= 0.09 push(pop() / op2); else printf(error: zero divisor\n™); break; ma Add ” ” ” ” ™ ‘The C Answer Book case ‘%* op2 = pope); Af Cop2 t= 0.09 Push( fmod(popt), op2) else print?c bresk; case ‘\nt: printfO"\tx.Bg\n", popOd); rror: zero divisor\n"); break; defeult: printfC"error: unknown command Xs\n", 9); break: , , return 0; We made modifications to the main program and getop. The routines push and pop (page 77 K&R) remain unchanged. ‘The modulus operator (2) is handled like the division operator (/). The brary function fmod calculates the remainder for the top two elements on the stack. op2 is the top element on the stack. This is the modified get op: #include @inelude #include ¢etype.h? adefine NUMBER 0’ /+ signal that @ number was found +/ Ant getchCvoid?; void ungetch¢ int); 7* getop: get next operator or numeric operand ” Ant getop(cher s(1) 4 ante, 43 while (GsL01 © 6 + getehQ)) oe 6 1 ttc we VED stra “N04; 1-0 If CHisdigit(e) ae te 4.7 abe > return cy 7+ not @ number ” Functions and Program Structure Chap. 4 if Ce se 7D Af Giadigit€e = getchOr> tt aCeril + ey ” else « Af Co t+ EOF) ungetch(es return /=*5 n > if Cisdigit(erd n while Cisdigit(steril = c= Af Cee td Mn while Cisdigit(steri) © 6 = stad = Nor; if Ce t= EOF) uagetehCe)s return NUMBER; Bs eee negative number ” minus sign ” collect integer part +/ getch>> collect fraction part +/ getchOr>? et op looks one character past the minus sign ta determine if it isa negative number. For example, “4 is a minus sign followed by a number. But “1.23 is a negative number. The extended calculator handles tote “10 2 4 ‘The first expression produces zero: 1 + ~1 duces -1. The second expression pro- 76 Exercise 4-4: (page 79 K&R) ‘The C Answer Book ‘Add commands to print the top element of the stack without popping, to du- plicate stack. ‘include (stdio.h? ‘include J+ for atot@ define MAKOF 100 /# max size of operand or operator #define NUMBER ‘0’ /* signal that a number was found int getopCehar (195 void push(double); double pop(void); void clear(void3; /* reverse Polish calculator maine? « int types double opt, op2; char s{MAXOP3; while ((type = getop(s)) !* EOF) ¢ switch Ctyped ¢ case NUMBER: pushCatof(s)); break; cone “+: pushCpop(? + pop); break: cose “8°: push(popt) * popt)); break; case f-4: op2 = pop); pushCpop() - op2); break; case 7": op2 = pop); if Cop2 '= 0.09 pushCpop) / op2); else printf("error: zero divisor\n' break; and to swap the top two elements. Add a command to clear the ” ” ” ” Functions and Program Structure Chap. 4 " case 177: 7+ print top element of the stack op2 > pope); PrintfO"\te.8g\n", p22; pushCop2); break: ters J+ clear the stack clearQ; break; case ‘d’ J+ duplicate top elem. of the stack op2 = pope); pusnCop2); pushCop2); break; tats J+ swap the top two elements opt * pope); ope = pope); pushCopt)s pushCop2); breek case ‘\n printfO"\t%.8g\n", popOrs break: default: printfC"error: unknown command %s\n", 5): breaks > > return 0; The newline operator pops the top element on the stack and prints it. We added a new operator, ’?’, that pops the top element of the stack, prints it, and pushes it back on the stack. We do not permanently pop the top element of the stack like the newline operator but we use the pop, print, push sequence to avoid letting the main program know about the stack or the stack position variables. To duplicate the top element of the stack we pop it and push it back twice. ‘We swap the top two elements of the stack by popping them and by pushing them back in reverse order. ” ” n ‘The C Answer Book It is easy to clear the stack; set sp to zero. So we added a new function that does exactly that and we put it together with push and pop. This way only the functions that maintain the stack refer to the stack and stack position var- iables. 7s clear: clear the stack ” void cleertvoid) rs sp 05 > Functions and Program Structure Chap. 4 n Exercise 4-8: (page 79 K&R) ‘Add access to library functions like ain, exp, and pow. See «math. ho in Appendix B, Section 4 (page 250 K&R). #include ‘include string.h> include meth. h> 1+ for atofO ” ‘define — mAaxoP 100 /* max 3i2e of operand or operator */ ‘define NUMBER 70° /* signal that @ number was found ¢/ adefine NAME nt 74 signal that @ name was found = */ Ant getopCcher (195 void pushCdouble double poptvoid?; void mathfnctchar (395 J+ reverse Polish calculator ” meinGd 4 int type; double op2; char s(MAXOP3; while CCtype * getop(s?) t= EOF) ¢ switch Ctype) ¢ case NUMBER pushta break; case NAME: mathfne(s); break; eters push(pop(? * popt>?: br ese fer: pushCpop() * ppt); brea! case -45 op2 = popt>s push(pop(? - op2); breaks of Cad? case 1/5 op2 = popt); af Cop2 t= 0.09 push(pop() / op2); else printfCerror: zero divisor\n"); breaks case ‘\n’: printfCM\t2.8g\n", pop? breeks defeult: printf(*error: unknown command s\n", 3); breaks ) > return 0; > Js mathfne: check string s for supported math functions void methfne(char 301) < double op2: Af Cstremp(s, “sin") ** 0) push(sin(popt)); else if Cstremps, “cos") ++ 0) push(cosCpop()))5 else if Catromp(s, exp") *- 0) pushCexpCpop()? else if Cstremp(s, “pows> += 0) ¢ op2 + pop); push(powlpop(), 0p2)); > else printfC"error: %3 not supported\n™ y The source file for the modified get op: ‘include (stdio.h> #include (string. h> ‘include ¢ctype.h? ‘define NUMBER 0’ /* signal that @ number was found ‘define NAME nt /* signal that @ name was found int geteh(void); Void ungetenCint>; The C Answer Book ” ” Functions and Program Structure Chap. 4 a J* getop: get next operator, numeric operand, cr math fre +/ int getop(char sf) ante, a while C630] = © = getchQd e261 ttc ae MED sO) © \0e5 105 af Cislower(er? ¢ J* command or NAME ” while Cislower(s(++il * ¢ = getch(>)) stil = ‘\0%5 if Co t+ EOF) ungetchc); 7+ went one char too far #/ Af Cstrlen€s) > 19 cetucn NAME; Jeo chars it as NAME 4s else return co; Je it may be @ command +/ > Af Clisdigit(ed a2 te 1.7) return cy 7+ aot @ number ” Af Cisdigit(cr? J+ collect integer part +/ while Cisdigit(s(er1) * © = getch(>>) if Come > /* collect fraction part «/ while Cisdigit(s(rrid © ¢ © getchOrs stil = *N0%5 if Co $= E0F> ungetchCe)s return NUMBER; We modified get op to be able to gather a string of lower case letters and return it with the type NAME. The main program recognizes NAME as one of the valid types and invokes mathfnc. The routine nathfne is new. It performs a sequence of 1 statements until it finds a function name that matches the string or it reports an error. If the string s is one of the supported functions, ma thf ne pops enough elements from the stack and invokes the math function. The math function returns a value and mathf nc pushes it back on the stack. For example, sin expects an argument in radians and the si is one. 3.14159265 2 / sin 82 ‘The C Answer Book The first operation divides P 1 by 2 and pushes the result back on the stack. The function sin pops the top clement of the stack, calcutates the sine, and pushes the result back. The result is one. 3.141592 2 / sin 0 cos + produces 2 because the sine of PI_/ 2 is one and the cosine of zero is one. Another example, S 2 pow 4 2 pow + raises 5 10 the power 2, 4 to the power 2, and adds the two values. getop does net know about specific function names; it just returns strings as it finds them. This way it is straightforward to expand mathf ne to include more functions. Functions and Program Structure Chap. 4 Exercise 4-6: (page 79 K&R) ‘Add commands for handling variables. (It's easy to provide twenty-six variables with single-letter names.) Add a variable for the most recently printed value. Finclude include math. h> (+ for atofOr sdefine — MAXOP 100 /* max size of operand or operator ‘define NUMBER 0" /* signal that a number was found int getop(char void push(double); double popvoid); (Ds /* reverse Polish calculator main? « int 4, type, var = 0; double op2, char s{MAXOP]; double veriablet261; for (1 © 0; 1 € 265 169d variebleti) = 0.0; while CCtype = getop(s)) '*EOF) ¢ awiteh (typed ¢ case cose case NUMBER: pushCatot(s); break; pushCpop? + popt>); eak; " pushCpopC) + pop>); br op2 = popt); push(pop() - op2); breaks as op2 = pop; Af Cop2 o> push(pop() / 0p2); else printf(“error: zero diviser\ break; ” ov v " “ The C Answer Book pope if (var oe fAt ak var ce IZ) vartebletvar - ‘Al + popt); else printf("error: no variable name\n"); brea case ‘\n’ v * popt)s printfC*\tz.egin", v2; brea defoult: if (type >= AT a type < 124) push(variable(type - 7071); else if Ctype == ‘v7? pushv); else Printferror: unknown command Xs\n", 5); break; , var = types > return 05 ‘The variables we added are upper case letters from A to 2. The letters serve as the index to the array variable. We added a lower case variable v that contains the most recently printed value. ‘Whenever the program finds a variable name (A-2, or v) it pushes its value on the stack. We aiso have a new operator, *=’, that assigns an element from the stack to the preceding variable name. For example, aA- assigns the value 3 to the variable @, Then 2as adds 2 to 3 (the value assigned to the variable a). At the newline operator the program prints 5 and also assigns 5 to the variable v. If the next operation is vie it produces 6: 5 +1. Functions and Program Structure Chap. 4 8 Exercise 4-7: (page 79 K&R) Write a routine unget s¢s) that will push back an entire string onto the input Should ungets know about buf and bufp, or should it just use unge ten? ‘include 7+ angets: push string back onto the input ” void ungets(char s{1) « int len * striencs); void ungetchCint); while Clen > 0) ungetch(s{--1en}); The variable 1en contains the number of characters in the string = (ex- cluding the terminating ‘\0*), which is determined by the function strlen (page 39 K&R). ungets calls the routine ungetch (page 79 K&R) Len times, each time pushing back a character from the string s onto the input. ungets pushes the string back in reverse order. ungets does not need to know about buf and bufp. The routine un- getch handles buf, bufp, and error checking. 86 ‘The C Answer Book Exercise 4-8: (page 79 K&R) Suppose that there will never be more than one character of pushback. Modify getch and ungetch accordingly. finclude ¢stdio.h> char buf = 05 7+ getch: get » (porsic.. pushed back? cherecter ” Amt getch(void) ¢ int es Af (buf t= 0) e+ buf; else e+ getcherQs buf +05 return cy , J* ungetch: push character back onto the input ” void ungetchCint ¢) « if buf t+ 09 printfungetch: too many characters\n); else buf = cs > The buffer, buf, is no longer an array because there will never be more than 1 character in the buffer at any time. buf is initialized to zero at load time and get ch resets buf to zero every time it gets a character, ungetch checks for an empty buffer before it pushes a character back. If the buffer is not empty then ungetch produces an error message. Functions and Program Structure Chap. 4 a Exercise 4-9: (page 79 K&R) Our geteh and ungeteh do not handle a pushed-back EOF correctly. Decide what their properties ought to be if an EOF is pushed back, then implement your design. ‘include adefine BUFSIZE 100 int buf (BUFSIZE)5 7+ buffer for ungetch ” int bufp = 05 7+ next free position in buf ” J getch: get a (possibly pushed beck) character ” int getehCvoid? ‘ return (bufp > 0) 7 buff--bufp] : getchar(>; , 7+ ungetch: push character back onto the input ” void ungetch(int c? « Af Cbufp >= BUFSIZE) printfC"ungetch: too many characters\n"); else buflbufpes} +c; In the routines getch and ungetch (page 79 K&R), the buffer, buf, is declared to be an array of characters: char buf (BUFSIZEJ; The C programming language does not require a char variable to be signed or unsigned (page 43 K&R). When a char is converted to an int it might never produce a negative number. On some machines, if the leftmost bit of a char is 1, it will produce a negative number when converted to an int . ‘On others, a char is converted t6 an int by adding zeros at the left end. This conversion will always produce a positive number, regardless of whether the leftmost bit was 1 or not. In hexadecimal notation ~1 is OxFFFF (to 16 bits). When OxFFFF is stored in a char, the actual number being stored is OxFF. When OxFF is 8 ‘The C Answer Book converted to an int, it might produce Ox00FF, which is 255, or OxFFFF, which is -1. negative number (~1)—> character—> _ integer OxFFFF OxFF Ox00FF (255) OxFFFF OxFF OxFFFF (—1) If we are going to treat EOF (—1) as any other character, buf should be declared as an array of integers: Ant buf (BUFSIZE); No conversions will occur and €0F (~1) or any negative number will be handled in a portable way. Functions and Program Structure Chap. 4 Exercise 4-10: (page 79 K&R) An alternate organization uses get ine to read an entire i ine; this makes getch and ungetch unnecessary. Revise the calculator to use this approach. #include ¢stdio.n> ‘#include séefine MAXLINE 100 define NUMBER 70’ /+ signal that a number was found +/ int getlinechar linet}, int limit ant 1a = 0 Z* input line inde: ” char Line (MAXLINE); 7 one input line " 74 getop: get next operator or numeric operand ” int getopCcher sf) « int cy 43 if CLinetlid s= 7\079 af CgetlineCtine, MAXLINED return EOF; tise dis 05 while (C500) * e = Lineflise]2 we 4 fo tt ewe NED att = N05 Af Clisdigit(ed abc te 1.79 return ¢; (+ not & number ” 1 Oy Af Cisdigitéer> 7+ collect integer part «/ while Cisdigit(s(+ei) © © = line lives) ie 1+ collect fraction part +/ while Cisdigit(s(eeid + © = line(tiee39) stad # N07 hives return NUNBER; > Instead of using getch and ungetch we use getline in the function getop. line is an array that contains one entire input line at a time; 11 is the ° ‘The C Answer Book index for the next character in Line. We made 1 ine and 11 external variables so that they will maintain their values between calls, unlike local variables. If getop is at the end of the line (or we do not have a line yet) if Clinetlid ee 1.049 then getop invokes get line to get a line. in the original get op (page 78 K&R) the function invokes getch every time it needs another character. In this version we get the character at position 11, in tine, and then we increment 11. At the end of the function, instead of calling unge teh to push a character back, we decrement 11 because we went one character too far. Remember that any function may use and modify external variables in another function, so 11 and 11ne could be modified by a function other than getop. Sometimes we want to prevent this from happening so we should declare the variables to be static. We did not do so because static variables are not discussed until page 83 K&R. Functions and Program Structure Chap. 4 a Exercise 4-11: (page 83 K&R) Modify get ep so that it doesn’t need to use ungeteh. Hint: use an internal static variable. finclude ¢stdio-h> ‘include <¢etype-h> define NUMBER 0" /+ signal that a number was found */ ant getch€void)s J+ getop: get next operator or numeric operand “ Ant getop(char s(> 4 ante, 45 static int laste + 05 af Claste ** 0) © * getcne>; else « c+ laste; laste = 05 ) while ((s(0] = cd ve * fbb cee NED ct getchO; a1) = 1\0%5 Af Clisdigit(cd asc t= *.79 return cy (+ not a number " b= 0; if Cisdigi ter? J+ collect integer pert +/ while Cisdigit(sleeil + © = getch(>)> if Cee J+ collect fraction part */ while Cisdigit(steril + ¢ + getch(r>> stil © ‘\0%5 af Co t* EOFD laste = 3 return NUMBER; > We modified get op to have an internal static variable that remembers the last character that we should push back onto the input. Since we do not use ungetch we store the character in laste. 92 ‘The C Answer Book When getop is invoked it checks for a previous character in laste. If ‘one does not exist the function invokes get ch to get a new character. If there is a previous character then getop copies the character into c and zeros out laste. The first whi Le statement changed a bit. The reason is that getop needs to get another character only after it examines the current character inc. Functions and Program Structure Chap. 4 93 Exercise 4-12: (page 88 K&R) ‘Adapt the ideas of pr i ntd to write a recursive version of 1 toa; that is, convert an integer into a string by calling a recursive routine include math. n> (+ itoa: convert n to characters in 3; recursive " void itoaCint a, char s0)) 4 static int is Af (ns 100 itoatn / 10, 395 else ¢ A005 if (rn © 09 > slite) = abs(nd 210 6 404; aC) © T\0F i toa receives two arguments: an integer n and array of characters s._ If the result of the integer division n/10 is not zero then i toe calls itself with 1/10: if (nf 10) itoaCn / 10, 595 When r/10 is zero in one of the recursive calls we are looking at the most significant (leftmost) digit in n. We use a stetic variable i as the index for the array s. If n is negative we put a minus sign in the first position of the array s and increment 1, As i toa returns from the recursive calls it calculates the digits from left to right. Note that each level adds a *\0° to terminate the string and the next level overwrites the ‘\0“, except the last. 34 The C Answer Book Exercise 4-13: (page 88 K&R) Write a recursive version of the function reverses), which reverses the string s in place #include ¢atring.h> J+ reverse: reverse string s in plece ” void reverse¢char s(1) « void reverser(char sf], int i, int tend; reverser(s, 0, stelen(s)); » (+ reverser: ceverse string 3 in place; recursive vy void reverser(char st], int i, ant len? 4 ant ce. yt poten = G6 9)5 if Ge ye et sts stats atyls style cs reverser(s, tri, lend; We need to maintain the same user interface to the routine reverse regardless of implementation. Therefore, we pass only the character string to reverse. reverse determines the length of the string and then calls reverser, which reverses the string s in place, reverser receives three arguments: » is the string to be reversed, 1 is the left-side index for the string, and Len is the length of the string (str 1enCs), page 39 K&R). Initially, 1 is equal to 0. is the right-side index for the string. j is computed as pot der = G4 195 ‘The characters in the string are swapped from the outside in. For example, the first two characters swapped are s( 0} and s{1en-1) and the second two Functions and Program Structure Chap. 4 95 characters swapped are s{11 and s{1en-21. The left-side index, 1, is incre- mented by 1, every time reverser is called: re peers, #41, Lend; ‘The swapping continues until either the two indexes are pointing to the same characters (1 =* }) or the left-side index points to a character to the right of the right-side index (1 > |). This is not a good application of recursion. Certain problems lend them- selves to recursive solutions—see, for example, the function treeprint on page 142 K&R. Other problems are best solved without recursion. This is ‘one of them. 96 ‘The C Answer Book Exercise 4-14: (page 91 K&R) Define a macro swept , x,y? that interchanges two arguments of type t. (Block structure will help.) adefine swept, x, yd «tts We use the braces to define a new block. At the beginning of a block we can declare local variables. _z is a local variable of type + that helps swap the two arguments. ‘The swap macro works if neither of the arguments is _2. If one of the arguments has the name _z, swapCint, 2, 5 then when the macro is expanded, it becomes (int 25 2 2 les 2 exe la and the two arguments are not swapped. The assumption is that _z will not be used as a variable name. CHAPTER 5 Exercise 5-1: (page 97 K&R) Pointers and Arrays Aswritten, get int treatsa + or - not followed by a digit asa valid representation of zero. finclude include int getch(vord); void ungetchCint; /e getint: get next int getint(int #pn) 4 int c,d, sign; while Cisspacete = getchQo> s+ Af Clisdigit(c) ac t+ EOF bac ungetch(c); i return 0; ) signs Cone 1-497 fy if Come OF Tt cee Td dc i ifCLisdigitle * getchO? ¢ af Cc t= EOF) ungetch(c); —/* ungetch(d); i return d; i Fix it to push such a character back on the input. integer from input into *pn skip white space fe tet tbe it’s not a number ” remember sign char push back non-digit push back sign char return sign char ” ” ” ” ” “ ” a7 98 The C Answer Book for Cepn + 0; isdigit€ed; © = geteh(r? tpn = 10 5 #pn s (co - 701); tpn ee signs if Cc t= EOF) ungetch¢c)s return cs When there is a sign character, getint saves it in d and gets the next character. If the next character is not a digit and it is not EOF, get int pushes it back on the input, Then the function pushes back the sign character and returns the sign character (o indicate this situation. Pointers and Arrays Chap.5 99 Exercise 5-2: (page 97 K&R) Write get float, the floating-point analog of get int. What type does get - float return as its function value? finclude ¢stdio.h> Finclude ¢ctype.h> int getch(void?; void ungetchCint); /* getfioat: get next floating-point number from input ” int getfloat¢float *pn) « int c, signs float powers while Cisspacee * getch(r)) 7/* skip white space */ af Cfisdigit(c) bh c's EOF bec te fet 86 cts tet bh te 4) Ge ungetehte); J+ it's not a nunber/ return 0; > sign = Coss (999% -1 45 Af (ene fe it eee nD © + getched; for (spn = 0.0; isdigit(ed; © + getch(> spn + 10.0 # #pn + Co - 0°93 s+ integer part +/ oy c= getehers for (power + 1.0; isdigitCed; © = getehcr? ¢ tpn # 10.0 + epn s Ce - 070; /# fractional part +/ power += 10.05 if Ce > tpn ** sign / power; 7+ final number ” if Co t= EOF) ungetch(e?s return ec; The routine get loat is similar to the routine getint (page 97 K&R). getfloat skips white spaces, records the sign, and stores the integer part of the number at the address in pn. get float also handles the fractional part of the number (but not scientific 100 The C Answer Book notation). The fractional part is added to + pn in the same fashion as the integer part: spn = 10.0% *pn + Ce - 10°95 For each digit collected after the decimal point the variable power is multiplied by 10. For example, if 0 digits follow the decimal point power equals 1, if I digit follows the decimal point power equals 10, and if 3 digits follow the decimal point power equals 1000. And then get/loat multiplies the final value of spn by sign/power to get the floating-point value. The function returns either EOF or the ASCII value of the first character after the floating-point number. The function type is i nt. Pointers and Arrays Chap. 5 101 Exercise 5-3: (page 107 K&R) ‘Write a pointer version of the function strcet that we showed in Chapter 2: atrcat(s,t) copies the string t to the end of 3. /* streat: concatenate t to the end of 3; pointer version +*/ void streat(char #5, char #t? re while Ces) while Cases = eteed , Initially » and t point to the beginning of the character strings. The first white loop increments the pointer s until it finds the end of string marker (“\0"). The statement while Cra) is true as long as the character is not the end of the string marker. The second whi 1e loop appends the string t to the string s: while Ceses = ster) The above statement assigns to +s whatever is in «t, increments both pointers, and continues as long as t does not point to the end of string marker. 102 The C Answer Book Exercise 5-4: (page 107 K&R) Write the function strend(s,t), which returns I if the string t occurs at the end of the string s, and zero otherwise. J+ strend: return 1 if string t occurs at the end of 5 ” int strend(char +s, char st) « char *bs © 55 /* remember beginning of strs */ cher sbt + ty for © 3 ess se9) /+ end of the string 5 ” for © 3 ety teed 7+ end of the string t ” for (3 #3 #* ety ace, t--) if (ose bt dios + bs breaks 7+ at the beginning of @ str +/ Af Ges te et bb tee bt tees te 0) return 1; else return 0; We save the beginning addresses of the strings in the pointers bs and bt and then we find the end of each string 2s we did in streat. To determine if the string t occurs at the end of the string s we start comparing the last character in 3 with the last character in t and move toward the beginning of the strings. strend returns 1 (the string { occurs at the end of the string s) when the characters in t match the characters in s, the pointer ¢ is back at the beginning of the string, and the strings are not empty: Af Ces ee et bb tore bt dbes te 0D retuen 15 Pointers and Arrays Chap. 5 103 Exercise 5-5: (page 107 K&R) Write the versions of the library functions strnepy, steneat, and strnemp, which operate on at most the first n characters of their argument strings. For example, strnepy(s,t.n) copies at most n characters of t 10 s. Full de- scriptions are in Appendix B (page 249 K&R) 1+ stenepy: copy n characters from t to s ” void stracpy(char *s, char #t, int nd « while Cet @& ne= > 0D este e otees while (n-= > 9) were NDS + /* strrcat: concatenate n characters of t to the end ot s +/ void strncat(char *s, char +t, int nd 4 void stenepy€char +s, char #t, unt nd5 int strienCcher +5 stencpy(ststrients), t, 9); > J+ stenemp: compare at most n characters of t with 5 ” int stencmp(char #3, char *t, int nd ‘ for ©; #5 te ety see, teed af Ces ee NO EE oon = OD return 0; return es ~ ats ‘The functions strnepy and strneat have the type void as strepy on page 105 K&R. The library versions of these functions return a pointer to the beginning of the target string strnepy copies at most n characters from t tos. {f t has less than n characters we pad the string s with ‘\ 0". stencat invokes strnepy to copy’ at most r characters from the string t starting at the end of the string s strncmp compares at most n characters of t with characters of s. The function is similar to stremp on page 106 K&R. This time we terminate the comparison when we find the end of the strings or we successfully compare n characters: af Ces ee NO EE oon «= 07 return 05 Exercise 5-6: (page 107 K&R) Rewrite appropriate programs from earlier chapters and exercises with pointers instead of array indexing. Good possibilities include get ine (Chapters 1 and 4), atot, itea, and their variants (Chapters 2, 3. and 4), reverse (Chapter 3), and strindex and get op (Chapter 4). #include (stdio.h> Js getline: read @ line into 3, return length ” int getline€cher ¢s, int lim) 4 ant ¢¢ char *t = 33 while C--lim > 0 && Cesgetchar(d? !* EOF && © te ‘\nt> waee ees Af Ce ee Vn) taee = es maa returns > ty get | ine receives a pointer to an array of characters. We use + instead of 91) to refer to an element in the array and we increment the pointer s to travel down the array. The statement stisel + cs is equivalent to outs = cy At the beginning of the function, » points to the beginning of the array and we save that address in the pointer t: char *t = 35 After get 14ne reads a line, » points to the terminating character (*\0°), 1 still points to the first character in the line so the length of the line is 5-1. ‘include /+ atol: convert 3 to integer; pointer version v int atoi(char +5) 4 Pointers and Arrays Chap. 5 105 > int n, sign; for € ; isspace(esd; 564) 7+ skip white space */ signs Ces ee to Dt If Cea ee ter des eet /* skip sign ” a for (n+ 0; isdigit(esd; s+) ne 10 ens os = OF return sign n; 3011 is equivalent to #3. i++) is equivalent to #5++ void reverse(char #); 7+ \toa: convert nm to characters in s5 pointer version ” void itoaCint ny char +5) 4 ant signs char et +35 s+ save pointer tos #/ Af CCsign = nd © 09 J+ record sign w aeons 7+ make n poaitive ” do ¢ J+ generate digits in reverse ordert/ tates nn B10 + 20" J+ get next digit " Pwhile Cn /= 109 > O25 7+ delete it ” if Caign ¢ 0) tee ty os N07 reversect); , The character pointer ¢ is initialized to point to the first element in the string s: cher *t © 35 The statement face en 1d + 1075 is equivalent to slitel en B10 + 408 106 ‘The C Answer Book We invoke reverse with a pointer to the beginning of the string » sinclude J+ reverse: reverse string s in place ” void reverseCchar +s) t int ¢5 char et; for CL * a + (strlen€s)-19; 3 ¢ ty set, to ¢ + points to the first character of the string and we set the character pointer + to point to the last character of the string (excluding the terminating *\0°): t= s+ CstrlenCso-1 +3 is equivalent to 3(11 and +t is equivalent to stj 1. The test in the for loop set is equivalent to the test ey s++ has the same effect as incrementing the index 1 ¢1++) and t-~ has the same effect as decrementing the index ¢)--). /* strindex: return index of t ins, -1 if none ” Ant steindexCchar *s, char +t) « char sb #35 7* beginning of string s */ char *p, #rs, for Gs for Cp Af Gt bbe ee 0D return a ~ b; SNOT; weed poraty ar fe Nh bb sey pee, reed , eeturn -15 Pointers and Arrays Chap.5 107 3111 is replaced by +s, 3C is replaced by *p, and t(r1 is replaced by +r. b isa character pointer that always points to the first element of the string 2(s€01). p= sisequivalent toy + 1. r= tis equivalent tok + 0 ‘When the if statement is true Af Cr > tok er ee N09 a match exists and strindex returns the index of t in the string » return s = 65 finclude 7+ atof: convert string 5 to double; peinter version double atof(char #3) « double vel, power; int signs for ( 5 isspace(es); a¢¢) /* skip white space sign = Ces ee 29 2-1 ay If Cs ee fee thes ae ey for (val = 0.0; isdigit@rads s¥4) val = 40.0 + val + Ges = 10°95 MGs ee 9 aes for (power = 1.0; asdigit(esd; aver ¢ val = 10.0 + val * (es - “0795 power *= 50.0; > return sign * val / power; sti 9+) is equivalent to +++. ” ” 108 The C Answer Book Finclude include ¢etype.h> veefine NUMBER 0" /* signal thet @ number was found int getchCvoid)s void ungetchCint); /* getop: get next operator or numeric operand; pointer ver int getop(char #5) « int es while (+s = ¢ © getchOn> <2 7 + cme Nt Hart = N08; Af Clisdigit(cd abc te 1.4) return ¢; 7+ not a number af CisdigitCer> /* collect integer part while Cisdigit(sers = ¢ = getcher)) if Go ee 4D (* collect fraction part while Cisdigit€eres = ¢ = getcner)) ts \0F af Co t= EOF) ungetch(e); return NUMBER; We use pointers to replace references to array elements. The changes are straightforward. Instead of SUD = *N045, we use ere ema a to place an end of string marker at the second position of the array without changing the pointer. ” ” ” Pointers and Arrays Chap. 5 109 Exercise 5-7: (page 110 K&R) Rewrite readiines to store lines in an array supplied by natn, rather than calling @11ce to maintain storage. How much faster is the program? include ¢string.h> define MAXLEN 1000 /* maximum length of Line " define MAXSTOR 5000 /* size of available storage space +/ int getlineCchar +, int); /* readlines: read input lines “ int readlines(char *lineptr(}, char linestor, int maxlines? t int len, nliness char Line (MAXLEN: char p= Linestor; char *linestop = linestor + MAXSTOR: niines + 05 while CClen + getiineCline, MAXLEND? > 0> Lf Colines 9* maxlines 11 prlen > Linestep> return - else { Linetlen-12 = ‘\0%5 ye delete newline +/ strepyCp, lined; lineptrfnlineseed = p pote tens > return nliness The main routine supplies the array Linestor where readiines stores the text for the lines. The character pointer p is initialized to point to the first element of linestor: char *p + linestor; The original routine read! ines (page 109 K&R) uses the routine @! Loc (page 101 K&R): Af C(nlines >* maxlines if (p= alloc(len)) == NULL 110 The C Answer Book In this version eed! ines stores Line in Iinestor starting at position p. The statement Af Colines >= maxlines 11 psien > linestop> ensures that there is available space in linester. This version of readl ines is slightly faster than the original one. Pointers and Arrays = Chap. 5 mt Exercise 5-8: (page 112 K&R) There is no error checking in day.of_year or month_day. Remedy this defect. static cher deytep(21(13) = ¢ 40, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), 40, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31> dy 7+ day.of_yeer: set day of year from month 4 day ” Ant day_of_yearCint year, int month, int day? « ant 4, leaps leap * yeart4 += 0 && yeart100 t= 0 11 yeartao0 == 0; Af (month €1 11 month > 12> return -15 if Cday € 1 If day > daytabl leap] {month]> return -15 for (i = 15 4 € month; dey + return day; > 7» month_day: set month, day from day of year 7 void month_dayCint year, int yearday, int spmonth, int +pday? t ant a, leaps if (year © 19 ¢ spmonth = -15 spday * - return; > leap + yeart4 ++ 0 86 yeart100 != 0 11 yeart400 == 0; for (i + 15 1 €+ 12 46 yearday > dayteblleap}(i}; i++) -* daytabfleap}(1); & yearday > daytab(ieap3(i2)) ¢ ‘pmonth © -1 spday = - d else ¢ spmonth = 45 spday = yearday; 112 The C Answer Book In day_of_year we check for reasonable values in month and day. If month is less than one or greater than twelve, day_of_year returns —1. If dey is less than one or day exceeds the number of days for the month, the function returns ~ 1. In month_day we first check for negative year. You may want to add this kind of check in dey_of _yeer also. Then we proceed to decrement year - day while we check that the month (index 4) does not exceed 12. If the loop terminates with month 13 and the value in year day exceeds the number of days in the last month of the year, then yearday started with an incorrect value and we set both month and day to — 1. Otherwise the function month_day received correct values. Pointers and Arrays Chap. 5 M3 Exercise 5-9: (page 114 K&R) Rewrite the routines day_of_year and month_day with pointers instead of indexing. static char daytab(2]{131 * ¢ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31>, 40, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 317 ds 7+ day_of_year: set day of year from month § day uv ant day_of_yearCint yeer, int month, int day? « int Leap; char *ps leap = yeart4 == 0 4b yeart100 = 0 tf yeart4oo ++ 0; p = daytabl leap}; While €--month> day +* #+»p; return day; > J+ month_day: set month, day from dey of year " veid monthdayCint year, int yearday, int *pmonth, int *pday? 4 int leap; cher *p; leap * yeark4 == 0 && yeart100 !+ 0 11 year%400 == 0; p * daytab{ leap}; while Cyearday > #¢+p) yearday -* *p; spmonth = p - *Cdaytab + leap); spday + yearday; » p points to the first or second row of daytat depending on the value of leap p+ daytab( leap}; The for loop in the original dey_of year rou for Gi = 13 1 € month; 16) day ** deytablleep}ts "4 The C Answer Book is equivalent to the whi le loop while (--month> dey os e+ in the revised day_of _year routine. The for loop in the original month_day routine for Ci = 15 yearday > dayteblieepi(i}; ite) yearday -* dayteblleap!(1}; is equivalent to the statements p * dayteb{leap); while Cyearday > ***p) yearday -* sp; in the revised month_day routine Pointers and Arrays Chap.5 15 Exercise 5-10: (page 118 K&R) Write the program expr, which evaluates a reverse Polish expression from the command line, where each operator or operand is a separate argument. For example, expr 29 404 4 evaluates 2 x (3+4). #include ‘include ¢math.h> 7+ for etofe> ” define MAXOP 100 /* max size of operand or cperator +/ define NUMBER 0 /# signal that a number was found +*/ int getopCcher (195 void ungets(cher £1); void push(double); double pop(void); J* reverse Polish calculator; uses command line ” main€int arge, cher sargvi) 4 char sCMAXOP); double op2; while C--erge > 0) ¢ ungetsc" "5 J+ push end of argument +/ ungets(*++argu?s, 7* push an argument ” awiteh Cgetop(s>) ¢ case NUMBER: pushCatof(s)); break case 147: push(popl? + popl?); break; case ‘+’ pushCpop() * pop(>?; break; cane ‘-! op2 = pope; push(pop() - op2)s break; 6 The C Answer Book case ‘/*: op2 © poptr; af Cop2 t= 0.0) push(pop() / op2); else printf@error: zero divisor\n" breaks default: printfCerror: unknown command Xsin™, 995 arge * 15 break; > > printfCrtn.ag\n" return 0; pop); This solution is based on the reverse Polish calculator on page 76 K&R. It uses the routines push and pop (page 77 K&R). We use ungets to push an end of argument marker and an argument onto the input buffer. This way we can use getop unchanged. getop invokes getch to read characters and gets the next operator or numeric operand. If an error should occur while reading in the arguments, ar ge is set to 1. The white loop in the main routine while (--arge > 09 becomes false and the program ends. The result for a valid expression is on top of the stack and we print this result when the argument list terminates. Pointers and Arrays Exercise 5-11: (page 118 K&R) WwW Modify the programs entab and deteb (written as exercises in Chapter 1) to accept alist of tab stops as arguments. Use the default tab settings if there are no arguments. vinclude adefine define adefine adefine void settabCint argc, char eargy(}, char +teb); void entab(char *tab int tabpos(int pos, char *tab); /* replace strings of blanks with tabs main€int argc, cher vary) { char settabCargc, argv, tab); entab(tab); return 0; > /* entab: replace strings of blanks with tabs and blanks void entab(char «tab) « int ¢, int nb = 05 int nt = 05 for (pos = 1 (+ maximum line size 7+ default tab increment size tabIMAXL INET] 5 J+ initialize teb stops J* replace blanks w/ tab J+ # of blanks necessary 78 # of tabs neces: Congetchar€)) = EOF; poste) tab? == NOD increment # of blanks af CabposCpos, Js reset # of blanks 7+ one more tab ” ” ” ” ” ” “ ” ” ” 118 ‘The C Answer Book d else « for € 5 nt > 05 nt--> putchar(’\t/); /* output teb(s) ” af (ome ED (+ forget the blanks) +/ nb = 0; else /* output blank(s> ” for ©; nb > 0; nb--> putchar¢* "5 putchar(e); Af (eome \nt> pos = 0; else if Coes MED while ClebposCpos, tab) != YES? s1pos: > > The source file settab.c: @include ¢stélib.n> ‘define MAXLINE 100 (+ meximun Line size ” ‘define TABINC 8 (+ default tab increment size +/ ‘define YES 1 ‘define NO 0 /* settab: set tab stops in array tab ” void settabCint arge, char € egvl), char *teb> int 1, poss Af Carge « 1) J+ default tab stops ” for (i = 15 4 €* MAXLINE; i¢e) Af GX TABING *© 09 tablil = YES; else tebti) = NOs else 4 (> user provided tab stops +/ for (i = 15 4 €= MAXLINES 166) tablil + NO; /* turn off ell tab stops uv while Cr-erge > 0) {/* walk through argument list +/ pos = atoiCsrsargy); Af (pos > 0 bb pos C+ MAXLINED teblpos) = YE Pointers and Arrays Chap. 8 119 The source file tabpos.c: fdefine MAXLINE 100 y+ maximum Line size " ‘define YES ' J+ tabpos: determine if pos is at @ tab stop “ int tabposCint pos, char *tab> ‘ 1 (pos > MAXLINED return YES; else return tablpos}; The framework for this solution is the entab program in Kernighan & Plauger, Software Tools (Addison-Wesley, 1976). Each element in the array tab corresponds to ¢ position within the line, e., tab} corresponds to the first position within the line (pos equals 1). If the position is a tab stop, then the corresponding element tab: } equals YES; otherwise tab(1) equals NO. ‘The tab stops are initialized in the routine settab. If there are no ar- guments (argc equals 1), then we put a tab stop every TABINC position If there are arguments, then we set the tab stops as the user specifies them. The routine entab is similar to Exercise 1-21. The routine taopos determines if the position is a tab stop. It returns YES if pos exceeds MAXLINE, otherwise it returns tab(pos) Finelude ¢stdio.h> #define MAXLINE 100 J+ maximum line size ” ‘define TABINC 8 7+ default tab increment size */ define YES 1 define NO o void settab(int arge, char targv[}, char *tab); void detab(char *tab); int tabposCint pos, char stab); 7+ replace tabs with blanks ” main€int argc, char targv(}) ¢ char tabCMAXLINE +1); settablarge, argv, tab): /* initialize teb stops +/ detab(tab) 7+ replace tab w/ blanks +/ return 0 120 The C Answer Book (* detabs replace tab with blanks void deteb(char *tab) o ‘ int c, pos * 15 while (Ce + geteharQ) t+ EOF) af Ce me NED J+ tab character ” do putchar’ 75 while (tabpos(poses, tab) t+ YES); D else if Cote “\nt) { /+ newline character ” putcharCe); pos = ty > else ( J+ all other characters +/ putchar Ce); +epos; The framework for this solution is the detab program in Kernighan & Plauger, Software Tools (Addison-Wesley, 1976). ‘The routines tabpos and set tab are the same as in the first part of this exercise. ‘The routine de teb is similar to Exercise 1-20. Pointers and Arrays Chap. 5 Exercise §-12: (page 118 K&R) Extend entab \d detad to accept the shorthand entab -m +n to mean tab stops every » columns, st (for the user) default behavior. ‘include define MAXLINE 100 7+ maximum line size adefine TABINC 8 /* default tab increment size fdefine YES 1 fdefine NO o void esetteb(int arge, char sargv{l, char *tab); void enteb(char *tab /* replace strings of blanks with tabs mainCint arge, char eargvl 1) 4 char tabMAXLINE +1}; esettebCarge, ergy, tab); ye initialize tab entab(tab); J+ replace blanks w/ tab reture 05 The source file esettab.c: Finclude (stdlib.h> #define MAXLINE 100 J+ maximum line size ‘define TABINC 8 J+ default tab increment define YES 1 ‘define NO o /* esettad: set teb stops in errey tab void esettebCint argc, char sargv(}, cher +teb> « int i, ine, poss 121 ing at column m. Choose convenient ” ” ” “ ” ” 12 ‘The C Answer Book if Carge «= 1) y+ default tab stops ” for C1 * 15 4 €* MAXLINES 160) af (. & TABING #* 03 teblil = YES; else tablil = Nt else if Carge == 3 bk /* user provided range ” egy) guia) ee 48d ¢ pos + ator CeCe rrargydit1); inc + atorCeCerrargyd( 1195 for Ci * 15 4 <= MAXLINEs 140) if Gt pos? tablil = NO; else ¢ tablil = YES; pos += ines > yelse ¢ /* user provided tab stops +/ for (i * 1: 4 (= MAXLINES ated tablil + NO; /* turn off all tab stops ” while C--arge > 0) {/* walk through argument list +/ pos = atoils++argy); if (pos > 0 64 pos ¢* MAILINED tab(pos) = YES; > ‘The framework for this solution is the entab program in Kernighan & Plauger, Software Tools (Addison-Wesley, 1976). ‘This solution is similar to the entab program in Exercise 5-11. The only modification is that the routine set tab was replaced by esettab (extended settab), eset tab accepts the shorthand notation ~m +n. The statements pos = atoiCacerrargvd(11); ine = atoiCeCersargvr(t 1); set pos equal to the first tab stop and set inc equal to the increment size. Therefore, the tab stops begin at pos and occur at every ine position. Pointers and Arrays Chap. 5 123 finclude €stdio.h> define MAXLINE — 100 f+ maximum line size vy vdefine TABINC 8 y+ default tab increment size */ vaefine YES 1 waefine NO ° void esetiabCint argc, char targvl], cher tab); void detab(cher *tab); /* replace tabs with blanks ” main(int arge, char targyl)) « char tabIMAXLINE+t}; esettablerge, argv, tab); (+ anatialize tad stops +/ detapCtab); /* replace tab w/ blanks +/ return 0: The framework for this solution is the detab program in Kernighan & Plauger. Software Tools (Addison-Wesley, 1976). This solution is similar to the detab program in Exercise 5-11 and uses the routine eset tab from the first part of this exercise 124 The C Answer Book Exercise 5-13: (page 118 K&R) Write the program tai, which prints the last 1 lines of its input. By default, nis 10, let us say, but it can be changed by an optional argument, so that tail include #include define DEFLINES 10 /* default # of lines te print ‘define LINES 100 /+ max # of lines to print define MAXLEN 100 /* max length of an input line void error(char #95 . int getlineCcher *, ant); /* print last n lines of the input main(int argc, char eargyll) 4 char #ps char buf; /+ pointer to large buffer char sbufend; 7+ end of the buffer char line(MAXLEN); /* current input line char *lineptr(LINES}; /* pointers to lines read int first, 1, last, len, n, nlines; Af Carge ** 1) /* no argument present n DEFLINES; Je use default # of lines else if Carge *= 2&4 Cessargu(O) ee 4-79 n= atoiCargv(dl+1); else error(usage: tail (-n)) if (x © 1 Th n> LINES) /* unreasonable value for n? n* LINES; for Ci * 0; 4 © LINES; ited linepte(il = NULL; af (€p = buf = malloc(LINES * MAXLEND) ** NULL? error(*tail: cannot allocate buf"); bufend = buf + LINES * MAXLEN; last = 05 7+ index of last line read nlines = 0 7+ number of lines reed “ ” “ ” ” ” ” ” ” “ ” ” ” Pointers and Arrays Chap. 5 125 while CClen * getline(iine, MAXLEN)? > 09 4 if (pt len + 1 9» bufend) p + buf; 7+ buffer wrap around ” lineptr(last) = ps strepy(lineptel last}, line): if Gtlast >= LINES? last = 05 J» pirs te buffer wrap around +/ pre len+ 45 niines+ +5 > if (n> nlines J+ reg. Lines more than cec.? #/ nt nliness first = last - n; af Cfarst © 0) 7+ at did wrap around the liste/ first += LINES for (i= first; n-- > 0; 1 * Gi + 19 % LINES? prantf(mxs™, Lineptelilds return 0; > /* error: print error message and exit ” void error(char +s) 4 prantfCrnsr exits 135 The program prints the last 1 lines of its input. When erge is 1, nm has the default value DEFLINES. When argc is 2, the value for n is obtained from the command line. It is an error for arge to be greater than 2. ‘The loop while (Clen * getline€line, MAXLEND) > 0) gets a line at a time until gett ine (Exercise 1-16) finds the end of its input For each line read the program uses space in the allocated buffer. The statement if (p+ len + 1 >= bufend? ps buts resets the pointer p to the beginning of the buffer when there is not enough room left to copy the current line. The elements of the array Lineptr point to characters: the last LINES lines read so far. The index for this array is the variable lest When last becomes equal to LINES it wraps around and the elements of 1ineptr and their buffers are then reused. 126 The C Answer Book The total number of lines is n1ines. The program prints the last n lines, so the number of lines requested cannot exceed the number of lines received if (n> plinesd nt nliness If the total number of lines exceeds LINES. the index Last wraps around and the starting index has to be adjusted: if Cirst «0 first += LINES: The program then prints the last 7 lines: for Ci = first; n-- > 0; 2 = (i © 1) % LINESD praintfcmas", ineptr(ilds Since i starts with the value of first and goes for n elements, it may wrap around. The modulus (remainder) operator maintains 1 between the values 0 and LINES-1: 1s G+ 40% LINES The standard library function ex:1 (page 163 K&R) terminates the program when an error occurs. exit returns 1 indicating an error condition. Pointers and Arrays Chap. 5 27 Exercise 5-1 }: (page 121 K&R) Modify the sort program to handle a -r flag, which indicates sorting in reverse (decreasing) order. Be sure that -r works with -n. Finclude ¢stdio.h> include Cstring.h> fdefine NUMERIC 1 /* numeric sort ” define DECR 2 /* sort in decreasing order ” edefine LINES 100 /* max # of lines to be sorted " int numempchar +, char #3; int readlinesCchar slineptr(), int maxiines?; void qsortCchar evi}, int left, int right, int Cscomp void #, void 2; void writelinesCchar #lineptriJ, int plines, int deer); ateatic char option = 7+ sort input lines */ main(int argc, cher targvi? « char *lineptr(LINES}; /* pointers to text lines ” int nlines; 7* number of input lines read */ inte, re + 05 while C--arge > 0 6 Cessargyd[0l s+ 4-79 while Cc = **+ergvl0l) switch Co) ¢ case ‘nt: 7* numeric sort ” option t= NUMERIC; case 7e ort in decreasing order +/ option I= DECR; break: default: printt(sor arge * 15 ree break: : illegel option Ze\n", ¢); 128 ‘The C Answer Book if Carge) printf("Usage: sort -nr \n")5 else if CCnlines + readlines(lineptr, LINES>> > 0) 4 Af Coption & NUMERIC? qsort((void ##) lineptr, 0, nlines-1, Gant (+) (void #, void #9) numemp>s else qsort((void ##) lineptr, 0, nlines-1, Cant C+) Cyoid *, void #9) strempd;, writelinesClineptr, nlines, option & DECR); > else ¢ print{("input too big to sort \n">5 rot ty > return rey , J+ writelines: weite output lines ” void writelines(char ¢lineptr(J, int nlines, int deer? { int as af Cdeer) (+ print in decreasing order */ for Ci + nlines-15 i >= 05 i--) printfC*Zs\n", linepte(s)) else (+ print in inereasing order +/ for (i = 0; 4 © nliness ver) printfOr%s\n", linepte (i195 The bits of the stat ic character variable opt son determine which options are requested. Oth bit = 0 character string sort Ast bit 0 sort in increasing order = I sort in decreasing order ¢-r If there is an option present, then the bitwise inclusive OR operator (1) sets the appropriate bit in the variable option. The statement option t= DECR: is equivalent to option = option t 2; Pointers and Arrays Chap. 5 129 The decimal number 2 is equivatent to 00000010 in binary. Since 1 OR anything = 1 the above C statement sets the Ist bit in the character variable opt ion to 1. (The bits are numbered 0, 1, 2,. . .. from right to left.) To determine if an option is set we use the bitwise AND (4) operator. The expression option & DECK is true if the -r option is requested and false if the -r option is not requested. writel ines was modified to accept a third argument, deer. The variable decr is the result of the expression option & DECR, which determines whether the sorted list is to be printed in decreasing or increasing order. The routines strcmp, numemp, swap, qsort, and readlines are those used in the sort program (page 119 K&R). 130 Exercise 5-15: (page 121 K&R) The C Answer Book Add the option -£ to fold upper and lower case together, so that case distinctions are not made during sorting; for example, e and A compare equal. Finelude ¢stdio.h> include ¢string.h> include sdefine vdefine edefine sdefine int charemp(char #, char #) NUMERIC 1 0 /# DECR 2 fe FOLD 4078 LINES 100 7 int numemp(char #, char *); int readlinesCchar ¢lineptr(J, int maxlines); void qsort(char evil, int left, int right, int Ccomp)(void #, void #395 void writelines(char *lineptri J, static char option * 0; /* sort input lines meinCint arge, char sargvi 1) « char slinepte (LINES); int nlines; int c, re + 05 while C--arge > 0 & numeric sort sort in decreasing order fold upper and lower cases max * of lines to be sorted /* pointers to text 7* number of input lines read Corsargvd (0) == while Ce # #¢sargv(o}) switch Cod ¢ case (Fs option | break; option ¢ break; case fri: option t break; default: printf arge = 1 break: /* fold upper and lower cases + FOLD; 7+ numeric sort = NUMERIC; /* sort in decreasing order » DECR; sort: illegal option %e\ int nlines, int order); lines ” ” ” ” ” ” “ ” ” Pointers and Arrays Chap. 5 134 Af Gorge? printf("Usage: sort -fnr (nM); else ( if C(nlines + readlineslineptr, LINES?) > 0) ¢ Af (option & NUMERIC) qaort({void #*) lineptr, 0, nlines-1, Cint G€void #, void #29 numemp); else if Coption # FOLD) qsort((void ##) lineptr, 0, nlines-1, Cint Gd€void #, void #2) charemp); else qsort((void *#) lineptr, 0, nlines-1, Cint C€void +, void #2) stremp)s writelinesClineptr, niines, cption & DECR); b else « printfC"input too big to sort \n"); ret ty > > return re; > 7+ charcmp: return <0 if st 0 #/ int charcmp(char *s, char *t? « for ( j tolowerCea> = tolowerCety; ste, tre) of Ges ee N00) return 0; return tolowerCes) = tolower Cet); > ‘The framework for this solution is Exercise 5-14. 2nd bit = 0 no folding = 1 folding ¢-#) If the user requests the fold option, the second bit in option must be set equal tol: option t= FOLD: FOLD (decimal 4) is 00000100 in binary (the bits are numbered 0, 1, 2, 3, from right to left). ‘The function charemp compares strings like strcmp (page 106 K&R). It converts characters to lower case, 10 support the FOLD option, and compares them. The routines numemp, swap, qaort, readlines, and writelines are those used in Exercise 5-14. 132 The C Answer Book Exercise 5-16; (page 121 K&R) ‘Add the -d (“directory order”) option, which makes comparisons only on let- ters, numbers, and blanks. Make sure it works in conjunction with -f. finclude ¢stdio.h> #include define NUMERIC 1 — /* numeric sort ” sdefine DECR 2 /* sort in decreasing order ” adefine FOLD 4 /* fold upper and lower cases “ ‘define DIR 8 —/* directory order ” define LINES 100 /# max # of lines to be sorted = */ int charemp(char *, char #); Ant numemp(char *, char #) int readlinesCchar *lineptr(}, int maxlines); void qaort(char *vi}, int left, int right, int Csconp) (void *, void #9); Void writelines(char *lineptr(}, int nlines, int order); static char option * 0; 7+ sort input lines " main¢int arge, char sargvt)) { char *lineptr{LINES}; /* pointers to text lines ” int nliness y+ number of input lines read */ ant c, re # 05 while C--arge > 0 8b Cessargvdtol e# 1-1) while Ce = ##+argvl0)) switch Ce) ¢ case ‘d?: /+ directory order uw option t= DIR; break; case 7f%: 7* fold upper and lower cases */ option t= FOLD: break: Lint: 7* numeric sort “ option t= NUMERIC; breaks case fr’: y+ sort in decreasing order */ option t= DECR; break; Pointers and Arrays Chap. 5 133, default: printf(*sort: illegal option c\n", c); arge = 15 break; » Af Cerge) printf("Usage: sort -dfnr \n"5 else ¢ af C(nlines * readlines€lineptr, LINES)? > 0) ¢ Af Coption & NUMERIC? qsort((void ##) lineptr, 0, nlines-1, Cint CdCvoid #, void #9) numemp); else qrort((void ##) Lineptr, 0, nlines-1,, Cint (void #, void #2) charemp); writelinesClinepte, nlines, option & DECR); > else ¢ printfC'input too big to sort \n"); re = <5 > y return re; » /* chercmp: return <0 1f set, 0 af seat, 90 if art 0 o/ int charcmp(char #5, char +t? ‘ char a, b int fold + Coption & FOLD) 7 1: 0 int cir * Coption & DIRI 7 4: 0; do { if Cdird ¢ while Clisalnum(+s) @& es fs / 4 be es te NOD seth while Clisalnum(et) e@ et te ¢ be et te NOD tees » a + fold ? tolowerCes) : #5; b+ fold 7 tolowerCetd : ety tees Af (ete b aba ne 1107 return bwhile Ce return a - by bs 134 The C Answer Book The framework for this solution is Exercises 5-14 and 5-15. 3rd bit = no directory order 1 directory order (— 4) If the user requests the directory option, the third bit in opt.on is set to L option t+ DIR; DIR (decimal 8) is 00001000 in binary notation (the bits are numbered 0. 1,2, 3,... . from right to left) The cheremp routine (Exercise 5-15) was modified to handle both the fold option and the directory option If the user requests the directory option then the wh: 1e loop while Cisalnumées) 6 es te 1 4 bb oa Ee OD atte examines each character in the string s and skips over those characters that are not letters, numbers, and blanks. The macro 1 se ! numis defined in ¢c type .h> - 1salnum tests for alphabetic characters (a-2, A-2) and digits (0-9). If +s is an alphabetic character or a digit then 1sa1num¢+s) is nonzero; otherwise 1gelnum¢+s) is zero. The next while loop while Clisalnum(st) ae at te oo bb ot te NON) tees examines each character in the string t and skips over those characters that are not letters, numbers, and blanks. When a letter, number, or blank is found in s and a letter, number, or blank is found in 1, the routine charemp compares the two characters. ‘We could have taken another approach and created three functions instead of cherenp: feldemp, dircmp, and folddiremp. foldemp would fold and compare characters as charemp in Exercise 5-15. diremp would compare characters for directory order, and felddiremp would fold and compare char- acters for directory order. Each individual function would be faster than the current charemp. We chose to complicate char emp instead of creating more functions. The routines numcmp, swap, qsort, readlines, and writelines are those used in Exercise 5-14. Pointers and Arrays Chap. 5 135 Exercise 5-17: (page 121 K&R) Add a field-handling capability, so sorting may be done on fields within lines each field sorted according to an independent set of options. (The index for this book was sorted with -df for the index category and -n for the page numbers.) finclude éstdio.h> ‘finclude fdefine NUMERIC 1 /* numeric sort ” ‘define DECR 2 /* sort im decreasing order " ‘define FOLD 4/4 fold upper and lower cases ” define DIR 8 —/* directory order " define LINES 100 /+ max # of lines to be sorted " ant charcmp(char ¥, char #5 void error€char #2; int numempCchar *, char *); void readargs(int argc, char targvl)); int readlines(char ¢lineptr(J, int maxlines); void qsort(char *v{}, int left, int right, ant Cecomp)(void *, void #995 void writelines(char ‘lineptr£}, int nlines, int order); char option = 0; int pest = 0 7+ field beginning with post */ int pos2 = 05 7+ ending just before pos? */ J+ sort input lines ” main(int argc, char sargvl i> 4 char ¢lineptr(LINES]; /* pointers to text lines ” int nlines; y+ number of input lines read */ int re + 05 readergsCargc, argv); if C(nlines = readlinesClineptr, LINES)? > 02 ¢ if Coption & NUMERIC) qsort((void ##) lineptr, 0, nlines-1,, Gint Ced€verd #, voud #9) numemp); else qsort((vord ##) lineptr, 0, nlines-1,, Gint Gr€verd #, void #9) charcmp)s writelinesClineptr, nlines, option & DECR); 136 The C Answer Book yelse ¢ praintf("input too big to sort \n">: ros ty ‘ » return rcs > 7* readargs: read program arguments v void readargsCint argc, char targvi}) re ant ci int atoichar +); while (--arge > 0 && Co = Cereargy)(O)) #8 7-7 the ee HDL AF Ce ee 2 bk HisdigitCeCargvlol+1>9> while Co * sssergvl01) switch Ce) ( case ‘d': 14 directory order ” option t+ DIR: break; case ‘Tf: /* fold upper and lower +/ option t+ FOLD; break; case ‘nf: 7+ numeric sort ” option I= NUMERIC; break; cose fr’: 7+ sort in decr. order #/ option I+ DECR; break; default: printf(sort: illegal option Ze\n", ©); error("Usage: sort -dfnr (+post] (-pos2]" break; y else if Cone 1-4) pos2 = atoiCargvid)+t); else if C(poat = atoslargvidle1)) < o> error("Usage: sort -dfnr (*pos!) [-pos2]"); > Af Carge 11 post > pos2> error€"Usage: sort -dfnr (+post) (-pos2]"); The source file numemp.c: sinclude ¢math.h> Finclude Pointers and Arrays Chap. 5 ‘define MAXSTR = 100 void aubstr(char #3, char ¢t, int maxstr)s 7+ numemp: compare st and s2 numerically int numempCchar *st, cher +52) 4 double v1, v2s char str(MAXSTRI; substr(st, str, MAXSTR): vi = atof(sted; sudstr(a2, str, MAXSTR); v2 * atof(strd; af Gt © v2) return -1; else if Gv! > v2 return 15 else return 05 > fdefine FOLD 4 /* fold upper and lower cases #define DIR 8 —/* directory order Js charcmp: return ¢O if set, O1f set, 20 af art Ant charcmp(char #3, char #1) 4 char e ant i,j, endpos; extern char option; extern int post, pos2; ant fold * Coption # FOLD) 7 1: 0 ant dir * Coption # DIRD 7 1: 05 iy pos Af Gpos2 > 0 endpos * pos2s else if (Cendpos = strien€s)) > strienct>? endpos + strlentt); do ¢ af died € while Ci € endpos 6 fiselnum(sti}) && eC) fet 6 be slid fe 709 while €) € endpos #6 Viselnum(tlj)) o6 Vy) te tt ge tga be NOD we 137 ” ” ” ” 138 The C Answer Book tf C € endpos b& y < endpasd ¢ a+ fold ? tolower€s(ild + sf1); b+ fold ? tolowerCt(jid : tly; sets Af (att bbb aw =e 1N079 return 05 » } while (a == bbs 1 € endpos by € endpos?; return a = b; ‘The source file substr.c: finclude ¢string.h> void error(char #3; /* substr: get @ substring of s and put in str ” void substr(char ¢s, char estr? « int i, j, lens extern int post, pos2s len + atelents); 1f (pos? > 0 48 len > pos?) len = pos2s else if (pos? > 0 && Len < pose) errorC*substr: string too short"); for (#0, 1 * post; 1 lent ite, yee? str()] = stil; strty) = N08 The framework for this solution is Exercises 5-14, 5-15, and 5-16 ‘The syntax of the sort command is sort -dfnr (+pos!] [-pos2] If you want to sort on fields within lines you can specify post and pos2, the sort begins at post and ends just before pos2. Otherwise pos! and pos2 are equal to 0 and the entire line is the sort key. The routine reedargs reads the command line arguments. The while loop in reader gs is true while there are arguments and the arguments preceded by a minus sign. Pointers and Arrays Chap. 5. 139 ‘The first 1f statement If Coss fo? bb NisdigitCeCargv(0le199> is true if the argument is a minus sign followed by a non-digit. The switch statement processes these arguments the same way as in Exercises 5-14, 5-15, and 5-16. The next e1se-1# statement else af (tee 1-4) is true only if the argument specified is the optional -pos2. The final elseif statement processes +pos1 and makes sure itis greater than zero. charcmp is a modified version of the function in the previous exercises This version handles fields. numemp compares numbers like the previous version but requires a new routine substr since atof does not take origin and length as arguments. It is safer to invent a new routine substr rather than to change the interface of a highly used function like atof The routines swap, qsort, readiines, and writelines are those used in Exercise 5-14. error is the function from Exercise 5-13, Exercise 5-18: (page 126 K&R Make de1 recover from input errors. include finclude finclude enum ( NAME, PARENS, BRACKETS }; enum (8G, YES }; void delCvoid); void dirdel(void)s void errmagtchar #); int gettoken(void)s The C Answer Book extern int tokentype:; /+ type of lest token ” extern char tokent]; 7+ last token string ” extern char name( 7+ identifier nane ” extern char out(}; extern int prevtoken; y+ del: parse @ declarator ” void deltvoid) « int nss for (ns = 0; gettoken(> == ‘#75 2 /* count +*s ” nares dirdclOs while (ns-- > 09 streat(out, " pointer tom); > J+ dirdel: parse @ direct declaration ” void dirdel(void) { int types Af Clokentype == 10 ¢ mC deld ” de1O3 Af Ctokentype ts 7049 errmag(error: missing >\n"); } else if Ctokentype == NAMED /* variable name */ strepyCname, token); Pointers and Arrays Chap. 5 1 > else errmsg("error: expected name or (del)\n"); while CCtype = gettoken()) =* PARENS It type == BRACKETS) if Ctype == PARENS) street(out, * function returning"); else ¢ streatCout, “ erray"); stecat(out, token); streatCout, * of"); {+ errmsg: print error message and indicate avail. token +/ void errmag(char #msg) « print®c mag) prevtoken ~ YES; The source file get token.c: vinclude ¢ctype-h> include ¢string.h> enum ( NAME, PARENS, BRACKETS ); enum { NO, YES ¥5 extern int tokentype; /* type of lest token ” extern char tokent]; 7» last token string “ ant prevtoken = NO; 7+ there is no previous token ” J+ gettoten: return next token ” int gettoken(void) ‘ int c, geteh(void?; void ungeteh¢ int); char #p = tokens 1f (prevtoken =* YES? ¢ prevtoken = NO; return tokentypes > while Ce © getchQ>) = 1 6 tt eae NttD 42 The C Answer Book Af fo ee OD if Ce = getchOrd = 979 4 strepy(token, "C0"; return tokentype = PARENS; > else « ungetch€c); return tokentype © °C; , Delse if Ce ne 111) ¢ for Cepes = cz Capes = getchOrd t+ "1%; > op NO; return tokentype » BRACKETS; } else if Cisalphatc>> « for Capes +c} tselnum(s = getch(>); > sper cs tps fl0rs ungetch(e); return totentype = NAME; > else return tokentype = 5 We modified dirde! a little because this function expects one of two tokens: a ’>¢ after a call to det or aname. Ifit is neither of these tokens we invoke errmsg instead of print?. errmag displays the error message and sets prevtoken to indicate to get token that a token is already available. get - token has a new if statement at the beginning: Af Cprevtoken =* YES) ¢ prevtoken = NO; return tokentype: That is, if there is a token available do not get a new one yet. Our modified version is not bullet-proof, but it has an improved error handling capability. Pointers and Arrays Chap. 5 “a Exercise 5-19: (page 126 K&R) Modify unde 1 so that it does not add redundant parentheses to declarations. finclude ¢stdio.h> include ‘include ¢etype.h> define MAXTOKEN 100 enun void void int int ant char char (NAME, PARENS, BRACKETS >; delCvoid); dirdelvoid); gettokenCvoid); nexttoken€void); tokentype; 7 type of last token ” token{MAXTOKEN]; /# last token string ” out( 100025 /* undel: convert word description to declaration ” mang? « int types char temp{MAXTOKEN) ; while (gettoken() '* EOF) ¢ strepyCout, token); while CCtype = gettoken(>) t= *\n‘) af Ctype += PARENS It type == BRACKETS) atreatCout, tokend; else if Ctype a= 787) ¢ Af CCtype = nexttoken()) ** PARENS 11 type += BRACKETS? sprintf(temp, "C#ts)", outd; else sprintf(temp, “ets, out strepyCout, temp); D else if (type ©* NAMED ¢ sprintfCtemp, “Zs Xs", toten, out); strepyCout, temp); ) else printfCinvalid input at ts\n", token); printfC"Esin, outds > return 0; “4 The C Answer Book enum (NO, YES >; int gettokentvoid); J+ nexttoken: get the next token and push it beck ” int nexttokentvois) 4 int types extern int prevtoken; type = gettokenc; prevtoken = YES; return types For the description “x is a pointer to char,” the input to undc1 is x + char and unde 1 produces char (ex) ‘The parentheses are redundant. In fact, the parentheses are required only when the next token is either © or (3 For example, “daytab is a pointer to an array of [13] int,” the input for unde! is daytab + (13) int and unde 1 produces int Cedayteb)(13) which is correct. On the other hand, “daytab is an array of [13] pointers to int,” the input is daytab (132 © int and unde produces ant Cedaytab(131) Thiis time the parentheses are redundant. Pointers and Arrays Chap. 5 145 We modified unde to check if the next token is (2 or (1. If itis © or (1 the parentheses are necessary, otherwise the parentheses are redundant We look ahead one token before we make the decision about adding parentheses. We created a simple function called next token that invokes gettoken, records the fact that there is a token available, and returns the token type gettoken is the function from Exercise 5-18 that checks if there is 2 token already available before it gets a new one from the input The modified unde} does not produce redundant parentheses. For ex- ample, for the input x * char the modified unde1 produces cher ex For dayted + (13) int is int Cedaytabo(13) And for daytab (13) » int int edeytab(13) 146 The C Answer Book Exercise 5-20: (page 126 K&R) Expand de1 to handle declarations with function argument types, qualifiers like const, and so on. ‘include ‘include (atring.h> ‘include enum enum void void void { NAME, PARENS, BRACKETS >; (NO, YES D5 deltvoid); dirdel(void); eremag(char #5 int gettoken(void?; extern int tokentypes —/* type of lest token extern char token{); 7+ last token string extern char namel); (+ identifier name extern char datatype(}; /* date type = char, int, ete. extern char outl; extern int prevtoke y+ del: parse @ declarator void « » del(void? int ns; for (ns + 0; gettokent> ns dirdelO; while (na-- > 09 streatCout, pointer tom); /* dirdel: perse a direct declaration void « dirdel(void) int typ. void parmdel (void); Af Clokentype #9 104) ¢ fe deh > d1O5 Af Clokentype errmsg¢™ san ror: missing)\n™ ” ” ov ” 7 ” ” ” Pointers and Arrays Chap.5 Dd elae if Ctotentype =+ NAMED ( /* verieble name Af Cneme(0J ** *\07) strepyCrame, token); > else previoken = YES; while CCtype + gettoken(>> type == °C") af Clype += PARENS) strcatCout, “function returning"); else if (type #07) ¢ stecatCout, ” function expecting"); parmdcl(; streatCout, d else ¢ streatCout, array"); streatCout, token; atreatCout, * of); and returning > 7* errmsg: print error mes: void errmag(char *msg) 4 ge and indicate avail. token printfCrgs, msg) prevtoken = YES; The source file parnde 1c ‘include ¢stdio,h> @include finclude finclude sdefine MAXTOKEN 100 enum ( NAME, PARENS, BRACKETS >; enum (NO, YES 13 void dcl€voidd; void errmag(char #95 void delspec(void): int typespec(void); int typequel(void)s int compare(char **, char + “7 ” PARENS 11 type == BRACKETS It " “8 The C Answer Book ant gettoken(voidds extern int tokentypes 7+ type of last token ” extern char tokent]; J Last token string ” extern char neme( 1; 7+ identifier name " extern char datetypell; /+ data type * char, int, etc. ” extern char outl); extern int prevtokens /* parndcl: parse a parameter declarator ” void parmdelCvoid) « do ¢ del spect; } while Clokentype == 1,79; af Ctokentype != "972 eremag("missing ) in parameter declaration\n” > 7* delspec: declaration specification ” void delapectveid) 4 char temp(MAXTOKEN) ; tempt} = *\0r; gettokenc>; do ¢ if Ctokentype t+ NAMED ¢ prevtoken = YES; dels + else Af (typespece) += Yess ¢ streat(temp, "> streat(temp, token gettokent>; d else if Ctypequale? == YES) ¢ streatCtemp, : streat(temp, token); gettokent >; > else errmsg(“unknown type in parameter List\n } while Ctokentype !* ‘,/ 88 tokentype != 10); strcatCout, temp); if Clotentype == /,7) streatCout, ","); Pointers and Arrays Chap. 5 ug J+ typespec: return YES if token is a type-specifier ” int typespec(void) 4 stetic cha ys char *pt = token; if CosearchCapt, types, sizeof(types)/sizeof(char sizeof(cher ©), compare) ** NULL) return NO; else return YES; , J* typequal: return YES if token is a type-qualifier ” int typequalvoid> ‘ atetic char *typeqt) = ¢ “conat “volatile* > char *pt * tokens Af CbsearchCept, typeq, sizeof(typeq?/sizeof(char #), sizeof(char +), compare) += NULL) return NO; else return YES; > J+ compare: compare two strings for bsearch ” int compare(char tea, cher #+t) 4 return strempCes, #195 > We expanded the grammar on page 122 K&R to include parameter dec- larators: del: optional *'s direct-del direct-del: name (del) direct-del (optional parm-del) direct-dcl [optional size] 150 The C Answer Book parm-de!: parm-del, del-spec del dcl-spec: type-spec del-spec type-qual det-spec ‘This is an abbreviated version of the part of the grammar that describes declarations. We recognize a few type-specifiers described on page 211 K&R. For example, void.*Cecomp>Cint *, char #, int C+fned€D2 produces comp: pointer to function expecting pointer to int, pointer to char, pointer to function returning int and returning pointer to void ‘We modified the function dirde 1 and added the functions parmde 1 and delspee. ‘We use the lookahead facility we developed for Exercise 5-18, Sometimes we need to peek at a token before we decide what action to take. Sometimes we have a token available that we cannot use yet so we push it back; the next call to get token, elsewhere in the parser, retrieves that same token again and uses it. baeereh is a standard library routine that performs binary search.

You might also like