Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!samsung!uunet!amara!mcdaniel From: mcdaniel@amara.uucp (Tim McDaniel) Newsgroups: comp.lang.c Subject: Review: "Portable C", Rabinowitz and Schaap Message-ID: Date: 20 Mar 90 22:22:26 GMT Sender: news@amara.UUCP Distribution: comp Organization: Applied Dynamics Int'l. Lines: 180 Henry Rabinowitz and Chaim Schaap, "Portable C", Prentice-Hall, Englewood Cliffs, N.J., 1990. ISBN 0-13-685967-4, $30.00, 269 pages. Picoreview: Not quite recommended. Nanoreview: A generally decent book, with a goodly amount of rigor. However, there are enough errors, large and small, that I can't quite recommend buying this book. (In this review, I emphasize text by putting it in uppercase. All such emphasis is my own.) I consider myself to be a C guru (many thanks to Chris Torek, Doug Gwyn, Henry Spencer, et al), and I've read a few portability guides (many thanks to Andrew Koenig's "C Traps and Pitfalls"). I'm sure a newish C programmer would consider "Portable C" to be a very different book than I do. In fact, I have a hard time reviewing this book, because so many of the ideas were already familiar to me. This is generally a pretty decent book. They warn about differences among various implementations, and differences between older imple- mentations and ANSI C. They condense discussions into rules, and list these rules in Appendix A. These rules are generally good. They sink the term "lvalue", and just talk about "modifiable" and "addressable", a welcome clarification indeed! They are careful to point out that a data object has attributes (type, expression, name, value, access permissions) and that intermediate values are data objects with attributes -- again, most welcome! The authors know quite a bit about C. However, there are enough errors, large and small, that I can't quite recommend buying the book. I think the major problem is the treatment of pointers. A large block of formalism for these concepts is developed in Chapter 2, and apparently not used until Chapters 6 and 7. Furthermore, the pointer discussion confused me on first reading, and I think I know what they're trying to say! I also believe that they don't quite understand null pointers. From time to time (e.g. 7.3.2's section header), they refer to "THE null pointer". There's no such animal in C -- there's an unbounded number of null pointers, one per pointer type declared. They say "The only integer VALUE that can be portably assigned to any pointer is 0." (p. 140, paragraph 3). Integer VALUES cannot be assigned to pointers; the integer CONSTANT "0", appearing in a context requiring a pointer to a known type, is converted to a null pointer of that pointer type. (That constraint is mentioned in only two places, one a footnote quoting K&R! Unqualified 0s, as well as 0 in the program text typeface, abound.) In 7.3.2, they condemn the false conception that "*(char *)0" is '\0'. However, in 7.3.3, they condemn "the assumption among some VAX programmers that *NULL is '\0'". NULL has no fixed type (int, void *, or char *, depending on implementation); "*NULL" usually expands to "*0" or "*(void *) 0", which is a syntax error. I think there are some bits of confusion dealing with conversion, such as footnote 3 on page 119, about "int b[3][4];": Note that the expression 'b[0]' refers to an array of 4 integers. That is, 'b[0]' has type 'int [4]'. However, in the context in which it appears in the code fragment above [a value context], like most expressions denoting arrays, it acts as a pointer to the first element of the array it refers to; thus, it has type 'int *'. (They make similar statements about the use of function names.) Perhaps I'm too picky, but I think it's better to think of a conversion: 'b[0]', of type 'int [4]', is implicitly converted to an unnamed constant data object of type 'int *'; the new constant has the address of the first element of the array. I think that this helps clear up subtle issues, such as note 1 in Exercise 6-1. This is unfortunate, because pointers in C is one of the more subtle concepts to master, and possible the one concept that causes the most trouble. There are also a dozen or so small glitches scattered throughout. For example, they give a portable code fragment in section 1.1.3: #include #define VERSION "1.03" /* keep version number as a macro * to make changes easy */ void prVersion(); ... void prVersion() { static char outmsg[20] = "Version #"; printf(strcat(outmsg, VERSION)); } It does seem portable, but it's not really maintainable. - VERSION need not be a constant for the program to work correctly; it could be declared as a "char *" variable instead. Preprocessor names don't follow the normal scope and naming rules, and aren't often visible in a debugger. In My Humble Opinion, they should be avoided wherever possible. - What happens if the version string becomes longer than 10 characters? - What happens if someone puts a "%" in VERSION? Sec. 9.8: ''The following example shows a portable definition for STRINGIZE...: #ifdef __STDC__ #define STRINGIZE(a) #a #else #define STRINGIZE(a) "a" #endif'' If the second definition is so portable, why did ANSI disallow it and give another mechanism? Neither stringize nor token concatenation is portable. Yes, they also give a "portable" CONCAT_TOKEN macro. These definitions are "somewhat more portable". They are NOT "portable" without qualification. In section 3.2, they explicitly suggest/require that function declarations be prototyped but function definitions be non-prototyped. However, rule FUNC2 (3.2.2) says "The actual and formal parameters of a function should have the same types.". They only describe default argument promotions in a footnote. Problems mixing prototypes with non-prototypes have arisen at least twice in comp.lang.c; a substantial discussion of the possible pitfalls is needed. They use "#ifdef __STDC__" to test for ANSI C-ness. ("#if __STDC__ == 1" is more likely to work.) Sec. 4.9: "Floating point types are excluded from use as Booleans because ... Also, floating point zero may not be all-bits zero (false) in some environments." (Irrelevant: Boolean false does NOT mean all-bits zero; "if (e)" means "if ((e) == (typeof(e)) 0)", to borrow a GCCism.) Sec. 8.2.1: one of the reasons they suggest using unique member names is to assure "portability to older compilers". (Are there really compilers that ancient still around? Many of them? Should we care?) Sec. 9.9.2: "lint exists in all UNIX environments". (Just try to use standard lint on ANSI C prototypes.) They credit comp.lang.c and thank "Brian Kernighan [and others] for their careful reading of our manuscripts". This statement is rather surprising. Section 9.10 is totally inexplicable in light of the rest of the book. ''9.10 Porting other people's code It is possible to port a large program written by others without understanding how it works. At most, you may have to read some small sections of code. Here is a step-by-step procedure that has proven effective in porting other people's code. 1. Look at the makefile(s) for the code and adjust all environment-specific settings to fit your environment. 2. Look at all the #ifs and #ifdefs in the code to make sure the flags are defined appropriately for your environment. 3. Try to make the program. Often, it will fail to compile. Make the changes necessary to get it to compile and link. 4. Lint the code. Lint all the source files together if possible. Make appropriate changes in the code. 5. Make the program again. 6. Run the program. If it dies, use a runtime checker for diagnosis, or as a last resort, use a debugger. Repair the program, return to step 3, and repeat.'' "Incredibly naive" was the phrase that came to mind upon reading this section. If porting were so mechanical, what would be the point of the book? I don't think the book is unsalvagable by any means. A second edition could be an excellent book, providing a rigorous view of the C abstract machine model as well as helpful guidelines in writing code. However, it's just not quite up to snuff right now. I recommend Andrew Koenig's book, "C Traps and Pitfalls", Addison-Wesley Publishing Company, Reading, Massachusetts, 1989. ISBN 0-201-17928-8. It's not as ambitious, but it succeeds beautifully. I am also eagerly awaiting Mark Horton's new book, "Portable C Software", Prentice Hall, 1990, ISBN 0-13-868050-7, $32.95. -- Tim McDaniel Applied Dynamics International, Ann Arbor, MI Internet: mcdaniel%amara.uucp@mailgw.cc.umich.edu UUCP: {uunet,sharkey}!amara!mcdaniel