Changes to Answers to Frequently Asked Questions (FAQ) on comp.lang.c
Steve Summit
scs at adam.mit.edu
Thu Nov 1 16:00:59 AEST 1990
This article contains the changes between the previous posting of
the frequently-asked questions list (October 1) and the new one.
(Do _not_ worry if you have not seen the new one yet; it's coming
up next.)
(These diffs have been edited for readability and are not suitable
for the patch program.)
< This article, which will be reposted periodically, attempts to answer
---
> This article, which is posted monthly, attempts to answer these common
< some of the myths which this article attempts to debunk. Two invaluable
< references, which are an excellent addition to any serious programmer's
< library, are:
<
< The C Programming Language, by Brian W. Kernighan and Dennis M.
< Ritchie.
<
< C: A Reference Manual, by Samuel P. Harbison and Guy L. Steele, Jr.
<
< Both exist in several editions. Andrew Koenig's book _C Traps and
< Pitfalls_ also covers many of the difficulties frequently discussed
< here.
<
---
> some of the myths which this article attempts to debunk. Several
> noteworthy books on C are listed in this article's bibliography.
< please try to answer it by referring to these or other books, or to
< knowledgeable colleagues, before posing your question to the net at
---
> please try to answer it by checking a few of the referenced books, or by
> asking knowledgeable colleagues, before posing your question to the net
< your comments to scs at adam.mit.edu and/or scs%adam.mit.edu at mit.edu; this
< article's From: line may be unuseable.
---
> your comments to scs at adam.mit.edu, scs%adam.mit.edu at mit.edu, and/or
> mit-eddie!adam!scs; this article's From: line may be unusable.
> The questions answered here are divided into several categories:
>
> 1. Null Pointers
> 2. Arrays and Pointers
> 3. Order of Evaluation
> 4. ANSI C
> 5. C Preprocessor
> 6. Variable-Length Argument Lists
> 7. Memory Allocation
> 8. Structures
> 9. Declarations
> 10. Boolean Expressions and Variables
> 11. Operating System Dependencies
> 12. Stdio
> 13. Miscellaneous
< object. That is, the address-of operator & will never "return" a
---
> object. That is, the address-of operator & will never yield a null
< A null pointer is different from an uninitialized pointer. A null
< pointer is known not to point to any object; an uninitialized
---
> A null pointer is conceptually different from an uninitialized
> pointer. A null pointer is known not to point to any object; an
< null pointer is required, so it can make the distinction if
---
> type of null pointer is required, so it can make the distinction if
< A: Not since the early days. Attempting to push pointers into
< integers, or build pointers out of integers, has always been
---
> A: Not since the early days. Attempting to turn pointers into
> integers, or to build pointers out of integers, has always been
> 7. I use the preprocessor macro
>
> #define Nullptr(type) (type *)0
>
> to help me build null pointers of the correct type.
>
> A: This trick, though popular with beginning programmers, does not buy
> much. It is not needed in assignments and comparisons; see question
> 2. It does not even save keystrokes. Its use suggests to the
> reader that the author is shaky on the subject of null pointers, and
> requires the reader to check the #definition of the macro, its
> invocations, and _all_ other pointer usages much more carefully.
< in a non-pointer context generates an integer zero. If the null
< pointer keyword were "nil" the compiler could emit an error message
< for an ambiguous usage, but since it is "0" the compiler may end up
< emitting incorrect code.
---
> in a non-pointer context generates an integer zero instead of an
> error message, and if that uncast 0 was supposed to be a null
> pointer, the code may not work.
< 2. If the usage of "0" or "NULL" is in a function call, cast it to
< the pointer type expected by the function being called.
---
> 2. If the usage of "0" or "NULL" is an argument in a function
> call, cast it to the pointer type expected by the function
> being called.
< pointers, which you shouldn't need to know.
---
> pointers, which you shouldn't need to know. Understand questions 1,
> 2, and 4, and consider 9 and 13, and you'll do fine.
< arrays "turn into" pointers in expressions. That is, when an array
---
> arrays "decay" into pointers in expressions. That is, when an array
< expected to point to the start of an array rather than to a single
< value.
---
> expected to point to the start of an array rather than to some
> single value.
< A: Perhaps no aspect of C is more confusing than pointers, and the
< confusion is compounded by statements like the one above. Saying
< that arrays and pointers are "equivalent" does not by any means
---
> A: Much of the confusion surrounding pointers in C can be traced to
> this statement. Saying that arrays and pointers are "equivalent"
< (The confusion is heightened by incorrect compilers, including some
< versions of pcc and pcc-derived lint's, which incorrectly accept
< assignments of multi-dimensional arrays to multi-level pointers.)
---
> (The confusion is heightened by the existence of incorrect
> compilers, including some versions of pcc and pcc-derived lint's,
> which improperly accept assignments of multi-dimensional arrays to
> multi-level pointers.)
< If a function is already declared as accepting a pointer to a
< pointer, an intermediate pointer would need to be used when
< attempting to call it with a two-dimensional array:
<
< int *ip = &a[0][0];
< g(&ip);
< ...
< g(int **ipp) {...}
<
< Note that this usage is liable to be misleading (if not incorrect),
< since the array has been "flattened" (its shape has been lost).
---
> If a function is already declared as accepting a pointer to a
> pointer, it is probably incorrect to pass a two-dimensional array
> directly to it.
< A: Usually, you don't want one. Think about using a pointer to one of
---
> A: Usually, you don't want to. Consider using a pointer to one of the
> If you really need to declare a pointer to an entire array, use
> something like "int (*ap)[N];" where N is the size of the array. If
> the size of the array is unknown, N can be omitted, but the
> resulting type, "pointer to array of unknown size," is almost
> completely useless.
< resulting "ragged" array often saves space, although it may not be
< contiguous in memory as a real array would be.
---
> resulting "ragged" array often saves space, although it is not
> necessarily contiguous in memory as a real array would be.
< (In "real" code, of course, malloc's return value should be
< checked.)
---
> (In "real" code, of course, each return value from malloc would have
> to be checked.)
< You can keep the array's contents contiguous, while losing the
< ability to have rows of varying and different lengths, with a bit of
---
> You can keep the array's contents contiguous, while making later
> reallocation of individual rows difficult, with a bit of explicit
< value 4. ANSI allows compilers to reject code which contains such
< ambiguous or undefined side effects.
---
> value 4.
>
> The ANSI C standard declares that code which contains such ambiguous
> or undefined side effects is not merely undefined, but illegal.
> Don't even try to find out how your compiler implements such things
> (contrary to the ill-advised exercises in many C textbooks); as K&R
> wisely point out, "if you don't know _how_ they are done on various
> machines, the innocence may help to protect you."
< A: There is a special exception for those operators; each of them does
< imply a sequence point (i.e. left-to-right evaluation is
< guaranteed).
---
> A: There is a special exception for those operators, (as well as ?: );
> each of them does imply a sequence point (i.e. left-to-right
> evaluation is guaranteed). Any book on C should make this clear.
< arduous process, this C standard was finally ratified as an American
---
> arduous process, the committee's work was finally ratified as an
< library support routines, an unprecedented effort.
---
> library support routines.
< The cost is approximately $50.00, plus $6.00 shipping. Quantity
< discounts are available.
---
> The cost from ANSI is $50.00, plus $6.00 shipping. Quantity
> discounts are available. (Note that ANSI derives revenues to
> support its operations from the sale of printed standards, so
> electronic copies are _not_ available.)
< used, but it will not work for floating-point values or pointers.
---
> used, but it will not work for floating-point values or pointers
> (and the "obvious" supercompressed implementation for integral types
> a^=b^=a^=b is, strictly speaking, illegal due to multiple side-
> effects; and it will not work if the two values are the same
> variable, and...).
> 28. I have some old code that tries to construct identifiers with a
> macro like
>
> #define Paste(a, b) a/**/b
>
> but it doesn't work any more.
>
> A: That comments disappeared entirely and could therefore be used for
> token pasting was an undocumented feature of some early preprocessor
> implementations, notably Reiser's. ANSI affirms (as did K&R) that
> comments are replaced with white space. However, since the need for
> pasting tokens was demonstrated and real, ANSI introduced a well-
> defined token-pasting operator, ##, which can be used as follows:
>
> #define Paste(a, b) a##b
>
> Reference: ANSI Sec. 3.8.3.3 p. 91, Rationale pp. 66-7.
< that an apostrophe within a contracted word looks like the beginning
< of a character constant) and no newlines inside quotes. Therefore,
---
> that an apostrophe within a contracted word in a comment looks like
> the beginning of a character constant), and no newlines inside
> 30. What's the best way to write a multi-statement cpp macro?
>
> A: The usual goal is to write a macro that can be invoked as if it were
> a single function-call statement. This means that the "caller" will
> be supplying the final semicolon, so the macro body should not. The
> macro body cannot be a simple brace-delineated compound statement,
> because syntax errors would result if it were invoked (apparently as
> a single statement, but with a resultant an extra semicolon) as the
> if branch of an if/else statement with an explicit else clause.
>
> The best solution is to use
>
> #define Func() do { \
> /* declarations */ \
> stmt1; \
> stmt2; \
> /* ... */ \
> } while(0) /* (no trailing ; ) */
>
> When the "caller" appends a semicolon, this expansion becomes a
> single statement regardless of context. (An optimizing compiler
> will remove any "dead" tests or branches on the constant condition
> 0, although lint may complain.)
>
> If all of the statements in the intended macro are simple
> expressions, a simpler technique is to separate them with commas and
> surround them with parentheses.
>
> Reference: CT&P Sec. 6.3 pp. 82-3.
< extern char *malloc(); /* redundant */
< int len = 0;
---
> size_t len = 0;
< Using the older varargs package, rather than stdarg, requires a few
< changes which are not discussed here, in the interests of brevity.
---
> Under a pre-ANSI compiler, rewrite the function definition without a
> prototype ("char *vstrcat(first) char *first; {"), #include
> <stdio.h> rather than <stddef.h>, replace "#include <stdlib.h>" with
> "extern char *malloc();", and use int instead of size_t. You may
> also have to delete the (void) casts, and use the older varargs
> package instead of stdarg.
> (If you know enough about your machine's architecture, it is
> possible to pick arguments off of the stack "by hand," but there is
> little reason to do so, since portable mechanisms exist.)
> 38. I can't get strcat to work. I tried
>
> #include <string.h>
> main()
> {
> char *s1 = "Hello, ";
> char *s2 = "world!";
> char *s3 = strcat(s1, s2);
> printf("%s\n", s3);
> }
>
> but I got strange results.
>
> A: Again, the problem is that space for the concatenated result is not
> properly allocated. C does not provide a true string type. C
> programmers use char *'s for strings, but must always keep
> allocation in mind. The compiler will only allocate memory for
> objects explicitly mentioned in the source code (in the case of
> "strings," this includes character arrays and string literals). The
> programmer must arrange (explicitly) for sufficient space for the
> results of run-time operations such as string concatenation,
> typically by declaring arrays, or calling malloc.
>
> The simple strcat example could be fixed with something like
>
> char s1[20] = "Hello, ";
> char *s2 = "world!";
>
> Note, however, that strcat appends the string pointed to by its
> second argument to that pointed to by the first, and merely returns
> its first argument, so the s3 variable is superfluous.
>
> Reference: CT&P Sec. 3.2 p. 32.
< struct is pushed on the stack, which may involve significant
< overhead for large structures. It may be preferable in such cases
< to pass a pointer to the structure instead.
---
> struct is typically pushed on the stack, using as many words as are
> required. (Pointers to functions are often chosen precisely to
> avoid this overhead.)
< Structures are returned from functions either in a special, static
< place (which may make struct-valued functions nonreentrant) or in a
< location pointed to by an extra, "hidden" argument to the function.
---
> Structures are typically returned from functions in a location
> pointed to by an extra, "hidden" argument to the function. Older
> compilers often used a special, static location for structure
> returns, although this made struct-valued function nonreentrant,
> which ANSI disallows.
< 45. How do I declare a pointer to a function returning a pointer to a
< double?
---
> 49. How do I declare an array of pointers to functions returning
> pointers to functions returning pointers to characters?
< A: There are at least three answers to this question:
---
> A: This question can be answered in at least three ways (all assume the
> hypothetical array is to have 5 elements):
< 1. double *(*p)();
---
> 1. char *(*(*a[5])())();
< typedef double *pd; /* pointer to double */
< typedef pd fpd(); /* func returning ptr to double */
< typedef fpd *pfpd; /* ptr to func ret ptr to double */
< pfpd p;
---
> typedef char *cp; /* pointer to char */
> typedef cp fpc(); /* function returning pointer to char */
> typedef fpc *pfpc; /* pointer to above */
> typedef pfpc fpfpc(); /* function returning... */
> typedef fpfpc *pfpfpc; /* pointer to... */
> pfpfpc a[5]; /* array of... */
>
< cdecl> declare p as pointer to function returning pointer to double
< double *(*p)();
---
> cdecl> declare a as array 5 of pointer to function returning
> pointer to function returning pointer to char
> char *(*(*a[5])())()
> Any good book on C should explain tricks for reading these
> complicated C declarations "inside out" to understand them
> ("declaration mimics use").
> 51. I finally figured out the syntax for declaring pointers to
> functions, but now how do I initialize one?
>
> A: Use something like
>
> extern int func();
> int (*fp)() = func;
>
> When the name of a function appears in an expression but is not
> being called (i.e. is not followed by a "("), its address is
> implicitly taken, just as is done for arrays.
>
> An explicit extern declaration for the function is normally needed,
> since implicit external function declaration does not happen in this
> case (again, because the function name is not followed by a "(").
< as long as you are consistent within one program or project.
---
> or use raw 1 and 0, as long as you are consistent within one program
> or project.
> or define "helper" macros such as
>
> #define Istrue(e) ((e) != 0)
< _not_ necessarily 1. A good rule of thumb is to use TRUE and FALSE
< (or the like) only for assignment to a Boolean variable or as the
< return value from a Boolean function, never in a comparison.
---
> _not_ necessarily 1. (Besides, if you believe that
> "if((a == b) == TRUE)" is an improvement over "if(a == b)", why stop
> there? Why not use "if(((a == b) == TRUE) == TRUE)"?) A good rule
> of thumb is to use TRUE and FALSE (or the like) only for assignment
> to a Boolean variable or as the return value from a Boolean
> function, never in a comparison.
< comp.lang.c . Several common questions are answered in frequently-
< asked questions postings in the comp.unix.questions and
< comp.sys.ibm.pc newsgroups.
---
> comp.lang.c . Many common questions are answered in frequently-
> asked questions postings in such groups as comp.unix.questions and
> comp.os.msdos.programmer . Note that the answers are often not
> unique even across different versions of Unix. Bear in mind when
> answering system-specific questions that the answer that applies to
> your system may not apply to everyone else's.
< A: In general, it cannot. If the calling process is prepared to listen
< explicitly for some indication that its environment should be
< changed, a special-case scheme can be set up. (Under Unix, a child
< process cannot directly affect its parent at all. Other operating
< systems have different process environments which could
< intrinsically support such communication.)
---
> A: In general, it cannot. Different operating systems implement
> name/value functionality similar to the Unix environment in many
> different ways. Whether the "environment" can be usefully altered
> by a running program, and if so, how, is entirely system-dependent.
>
> Under Unix, a process can modify its own environment (Some systems
> provide setenv() or putenv() functions to do this), and the modified
> environment is passed on to any child processes, but it is not
> propagated back to the parent process. (The environment of the
> parent process can only be altered if the parent is explicitly set
> up to listen for some kind of change requests. The conventional
> execution of the BSD "tset" program in .profile and .login files
> effects such a scheme.)
> 59. How can a file be shortened in-place without completely clearing or
> rewriting it?
>
> A: BSD systems provide ftruncate(), and some MS-DOS compilers supply
> chsize(), but there is no portable solution.
< slightly depending on whether stdout is a terminal or not. To make
< this determination, these implementations perform an operation which
< fails (with ENOTTY) if stdout is not a terminal.
---
> slightly if stdout is a terminal. To make the determination, these
> implementations perform an operation which fails (with ENOTTY) if
> stdout is not a terminal.
< Or you could use fgets() to read a whole line, and then use sscanf()
< or other string functions to parse the line buffer.
---
> Usually the best solution is to use fgets() to read a whole line,
> and then use sscanf() or other string functions to parse the line
> buffer.
< A: Just use sprintf.
---
> A: Just use sprintf. (You'll have to allocate space for the result
> somewhere anyway; see questions 37 and 38.)
< Otherwise, you can try anonymous ftp and/or uucp from a central,
< public-spirited site, such as uunet.uu.net, but this article cannot
< track or list all of the available sites and how to access them.
---
> The usual approach is to use anonymous ftp and/or uucp from a
> central, public-spirited site, such as uunet.uu.net. However, this
> article cannot track or list all of the available sites and how to
> access them.
< might want to print them.
---
> might want to print them. It is hard to imagine why anyone would
> want or need to place a comment inside a quoted string. It is easy
> to imagine a program needing to print "/*".
< A: Several grammars are floating around; keep your eyes open. There is
---
> A: The definitive grammar is of course the one in the ANSI standard.
> Several copies are floating around; keep your eyes open. There is
< 69. Where can I get extra copies of this list?
---
> 75. Where can I get extra copies of this list? What about back issues?
1469,1472c1668,1674
< A: For now, just pull it off the net; it is normally posted on about
< the first of the month, with an Expiration: line which should keep
< it around all month. Eventually, it may be available for anonymous
< ftp, or via a mailserver.
---
> A: For now, just pull it off the net; it is normally posted on the
> first of each month, with an Expiration: line which should keep it
> around all month. Eventually, it may be available for anonymous
> ftp, or via a mailserver. (Note that the size of the list is
> monotonically increasing; older copies are obsolete and don't
> contain anything, except the occasional typo, that the current list
> doesn't.)
> Bibliography
>
> ANSI American National Standard for Information Systems --
> Programming Language -- C, ANSI X3.159-1989.
>
> H&S Samuel P. Harbison and Guy L. Steele, C: A Reference Manual,
> Second Edition, Prentice-Hall, 1987, ISBN 0-13-109802-0.
>
> PCS Mark R. Horton, Portable C Software, Prentice Hall, 1990, ISBN
> 0-13-868050-7.
>
> K&R I Brian W. Kernighan and Dennis M. Ritchie, The C Programming
> Language, Prentice Hall, 1978, ISBN 0-13-110163-3.
>
> K&R II Brian W. Kernighan and Dennis M. Ritchie, The C Programming
> Language, Second Edition, Prentice Hall, 1988, ISBN 0-13-
> 110362-8, 0-13-110370-9.
>
> CT&P Andrew Koenig, C Traps and Pitfalls, Addison-Wesley, 1989, ISBN
> 0-201-17928-8.
>
> There is a more extensive bibliography in the revised Indian Hill style
> guide; see question 74.
< Thanks to Mark Brader, Joe Buehler, Christopher Calabrese, Stephen M.
< Dunn, Tony Hansen, Guy Harris, Karl Heuer, Blair Houghton, Kirk Johnson,
< Andrew Koenig, John Lauro, Christopher Lott, Rich Salz, Joshua Simons,
< and Erik Talvola, who have contributed, directly or indirectly, to this
< article.
---
> Thanks to Mark Brader, Joe Buehler, rayc, Christopher Calabrese, Ray
> Dunn, Stephen M. Dunn, Bjorn Engsig, Doug Gwyn, Tony Hansen, Joe
> Harrington, Guy Harris, Karl Heuer, Blair Houghton, Kirk Johnson, Andrew
> Koenig, John Lauro, Christopher Lott, Evan Manning, Mark Moraes Francois
> Pinard, randall at virginia, Rich Salz, Joshua Simons, Henry Spencer, Erik
> Talvola, Chris Torek, and Freek Wiedijk, who have contributed, directly
> or indirectly, to this article.
More information about the Comp.lang.c
mailing list