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