I've been compiling a series of pages about the bad functions and
mistakes that occur in C into a book, subsequently, I now have a moderate
Incidentally, that introductory paragraph shows about the quality of
English writing we can expect from the whole book --- not so hot. I
have relatively little complaint with the actual technical content of
the document, but the presentation is awful. At the very /least/ run
a spell-checker on the text, and I'd recommend getting a good reader
to look it over for clarity before unleashing it on the unsuspecting
populace, as it were. </rant>
<rant mode=continue> Oh, and for Pete's sake don't use hard tabs on
Usenet! </ok done now really>
2.1: 'strcpy' is definitely not the first function I'd go to in the
"peril" department. After all, it's perfectly safe when used correctly,
and incredibly hard to mess up. Even in your example, you have to resort
to a function in which you declare a length ('10') and then proceed to
forget you ever declared it.
You should find out how to keep figures and code listings on one page
of the generated PDF, and use that feature liberally. It's hard to read
code that jumps across pages unnecessarily.
The second source listing has an extraneous ':' character in it.
2.2: "wil not" is spelled "will not" on my Red Hat system. I don't
know what system you have. (Google does provide circumstantial evidence
for the "wil not" spelling, 104 to 88.)
The correct fix for this 'printf' error is to use the "%*s" format
specifier instead of the "%s" specifier, and pass 'FOO_STR_LEN' to
'printf' yourself.
Returning 'NULL' from 'CP_strncpy' under any circumstances strikes
me as highly dangerous. Do you know why 'strcpy' and family always
return the destination pointer?
What will the following code do, and why? Try to answer first without
looking at the source code of 'CP_strncpy'.
char arr[10];
CP_strncpy(arr, "hello", strlen("hello"));
2.3: C++ programmers will not like that you use a struct with the
same name as a function.
2.4: Why 'CP_strncpy' but 'zstrncat'? Is there a method to your naming
convention?
Several of your single-line comments spill over and become syntax
errors; this is exactly why Real Programmers[tm] do not use C++-style
comments.
2.5: Are you going to explain /how/ to use 'snprintf' to emulate
'strncpy' (or rather, what you think 'strncpy' ought to be doing)?
I bet you'll have at least one reader who wonders why
snprintf(dst, n, src);
does not always work as expected.
3.1: A more realistic example (i.e., one that would do something
reasonable if it worked) would be helpful here. Your current example
merely loops forever --- who cares what it's doing with memory? Fixing
the "bug" won't make the program work!
Your "safe" usage contains at least one instance of undefined behavior,
not counting the obvious one.
You don't discuss a real-life programming error which I have recently
encountered in my own code --- /twice/!
int k, *pids = NULL;
int pids_len=0, pids_cap=0;
while ((k=getpid()) != NULL) {
/* Do we need to resize the |pids| array? */
if (pids_len >= pids_cap) {
void *v;
pids_cap = pids_cap*2 + 15;
v = realloc(pids, pids_cap);
if (v == NULL) do_error("Out of memory");
pids = v;
}
/* Add the new pid */
pids[pids_len++] = k;
}
/* Process array |pids|, and free it when we're done */
free(pids);
return;
3.2: "Redundant code makes for slower and larger programs." Are
you claiming that the statement 'free(p);' is more redundant than
the statement 'if (p != NULL) free(p);'? I don't think you'll find
many takers.
Listing 3.3 is labeled "Example of segmentation faulting using free,"
but it does not exhibit a segfault --- it's perfectly valid. Remove
the '= NULL' initializer and you've got a case.
Your "graceful exit" uses the non-portable expression 'exit(1)'.
3.4: 'sizeof' is not a function.
You are writing about C99 (since you use C++-style comments in earlier
listings). Therefore you should know that 'sizeof' is no longer strictly
a compile-time construct, when you're dealing with VLA expressions.
Your "As a rule" goes utterly against the comp.lang.c majority opinion.
Read the archives for enlightenment on both sides of this debate.
4.1: Do you know about
if ((p = strchr(buffer, '\n'))) *p = '\0';
4.3: Do you know about the * modifier?
4.4: 'syslog' is not a standard C function. You might want to explain
where it's used; I for one have never heard of it, and have no idea what
'syslog(1, ...)' is supposed to do.
The assumption that large compiles must necessarily generate lots of
spurious warnings is a dangerous one. Consider fixing warnings as they
appear.
5.2: Your example code is right. This would normally be a good thing,
but in this case you're supposed to be illustrating /wrong/ code.
5.3: The original example code for 5.2 will do nicely; consider
both the "bottom-up" case 'min(1&2, 1&3)' and the "top-down"
case '1 + min(1,2)'. Ditto for 5.4.
5.5: Note that the given output is only hypothetical; the program
exhibits undefined behavior.
5.6: Others have already pointed out the error and the textbook
solution. You might also consider addressing the pitfall of
#define foo(x) do { \
bar(x); \
baz(x); \
} while (0) \
#define bar(x) do { \
wubble(x); \
} while (0) \
Yes, it's easy to catch and easy to fix, but then so are many of the
problems you address.
6: "programming ... is not the place to express creativity ..."
Obviously you have never programmed!
6.1: However, note that a source file full of /too/ many characters
can be even worse! I'm sure you can find examples in the newsgroup
archives of instances in which our regulars have dissected newbie
programs redolent with excess parentheses and levels of nesting, only
to discover that the underlying algorithm can be expressed in five
lines of concise and idiomatic code. (By which I do /not/ mean Ben
Pfaff's signature file! When /I/ use the word "idiomatic," it is a
compliment!)
I hope these comments help you improve the book's technical content.
And remember what I said about the proofreader.
-Arthur