C FAQ - Full

Embed Size (px)

Citation preview

  • 8/8/2019 C FAQ - Full

    1/38

  • 8/8/2019 C FAQ - Full

    2/38

    char *p = 0;if(p != 0)

    However, an argument being passed to a function is not necessarilyrecognizable as a pointer context, and the compiler may not be ableto tell that an unadorned 0 "means" a null pointer. For instance,the Unix system call "execl" takes a variable-length, null-

    pointer-terminated list of character pointer arguments. Togenerate a null pointer in a function call context, an explicitcast is typically required:

    execl("/bin/sh", "sh", "-c", "ls", (char *)0);

    If the (char *) cast were omitted, the compiler would not know topass a null pointer, and would pass an integer 0 instead. (Notethat many Unix manuals get this example wrong.)

    When function prototypes are in scope, argument passing becomes an"assignment context," and most casts may safely be omitted, since

    the prototype tells the compiler that a pointer is required, and ofwhich type, enabling it to correctly cast unadorned 0's. Functionprototypes cannot provide the types for variable arguments invariable-length argument lists, however, so explicit casts arestill required for those arguments. It is safest always to castnull pointer function arguments, to guard against varargs functionsor those without prototypes, to allow interim use of non-ANSIcompilers, and to demonstrate that you know what you are doing.

    Summary:

    Unadorned 0 okay: Explicit cast required:

    initialization function call,no prototype in scopeassignmentvariable argument incomparison varargs function call

    function call,prototype in scope,fixed argument

    References: K&R I Sec. A7.7 p. 190, Sec. A7.14 p. 192; K&R IISec. A7.10 p. 207, Sec. A7.17 p. 209; H&S Sec. 4.6.3 p. 72; ANSISec. 3.2.2.3 .

    3. What is NULL and how is it #defined?

    A: As a matter of style, many people prefer not to have unadorned 0'sscattered throughout their programs. For this reason, thepreprocessor macro NULL is #defined (by or ),with value 0 (or (void *)0, about which more later). A programmerwho wishes to make explicit the distinction between 0 the integerand 0 the null pointer can then use NULL whenever a null pointer isrequired. This is a stylistic convention only; the preprocessorturns NULL back to 0 which is then recognized by the compiler (inpointer contexts) as before. In particular, a cast may still be

    necessary before NULL (as before 0) in a function call argument.(The table under question 2 above applies for NULL as well as 0.)

  • 8/8/2019 C FAQ - Full

    3/38

    NULL should _only_ be used for pointers; see question 8.

    References: K&R I Sec. 5.4 pp. 97-8; K&R II Sec. 5.4 p. 102; H&SSec. 13.1 p. 283; ANSI Sec. 4.1.5 p. 99, Sec. 3.2.2.3 p. 38,Rationale Sec. 4.1.5 p. 74.

    4. How should NULL be #defined on a machine which uses a nonzero bit

    pattern as the internal representation of a null pointer?

    A: Programmers should never need to know the internalrepresentation(s) of null pointers, because they are normally takencare of by the compiler. If a machine uses a nonzero bit patternfor null pointers, it is the compiler's responsibility to generateit when the programmer requests, by writing "0" or "NULL," a nullpointer. Therefore, #defining NULL as 0 on a machine for whichinternal null pointers are nonzero is as valid as on any other,because the compiler must (and can) still generate the machine'scorrect null pointers in response to unadorned 0's seen in pointercontexts.

    5. If NULL were defined as follows:

    #define NULL (char *)0

    wouldn't that make function calls which pass an uncast NULL work?

    A: Not in general. The problem is that there are machines which usedifferent internal representations for pointers to different typesof data. The suggested #definition would make uncast NULLarguments to functions expecting pointers to characters to workcorrectly, but pointer arguments to other types would still beproblematical, and legal constructions such as

    FILE *fp = NULL;

    could fail.

    Nevertheless, ANSI C allows the alternate

    #define NULL (void *)0

    definition for NULL. Besides helping incorrect programs to work(but only on machines with homogeneous pointers, thus questionablyvalid assistance) this definition may catch programs which use NULLincorrectly (e.g. when the ASCII NUL character was reallyintended).

    6. 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 in some circles, does not buy much. Itis not needed in assignments and comparisons; see question 2. Itdoes not even save keystrokes. Its use suggests to the reader thatthe 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.

  • 8/8/2019 C FAQ - Full

    4/38

    7. Is the abbreviated pointer comparison "if(p)" to test for non-nullpointers valid? What if the internal representation for nullpointers is nonzero?

    A: When C requires the boolean value of an expression (in the if,while, for, and do statements, and with the &&, , !, and ?:operators), a false value is produced when the expression compares

    equal to zero, and a true value otherwise. That is, whenever onewrites

    if(expr)

    where "expr" is any expression at all, the compiler essentiallyacts as if it had been written as

    if(expr != 0)

    Substituting the trivial pointer expression "p" for "expr," we have

    if(p) is equivalent to if(p != 0)

    and this is a comparison context, so the compiler can tell that the(implicit) 0 is a null pointer, and use the correct value. Thereis no trickery involved here; compilers do work this way, andgenerate identical code for both statements. The internalrepresentation of a pointer does _not_ matter.

    The boolean negation operator, !, can be described as follows:

    !expr is essentially equivalent to expr?0:1

    It is left as an exercise for the reader to show that

    if(!p) is equivalent to if(p == 0)

    "Abbreviations" such as if(p), though perfectly legal, areconsidered by some to be bad style.

    See also question 71.

    References: K&R II Sec. A7.4.7 p. 204; H&S Sec. 5.3 p. 91; ANSISecs. 3.3.3.3, 3.3.9, 3.3.13, 3.3.14, 3.3.15, 3.6.4.1, and 3.6.5 .

    8. If "NULL" and "0" are equivalent, which should I use?

    A: Many programmers believe that "NULL" should be used in all pointercontexts, as a reminder that the value is to be thought of as apointer. Others feel that the confusion surrounding "NULL" and "0"is only compounded by hiding "0" behind a #definition, and preferto use unadorned "0" instead. There is no one right answer.C programmers must understand that "NULL" and "0" areinterchangeable and that an uncast "0" is perfectly acceptable ininitialization, assignment, and comparison contexts. Any usage of"NULL" (as opposed to "0") should be considered a gentle reminderthat a pointer is involved; programmers should not depend on it(either for their own understanding or the compiler's) fordistinguishing pointer 0's from integer 0's.

    NULL should _not_ be used when another kind of 0 is required, eventhough it might work, because doing so sends the wrong stylistic

  • 8/8/2019 C FAQ - Full

    5/38

  • 8/8/2019 C FAQ - Full

    6/38

    the distinction between the several uses of the term "null" (listedabove) is often overlooked.

    One good way to wade out of the confusion is to imagine that C hada keyword (perhaps "nil", like Pascal) with which null pointerswere requested. The compiler could either turn "nil" into thecorrect type of null pointer, when it could determine the type from

    the source code (as it does with 0's in reality), or complain whenit could not. Now, in fact, in C the keyword for a null pointer isnot "nil" but "0", which works almost as well, except that anuncast "0" in a non-pointer context generates an integer zeroinstead of an error message, and if that uncast 0 was supposed tobe a null pointer, the code may not work.

    12. I'm still confused. I just can't understand all this null pointerstuff.

    A: Follow these two simple rules:

    1. When you want to refer to a null pointer in source code, use"0" or "NULL".

    2. If the usage of "0" or "NULL" is an argument in a functioncall, cast it to the pointer type expected by the functionbeing called.

    The rest of the discussion has to do with other people'smisunderstandings, or with the internal representation of nullpointers, which you shouldn't need to know. Understand questions1, 2, and 3, and consider 8 and 11, and you'll do fine.

    13. Given all the confusion surrounding null pointers, wouldn't it be

    easier simply to require them to be represented internally byzeroes?

    A: If for no other reason, doing so would be ill-advised because itwould unnecessarily constrain implementations which would otherwisenaturally represent null pointers by special, nonzero bit patterns,particularly when those values would trigger automatic hardwaretraps for invalid accesses.

    Besides, what would this requirement really accomplish? Properunderstanding of null pointers does not require knowledge of theinternal representation, whether zero or nonzero. Assuming thatnull pointers are internally zero does not make any code easier towrite (except for a certain ill-advised usage of calloc; seequestion 55). Known-zero internal pointers would not obviate castsin function calls, because the _size_ of the pointer might still bedifferent from that of an int. (If "nil" were used to request nullpointers rather than "0," as mentioned in question 11, the urge toassume an internal zero representation would not even arise.)

    14. Seriously, have any actual machines really used nonzero nullpointers?

    A: "Certain Prime computers use a value different from all-bits-0 to encode the null pointer. Also, some large

    Honeywell-Bull machines use the bit pattern 06000 to encodethe null pointer."

  • 8/8/2019 C FAQ - Full

    7/38

    -- Portable C, by H. Rabinowitz and Chaim Schaap,Prentice-Hall, 1990, page 147.

    The "certain Prime computers" were the segmented 50 series, whichused segment 07777, offset 0 for the null pointer, at least forPL/I. Later models used segment 0, offset 0 for null pointers inC, necessitating new instructions such as TCNP (Test C Null

    Pointer), evidently as a sop to all the extant poorly-written Ccode which made incorrect assumptions.

    The Symbolics Lisp Machine, a tagged architecture, does not evenhave conventional numeric pointers; it uses the pair(basically a nonexistent handle) as a C nullpointer.

    Section 2. Arrays and Pointers

    15. I had the definition char x[6] in one source file, and in another Ideclared extern char *x. Why didn't it work?

    A: The declaration extern char *x simply does not match the actualdefinition. The type "pointer-to-type-T" is not the same as"array-of-type-T." Use extern char x[].

    References: CT&P Sec. 3.3 pp. 33-4, Sec. 4.5 pp. 64-5.

    16. But I heard that char x[] was identical to char *x.

    A: Not at all. (What you heard has to do with formal parameters tofunctions; see question 19.) Arrays are not pointers. The arraydeclaration "char a[6];" requests that space for six characters beset aside, to be known by the name "a." That is, there is a

    location named "a" at which six characters can sit. The pointerdeclaration "char *p;" on the other hand, requests a place whichholds a pointer. The pointer is to be known by the name "p," andcan point to any char (or contiguous array of chars) anywhere.

    As usual, a picture is worth a thousand words. The statements

    char a[] = "hello";char *p = "world";

    would result in data structures which could be represented likethis:

    +---+---+---+---+---+---+a: h e l l o \0 +---+---+---+---+---+---+

    +-----+ +---+---+---+---+---+---+p: *======> w o r l d \0 +-----+ +---+---+---+---+---+---+

    17. You mean that a reference like x[3] generates different codedepending on whether x is an array or a pointer?

    A: Precisely. Referring back to the sample declarations in the

    previous question, when the compiler sees the expression a[3], itemits code to start at the location "a," move three past it, andfetch the character there. When it sees the expression p[3], it

  • 8/8/2019 C FAQ - Full

    8/38

    emits code to start at the location "p," fetch the pointer valuethere, add three to the pointer, and finally fetch the characterpointed to. In the example above, both a[3] and p[3] happen to bethe character 'l', but the compiler gets there differently. (Seealso question 98.)

    18. So what is meant by the "equivalence of pointers and arrays" in C?

    A: Much of the confusion surrounding pointers in C can be traced to amisunderstanding of this statement. Saying that arrays andpointers are "equivalent" does not by any means imply that they areinterchangeable.

    "Equivalence" refers to the following key definition:

    An lvalue of type array-of-T which appears in an expressiondecays (with three exceptions) into a pointer to its firstelement; the type of the resultant pointer is pointer-to-T.

    (The exceptions are when the array is the operand of the sizeof()operator or of the & operator, or is a literal string initializerfor a character array.)

    As a consequence of this definition, there is not really anydifference in the behavior of the "array subscripting" operator []as it applies to arrays and pointers. In an expression of the forma[i], the array reference "a" decays into a pointer, following therule above, and is then subscripted exactly as would be a pointervariable in the expression p[i]. In either case, the expressionx[i] (where x is an array or a pointer) is, by definition, exactlyequivalent to *((x)+(i)).

    References: K&R I Sec. 5.3 pp. 93-6; K&R II Sec. 5.3 p. 99; H&SSec. 5.4.1 p. 93; ANSI Sec. 3.3.2.1, Sec. 3.3.6 .

    19. Then why are array and pointer declarations interchangeable asfunction formal parameters?

    A: Since arrays decay immediately into pointers, an array is neveractually passed to a function. Therefore, any parameterdeclarations which "look like" arrays, e.g.

    f(a)char a[];

    are treated by the compiler as if they were pointers, since that iswhat the function will receive if an array is passed:

    f(a)char *a;

    This conversion holds only within function formal parameterdeclarations, nowhere else. If this conversion bothers you, avoidit; many people have concluded that the confusion it causesoutweighs the small advantage of having the declaration "look like"the call and/or the uses within the function.

    References: K&R I Sec. 5.3 p. 95, Sec. A10.1 p. 205; K&R IISec. 5.3 p. 100, Sec. A8.6.3 p. 218, Sec. A10.1 p. 226; H&SSec. 5.4.3 p. 96; ANSI Sec. 3.5.4.3, Sec. 3.7.1, CT&P Sec. 3.3

  • 8/8/2019 C FAQ - Full

    9/38

    pp. 33-4.

    20. Someone explained to me that arrays were really just constantpointers.

    A: That person did you a disservice. An array name is "constant" inthat it cannot be assigned to, but an array is _not_ a pointer, as

    the discussion and pictures in question 16 should make clear.

    21. I came across some "joke" code containing the "expression"5["abcdef"] . How can this be legal C?

    A: Yes, Virginia, array subscripting is commutative in C. Thiscurious fact follows from the pointer definition of arraysubscripting, namely that a[e] is exactly equivalent to *((a)+(e)),for _any_ expression e and primary expression a, as long as one ofthem is a pointer expression and one is integral. This unsuspectedcommutativity is often mentioned in C texts as if it were somethingto be proud of, but it finds no useful application outside of the

    Obfuscated C Contest (see question 95).

    22. My compiler complained when I passed a two-dimensional array to aroutine expecting a pointer to a pointer.

    A: The rule by which arrays decay into pointers is not appliedrecursively. An array of arrays (i.e. a two-dimensional array inC) decays into a pointer to an array, not a pointer to a pointer.Pointers to arrays can be confusing, and must be treated carefully.(The confusion is heightened by the existence of incorrectcompilers, including some versions of pcc and pcc-derived lint's,which improperly accept assignments of multi-dimensional arrays tomulti-level pointers.) If you are passing a two-dimensional array

    to a function:

    int array[YSIZE][XSIZE];f(array);

    the function's declaration should match:

    f(int a[][XSIZE]) {...}or

    f(int (*ap)[XSIZE]) {...} /* ap is a pointer to an array */

    In the first declaration, the compiler performs the usual implicitparameter rewriting of "array of array" to "pointer to array;" inthe second form the pointer declaration is explicit. Since thecalled function does not allocate space for the array, it does notneed to know the overall size, so the number of "rows," YSIZE, canbe omitted. The "shape" of the array is still important, so the"column" dimension XSIZE (and, for 3- or more dimensional arrays,the intervening ones) must be included.

    If a function is already declared as accepting a pointer to apointer, it is probably incorrect to pass a two-dimensional arraydirectly to it.

    23. How do I declare a pointer to an array?

    A: Usually, you don't want to. Consider using a pointer to one of the

  • 8/8/2019 C FAQ - Full

    10/38

    array's elements instead. Arrays of type T decay into pointers totype T, which is convenient; subscripting or incrementing theresultant pointer accesses the individual members of the array.True pointers to arrays, when subscripted or incremented, step overentire arrays, and are generally only useful when operating onmultidimensional arrays, if at all. (See question 22 above.) Whenpeople speak casually of a pointer to an array, they usually mean a

    pointer to its first element.

    If you really need to declare a pointer to an entire array, usesomething like "int (*ap)[N];" where N is the size of the array.(See also question 66.) If the size of the array is unknown, N canbe omitted, but the resulting type, "pointer to array of unknownsize," is useless.

    24. How can I dynamically allocate a multidimensional array?

    A: It is usually best to allocate an array of pointers, and theninitialize each pointer to a dynamically-allocated "row." The

    resulting "ragged" array can save space, although it is notnecessarily contiguous in memory as a real array would be. Here isa two-dimensional example:

    int **array = (int **)malloc(nrows * sizeof(int *));for(i = 0; i < nrows; i++)array[i] = (int *)malloc(ncolumns * sizeof(int));

    (In "real" code, of course, malloc should be declared correctly,and each return value checked.)

    You can keep the array's contents contiguous, while making laterreallocation of individual rows difficult, with a bit of explicit

    pointer arithmetic:

    int **array = (int **)malloc(nrows * sizeof(int *));array[0] = (int *)malloc(nrows * ncolumns * sizeof(int));for(i = 1; i < nrows; i++)array[i] = array[0] + i * ncolumns;

    In either case, the elements of the dynamic array can be accessedwith normal-looking array subscripts: array[i][j].

    If the double indirection implied by the above schemes is for somereason unacceptable, you can simulate a two-dimensional array witha single, dynamically-allocated one-dimensional array:

    int *array = (int *)malloc(nrows * ncolumns * sizeof(int));

    However, you must now perform subscript calculations manually,accessing the i,jth element with array[i * ncolumns + j]. (A macrocan hide the explicit calculation, but invoking it then requiresparentheses and commas which don't look exactly likemultidimensional array subscripts.)

    25. I have a char * pointer that happens to point to some ints, and Iwant to step it over them. Why doesn't

    ((int *)p)++;

    work?

  • 8/8/2019 C FAQ - Full

    11/38

    A: In C, a cast operator does not mean "pretend these bits have adifferent type, and treat them accordingly;" it is a conversionoperator, and by definition it yields an rvalue, which cannot beassigned to, or incremented with ++. (It was an anomaly in certainversions of pcc that expressions such as the above were everaccepted.) Say what you mean: use

    p = (char *)((int *)p + 1);

    Section 3. Order of Evaluation

    26. Under my compiler, the code

    int i = 7;printf("%d\n", i++ * i++);

    prints 49. Regardless of the order of evaluation, shouldn't itprint 56?

    A: Although the postincrement and postdecrement operators ++ and --perform the operations after yielding the former value, many peoplemisunderstand the implication of "after." It is _not_ guaranteedthat the operation is performed immediately after giving up theprevious value and before any other part of the expression isevaluated. It is merely guaranteed that the update will beperformed sometime before the expression is considered "finished"(before the next "sequence point," in ANSI C's terminology). Inthe example, the compiler chose to multiply the previous value byitself and to perform both increments afterwards.

    The behavior of code which contains ambiguous or undefined side

    effects (including ambiguous embedded assignments) has always beenundefined. (Note, too, that a compiler's choice, especially underANSI rules, for "undefined behavior" may be to refuse to compilethe code.) Don't even try to find out how your compiler implementssuch things (contrary to the ill-advised exercises in many Ctextbooks); as K&R wisely point out, "if you don't know _how_ theyare done on various machines, that innocence may help to protectyou."

    References: K&R I Sec. 2.12 p. 50; K&R II Sec. 2.12 p. 54; ANSISec. 3.3 p. 39; CT&P Sec. 3.7 p. 47; PCS Sec. 9.5 pp. 120-1.(Ignore H&S Sec. 7.12 pp. 190-1, which is obsolete.)

    27. But what about the &&, , and comma operators?I see code like "if((c = getchar()) == EOF c == '\n')" ...

    A: There is a special exception for those operators, (as well as ?: );each of them does imply a sequence point (i.e. left-to-rightevaluation is guaranteed). Any book on C should make this clear.

    References: K&R I Sec. 2.6 p. 38, Secs. A7.11-12 pp. 190-1; K&R IISec. 2.6 p. 41, Secs. A7.14-15 pp. 207-8; ANSI Secs. 3.3.13 p. 52,3.3.14 p. 52, 3.3.15 p. 53, 3.3.17 p. 55, CT&P Sec. 3.7 pp. 46-7.

    Section 4. ANSI C

    28. What is the "ANSI C Standard?"

  • 8/8/2019 C FAQ - Full

    12/38

    A: In 1983, the American National Standards Institute commissioned acommittee, X3J11, to standardize the C language. After a long,arduous process, including several widespread public reviews, thecommittee's work was finally ratified as an American NationalStandard, X3.159-1989, on December 14, 1989, and published in thespring of 1990. For the most part, ANSI C standardizes existingpractice, with a few additions from C++ (most notably function

    prototypes) and support for multinational character sets (includingthe much-lambasted trigraph sequences). The ANSI C standard alsoformalizes the C run-time library support routines.

    The published Standard includes a "Rationale," which explains manyof its decisions, and discusses a number of subtle points,including several of those covered here. (The Rationale is "notpart of ANSI Standard X3.159-1989, but is included for informationonly.")

    The Standard has been adopted as an international standard, ISO/IEC9899:1990, although the Rationale is currently not included.

    29. How can I get a copy of the Standard?

    A: Copies are available from

    American National Standards Institute1430 BroadwayNew York, NY 10018 USA(+1) 212 642 4900

    or

    Global Engineering Documents

    2805 McGaw AvenueIrvine, CA 92714 USA(+1) 714 261 1455(800) 854 7179 (U.S. & Canada)

    The cost from ANSI is $50.00, plus $6.00 shipping. Quantitydiscounts are available. (Note that ANSI derives revenues tosupport its operations from the sale of printed standards, soelectronic copies are _not_ available.)

    The Rationale, by itself, has been printed by Silicon Press, ISBN0-929306-07-4.

    30. Does anyone have a tool for converting old-style C programs to ANSIC, or for automatically generating prototypes?

    A: Two programs, protoize and unprotoize, convert back and forthbetween prototyped and "old style" function definitions anddeclarations. (These programs do _not_ handle full-blowntranslation between "Classic" C and ANSI C.) These programs existas patches to the FSF GNU C compiler, gcc. Look for the fileprotoize-1.39.0 in pub/gnu at prep.ai.mit.edu (18.71.0.38), or atseveral other FSF archive sites.

    Several prototype generators exist, many as modifications to lint.

    (See also question 94.)

    31. What's the difference between "char const *p" and "char * const p"?

  • 8/8/2019 C FAQ - Full

    13/38

    A: "char const *p" is a pointer to a constant character (you can'tchange the character); "char * const p" is a constant pointer to a(variable) character (i.e. you can't change the pointer). (Readthese "inside out" to understand them. See question 66.)

    32. My ANSI compiler complains about a mismatch when it sees

    extern int func(float);

    int func(x)float x;{...

    A: You have mixed the new-style prototype declaration"extern int func(float);" with the old-style definition"int func(x) float x;". Old C (and ANSI C, in the absence ofprototypes) silently promotes floats to doubles when passing themas arguments, and arranges that doubles being passed are coerced

    back to floats if the formal parameters are declared that way.

    The problem can be fixed either by using new-style syntaxconsistently in the definition:

    int func(float x) { ... }

    or by changing the new-style prototype declaration to match theold-style definition:

    extern int func(double);

    (In this case, it would be clearest to change the old-style

    definition to use double as well, as long as the address of thatparameter is not taken.)

    Reference: ANSI Sec. 3.3.2.2 .

    33. I'm getting strange syntax errors inside code which I've #ifdeffedout.

    A: Under ANSI C, the text inside a "turned off" #if, #ifdef, or#ifndef must still consist of "valid preprocessing tokens." Thismeans that there must be no unterminated comments or quotes (noteparticularly that an apostrophe within a contracted word could looklike the beginning of a character constant), and no newlines insidequotes. Therefore, natural-language comments and pseudocode shouldalways be written between the "official" comment delimiters /* and*/. (But see also question 96.)

    References: ANSI Sec. 2.1.1.2 p. 6, Sec. 3.1 p. 19 line 37.

    34. Can I declare main as void, to shut off these annoying "mainreturns no value" messages? (I'm calling exit(), so main doesn'treturn.)

    A: No. main must be declared as returning an int, and as takingeither zero or two arguments (of the appropriate type). If you're

    calling exit() but still getting warnings, you'll have to insert aredundant return statement (or use a "notreached" directive, ifavailable).

  • 8/8/2019 C FAQ - Full

    14/38

    References: ANSI Sec. 2.1.2.2.1 pp. 7-8.

    35. Why does the ANSI Standard not guarantee more than six monocasecharacters of external identifier significance?

    A: The problem is older linkers which are neither under the control of

    the ANSI standard nor the C compiler developers on the systemswhich have them. The limitation is only that identifiers be_significant_ in the first six characters, not that they berestricted to six characters in length. This limitation isannoying, but certainly not unbearable, and is marked in theStandard as "obsolescent," i.e. a future revision will likely relaxit.

    This concession to current, restrictive linkers really had to bemade, no matter how vehemently some people oppose it. (TheRationale notes that its retention was "most painful.") If youdisagree, or have thought of a trick by which a compiler burdened

    with a restrictive linker could present the C programmer with theappearance of more significance in external identifiers, read theexcellently-worded section 3.1.2 in the X3.159 Rationale (seequestion 28), which discusses several such schemes and explains whythey could not be mandated.

    References: ANSI Sec. 3.1.2 p. 21, Sec. 3.9.1 p. 96, RationaleSec. 3.1.2 pp. 19-21.

    36. What was noalias and what ever happened to it?

    A: noalias was another type qualifier, in the same syntactic class asconst and volatile, which was intended to assert that the object

    pointed to was not also pointed to ("aliased") by other pointers.It was phenomenally difficult to define precisely and explaincoherently, and sparked widespread, acrimonious debate. Because ofthe criticism and the difficulty of defining noalias well, theCommittee wisely declined to adopt it, in spite of its superficialattractions.

    References: ANSI Sec. 3.9.6 .

    37. What are #pragmas and what are they good for?

    A: The #pragma directive provides a single, well-defined "escapehatch" which can be used for all sorts of implementation-specificcontrols and extensions: source listing control, structure packing,warning suppression (like the old lint /* NOTREACHED */ comments),etc.

    References: ANSI Sec. 3.8.6 .

    Section 5. C Preprocessor

    38. How can I write a generic macro to swap two values?

    A: There is no good answer to this question. If the values areintegers, a well-known trick using exclusive-OR could perhaps be

    used, but it will not work for floating-point values or pointers,(and it will not work if the two values are the same variable, andthe "obvious" supercompressed implementation for integral types

  • 8/8/2019 C FAQ - Full

    15/38

    a^=b^=a^=b is, strictly speaking, illegal due to multiple side-effects, and...). If the macro is intended to be used on values ofarbitrary type (the usual goal), it cannot use a temporary, sinceit does not know what type of temporary it needs, and standard Cdoes not provide a typeof operator.

    The best all-around solution is probably to forget about using a

    macro, unless you don't mind passing in the type as a thirdargument.

    39. I have some old code that tries to construct identifiers with amacro like

    #define Paste(a, b) a/**/b

    but it doesn't work any more.

    A: That comments disappeared entirely and could therefore be used fortoken pasting was an undocumented feature of some early

    preprocessor implementations, notably Reiser's. ANSI affirms (asdid K&R) that comments are replaced with white space. However,since the need for pasting tokens was demonstrated and real, ANSIintroduced a well-defined token-pasting operator, ##, which can beused like this:

    #define Paste(a, b) a##b

    Reference: ANSI Sec. 3.8.3.3 p. 91, Rationale pp. 66-7.

    40. 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 bodyshould not. The macro body cannot be a simple brace-delineatedcompound statement, because syntax errors would result if it wereinvoked (apparently as a single statement, but with a resultantextra semicolon) as the if branch of an if/else statement with anexplicit else clause.

    The traditional solution is to use

    #define Func() do { \/* declarations */ \stmt1; \stmt2; \/* ... */ \} while(0) /* (no trailing ; ) */

    When the "caller" appends a semicolon, this expansion becomes asingle statement regardless of context. (An optimizing compilerwill remove any "dead" tests or branches on the constant condition0, although lint may complain.)

    If all of the statements in the intended macro are simpleexpressions, with no declarations or loops, another technique is towrite a single, parenthesized expression using one or more comma

    operators. (This technique also allows a value to be "returned.")

    Reference: CT&P Sec. 6.3 pp. 82-3.

  • 8/8/2019 C FAQ - Full

    16/38

    41. How can I write a cpp macro which takes a variable number ofarguments?

    A: One popular trick is to define the macro with a single argument,and call it with a double set of parentheses, which appear to thepreprocessor to indicate a single argument:

    #define DEBUG(args) {printf("DEBUG: "); printf args;}

    if(n != 0) DEBUG(("n is %d\n", n));

    The obvious disadvantage is that the caller must always remember touse the extra parentheses. (It is often best to use a bona-fidefunction, which can take a variable number of arguments in a well-defined way, rather than a macro. See questions 42 and 43 below.)

    Section 6. Variable-Length Argument Lists

    42. How can I write a function that takes a variable number ofarguments?

    A: Use the header (or, if you must, the older ).

    Here is a function which concatenates an arbitrary number ofstrings into malloc'ed memory:

    #include /* for NULL, size_t */#include /* for va_ stuff */#include /* for strcat et al */#include /* for malloc */

    char *vstrcat(char *first, ...){size_t len = 0;char *retbuf;va_list argp;char *p;

    if(first == NULL)return NULL;

    len = strlen(first);

    va_start(argp, first);

    while((p = va_arg(argp, char *)) != NULL)len += strlen(p);

    va_end(argp);

    retbuf = malloc(len + 1); /* +1 for trailing \0 */

    if(retbuf == NULL)return NULL; /* error */

    (void)strcpy(retbuf, first);

    va_start(argp, first);

  • 8/8/2019 C FAQ - Full

    17/38

    while((p = va_arg(argp, char *)) != NULL)(void)strcat(retbuf, p);

    va_end(argp);

    return retbuf;}

    Usage is something like

    char *str = vstrcat("Hello, ", "world!", (char *)NULL);

    Note the cast on the last argument. (Also note that the callermust free the returned, malloc'ed storage.)

    Under a pre-ANSI compiler, rewrite the function definition withouta prototype ("char *vstrcat(first) char *first; {"), includerather than , replace "#include "with "extern char *malloc();", and use int instead of size_t. You

    may also have to delete the (void) casts, and use the older varargspackage instead of stdarg. See the next question for hints.

    References: K&R II Sec. 7.3 p. 155, Sec. B7 p. 254; H&S Sec. 13.4pp. 286-9; ANSI Secs. 4.8 through 4.8.1.3 .

    43. How can I write a function that takes a format string and avariable number of arguments, like printf, and passes them toprintf to do most of the work?

    A: Use vprintf, vfprintf, or vsprintf.

    Here is an "error" routine which prints an error message, preceded

    by the string "error: " and terminated with a newline:

    #include#include

    voiderror(char *fmt, ...){va_list argp;fprintf(stderr, "error: ");va_start(argp, fmt);vfprintf(stderr, fmt, argp);va_end(argp);fprintf(stderr, "\n");}

    To use the older package, instead of , changethe function header to:

    void error(va_alist)va_dcl{char *fmt;

    change the va_start line to

    va_start(argp);

  • 8/8/2019 C FAQ - Full

    18/38

    and add the line

    fmt = va_arg(argp, char *);

    between the calls to va_start and vfprintf. (Note that there is nosemicolon after va_dcl.)

    References: K&R II Sec. 8.3 p. 174, Sec. B1.2 p. 245; H&SSec. 17.12 p. 337; ANSI Secs. 4.9.6.7, 4.9.6.8, 4.9.6.9 .

    44. How can I discover how many arguments a function was actuallycalled with?

    A: This information is not available to a portable program. Somesystems provide a nonstandard nargs() function, but its use isquestionable, since it typically returns the number of wordspushed, not the number of arguments. (Floating point values andstructures are usually passed as several words.)

    Any function which takes a variable number of arguments must beable to determine from the arguments themselves how many of themthere are. printf-like functions do this by looking for formattingspecifiers (%d and the like) in the format string (which is whythese functions fail badly if the format string does not match theargument list). Another common technique (useful when thearguments are all of the same type) is to use a sentinel value(often 0, -1, or an appropriately-cast null pointer) at the end ofthe list (see the execl and vstrcat examples under questions 2 and42 above).

    45. How can I write a function which takes a variable number ofarguments and passes them to some other function (which takes a

    variable number of arguments)?

    A: In general, you cannot. You must provide a version of that otherfunction which accepts a va_list pointer, as does vfprintf in theexample above. If the arguments must be passed directly as actualarguments (not indirectly through a va_list pointer) to anotherfunction which is itself variadic (for which you do not have theoption of creating an alternate, va_list-accepting version) noportable solution is possible. (The problem can be solved byresorting to machine-specific assembly language.)

    Section 7. Lint

    46. I just typed in this program, and it's acting strangely. Can yousee anything wrong with it?

    A: Try running lint first. Many C compilers are really only half-compilers, electing not to diagnose numerous source codedifficulties which would not actively preclude code generation.

    47. How can I shut off the "warning: possible pointer alignmentproblem" message lint gives me for each call to malloc?

    A: The problem is that traditional versions of lint do not know, andcannot be told, that malloc "returns a pointer to space suitably

    aligned for storage of any type of object." It is possible toprovide a pseudoimplementation of malloc, using a #define inside of#ifdef lint, which effectively shuts this warning off, but a

  • 8/8/2019 C FAQ - Full

    19/38

    simpleminded #definition will also suppress meaningful messagesabout truly incorrect invocations. It may be easier simply toignore the message, perhaps in an automated way with grep -v.

    48. Where can I get an ANSI-compatible lint?

    A: A product called FlexeLint is available (in "shrouded source form,"

    for compilation on 'most any system) from

    Gimpel Software3207 Hogarth LaneCollegeville, PA 19426 USA(+1) 215 584 4261

    The System V release 4 lint is ANSI-compatible, and is availableseparately (bundled with other C tools) from Unix Support Labs (asubsidiary of AT&T), or from System V resellers.

    Section 8. Memory Allocation

    49. Why doesn't this fragment work?

    char *answer;printf("Type something:\n");gets(answer);printf("You typed \"%s\"\n", answer);

    A: The pointer variable "answer," which is handed to the gets functionas the location into which the response should be stored, has notbeen set to point to any valid storage. That is, we cannot saywhere the pointer "answer" points. (Since local variables are notinitialized, and typically contain garbage, it is not even

    guaranteed that "answer" starts out as a null pointer. Seequestion 85.)

    The simplest way to correct the question-asking program is to use alocal array, instead of a pointer, and let the compiler worry aboutallocation:

    #include

    char answer[100], *p;printf("Type something:\n");fgets(answer, 100, stdin);if((p = strchr(answer, '\n')) != NULL)*p = '\0';printf("You typed \"%s\"\n", answer);

    Note that this example also uses fgets instead of gets (always agood idea), so that the size of the array can be specified, so thatfgets will not overwrite the end of the array if the user types anoverly-long line. (Unfortunately for this example, fgets does notautomatically delete the trailing \n, as gets would.) It wouldalso be possible to use malloc to allocate the answer buffer,and/or to parameterize its size (#define ANSWERSIZE 100).

    50. I can't get strcat to work. I tried

    char *s1 = "Hello, ";char *s2 = "world!";

  • 8/8/2019 C FAQ - Full

    20/38

    char *s3 = strcat(s1, s2);

    but I got strange results.

    A: Again, the problem is that space for the concatenated result is notproperly allocated. C does not provide an automatically-managedstring type. C compilers only allocate memory for objects

    explicitly mentioned in the source code (in the case of "strings,"this includes character arrays and string literals). Theprogrammer must arrange (explicitly) for sufficient space for theresults of run-time operations such as string concatenation,typically by declaring arrays, or by calling malloc.

    strcat performs no allocation; the second string is appended to thefirst one, in place. Therefore, one fix would be to declare thefirst string as an array with sufficient space:

    char s1[20] = "Hello, ";

    Since strcat returns the value of its first argument (s1, in thiscase), the s3 variable is superfluous.

    Reference: CT&P Sec. 3.2 p. 32.

    51. But the man page for strcat says that it takes two char *'s asarguments. How am I supposed to know to allocate things?

    A: In general, when using pointers you _always_ have to considermemory allocation, at least to make sure that the compiler is doingit for you. If a library routine's documentation does notexplicitly mention allocation, it is usually the caller's problem.

    The Synopsis section at the top of a Unix-style man page can bemisleading. The code fragments presented there are closer to thefunction definition used by the call's implementor than theinvocation used by the caller. In particular, many routines whichaccept pointers (e.g. to structs or strings), are usually calledwith the address of some object (a struct, or an array -- seequestions 18 and 19.) Another common example is stat().

    52. You can't use dynamically-allocated memory after you free it, canyou?

    A: No. Some early man pages for malloc stated that the contents offreed memory was "left undisturbed;" this ill-advised guarantee wasnever universal and is not required by ANSI.

    Few programmers would use the contents of freed memorydeliberately, but it is easy to do so accidentally. Consider thefollowing (correct) code for freeing a singly-linked list:

    struct list *listp, *nextp;for(listp = base; listp != NULL; listp = nextp) {nextp = listp->next;free((char *)listp);}

    and notice what would happen if the more-obvious loop iterationexpression listp = listp->next were used, without the temporarynextp pointer.

  • 8/8/2019 C FAQ - Full

    21/38

    References: ANSI Rationale Sec. 4.10.3.2 p. 102; CT&P Sec. 7.10p. 95.

    53. How does free() know how many bytes to free?

    A: The malloc/free package remembers the size of each block it

    allocates and returns, so it is not necessary to remind it of thesize when freeing.

    54. Is it legal to pass a null pointer as the first argument torealloc()? Why would you want to?

    A: ANSI C sanctions this usage (and the related realloc(..., 0), whichfrees), but several earlier implementations do not support it, soit is not widely portable. Passing an initially-null pointer torealloc can make it easier to write a self-starting incrementalallocation algorithm.

    References: ANSI Sec. 4.10.3.4 .

    55. What is the difference between calloc and malloc? Is it safe touse calloc's zero-fill guarantee for pointer and floating-pointvalues? Does free work on memory allocated with calloc, or do youneed a cfree?

    A: calloc(m, n) is essentially equivalent to

    p = malloc(m * n);memset(p, 0, m * n);

    The zero fill is all-bits-zero, and does not therefore guarantee

    useful zero values for pointers (see questions 1-14) or floating-point values. free can (and should) be used to free the memoryallocated by calloc.

    References: ANSI Secs. 4.10.3 to 4.10.3.2 .

    56. What is alloca and why is its use discouraged?

    A: alloca allocates memory which is automatically freed when thefunction which called alloca returns. That is, memory allocatedwith alloca is local to a particular function's "stack frame" orcontext.

    alloca cannot be written portably, and is difficult to implement onmachines without a stack. Its use is problematical (and theobvious implementation on a stack-based machine fails) when itsreturn value is passed directly to another function, as infgets(alloca(100), 100, stdin).

    For these reasons, alloca cannot be used in programs which must bewidely portable, no matter how useful it might be.

    Section 9. Structures

    57. I heard that structures could be assigned to variables and passed

    to and from functions, but K&R I says not.

    A: What K&R I said was that the restrictions on struct operations

  • 8/8/2019 C FAQ - Full

    22/38

    would be lifted in a forthcoming version of the compiler, and infact struct assignment and passing were fully functional inRitchie's compiler even as K&R I was being published. Although afew early C compilers lacked struct assignment, all moderncompilers support it, and it is part of the ANSI C standard, sothere should be no reluctance to use it.

    References: K&R I Sec. 6.2 p. 121; K&R II Sec. 6.2 p. 129; H&SSec. 5.6.2 p. 103; ANSI Secs. 3.1.2.5, 3.2.2.1, 3.3.16 .

    58. How does struct passing and returning work?

    A: When structures are passed as arguments to functions, the entirestruct is typically pushed on the stack, using as many words as arerequired. (Pointers to structures are often chosen precisely toavoid this overhead.)

    Structures are typically returned from functions in a locationpointed to by an extra, compiler-supplied "hidden" argument to the

    function. Older compilers often used a special, static locationfor structure returns, although this made struct-valued functionsnonreentrant, which ANSI C disallows.

    Reference: ANSI Sec. 2.2.3 p. 13.

    59. The following program works correctly, but it dumps core after itfinishes. Why?

    struct list{char *item;struct list *next;

    }

    /* Here is the main program. */

    main(argc, argv)...

    A: A missing semicolon causes the compiler to believe that mainreturns a struct list. (The connection is hard to see because ofthe intervening comment.) Since struct-valued functions areusually implemented by adding a hidden return pointer, thegenerated code for main() actually expects three arguments,although only two were passed (in this case, by the C start-upcode). See also question 101.

    Reference: CT&P Sec. 2.3 pp. 21-2.

    60. Why can't you compare structs?

    A: There is no reasonable way for a compiler to implement structcomparison which is consistent with C's low-level flavor. A byte-by-byte comparison could be invalidated by random bits present inunused "holes" in the structure (such padding is used to keep thealignment of later fields correct). A field-by-field comparisonwould require unacceptable amounts of repetitive, in-line code for

    large structures.

    If you want to compare two structures, you must write your own

  • 8/8/2019 C FAQ - Full

    23/38

    function to do so. C++ would let you arrange for the == operatorto map to your function.

    References: K&R II Sec. 6.2 p. 129; H&S Sec. 5.6.2 p. 103; ANSIRationale Sec. 3.3.9 p. 47.

    61. I came across some code that declared a structure like this:

    struct name{int namelen;char name[1];};

    and then did some tricky allocation to make the name array act likeit had several elements. Is this legal and/or portable?

    A: This technique is popular, although Dennis Ritchie has called it"unwarranted chumminess with the compiler." The ANSI C standard

    allows it only implicitly. It seems to be portable to all knownimplementations. (Compilers which check array bounds carefullymight issue warnings.)

    62. How can I determine the byte offset of a field within a structure?

    A: ANSI C defines the offsetof macro, which should be used ifavailable; see . If you don't have it, a suggestedimplementation is

    #define offsetof(type, mem) ((size_t) \((char *)&((type *) 0)->mem - (char *)((type *) 0)))

    This implementation is not 100% portable; some compilers maylegitimately refuse to accept it.

    See the next question for a usage hint.

    Reference: ANSI Sec. 4.1.5 .

    63. How can I access structure fields by name at run time?

    A: Build a table of names and offsets, using the offsetof() macro.The offset of field b in struct a is

    offsetb = offsetof(struct a, b)

    If structp is a pointer to an instance of this structure, and b isan int field with offset as computed above, b's value can be setindirectly with

    *(int *)((char *)structp + offsetb) = value;

    Section 10. Declarations

    64. How do you decide which integer type to use?

    A: If you might need large values (above 32767 or below -32767), use

    long. Otherwise, if space is very important (there are largearrays or many structures), use short. Otherwise, use int. Ifwell-defined overflow characteristics are important and/or negative

  • 8/8/2019 C FAQ - Full

    24/38

    values are not, use the corresponding unsigned types. (But bewaremixtures of signed and unsigned.)

    Similar arguments apply when deciding between float and double.Exceptions apply if the address of a variable is taken and musthave a particular type.

    Although char or unsigned char can be used as a "tiny" int type,doing so is often more trouble than it's worth.

    65. I can't seem to define a linked list successfully. I tried

    typedef struct{char *item;NODEPTR next;} *NODEPTR;

    but the compiler gave me error messages. Can't a struct in C

    contain a pointer to itself?

    A: Structs in C can certainly contain pointers to themselves; thediscussion and example in section 6.5 of K&R make this clear. Theproblem with this example is that the NODEPTR typedef is notcomplete at the point where the "next" field is declared. To fixit, first give the structure a tag ("struct node"). Then, declarethe "next" field as "struct node next;", and/or move the typedefdeclaration wholly before or wholly after the struct declaration.One fixed version would be

    struct node{

    char *item;struct node *next;};

    typedef struct node *NODEPTR;

    , and there at least three other equivalently correct ways ofarranging it.

    A similar problem, with a similar solution, can arise whenattempting to declare a pair of typedef'ed mutually recursivestructures.

    References: K&R I Sec. 6.5 p. 101; K&R II Sec. 6.5 p. 139; H&SSec. 5.6.1 p. 102; ANSI Sec. 3.5.2.3 .

    66. How do I declare an array of pointers to functions returningpointers to functions returning pointers to characters?

    A: This question can be answered in at least three ways (all assumethe hypothetical array is to have 5 elements):

    1. char *(*(*a[5])())();

    2. Build the declaration up in stages, using typedefs:

    typedef char *pc; /* pointer to char */typedef pc fpc(); /* function returning pointer to char */

  • 8/8/2019 C FAQ - Full

    25/38

    typedef fpc *pfpc; /* pointer to above */typedef pfpc fpfpc(); /* function returning... */typedef fpfpc *pfpfpc; /* pointer to... */pfpfpc a[5]; /* array of... */

    3. Use the cdecl program, which turns English into C and viceversa:

    cdecl> declare a as array 5 of pointer to function returningpointer to function returning pointer to charchar *(*(*a[5])())()

    cdecl can also explain complicated declarations, help withcasts, and indicate which set of parentheses the arguments goin (for complicated function definitions, like the above).

    Any good book on C should explain how to read these complicated Cdeclarations "inside out" to understand them ("declaration mimicsuse").

    Reference: H&S Sec. 5.10.1 p. 116.

    67. So where can I get cdecl?

    A: Several public-domain versions are available. One is in volume 14of comp.sources.unix . (See question 94.)

    Reference: K&R II Sec. 5.12 .

    68. I finally figured out the syntax for declaring pointers tofunctions, 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 notbeing called (i.e. is not followed by a "("), it "decays" into apointer (i.e. it has its address implicitly taken), much as anarray name does.

    An explicit extern declaration for the function is normally needed,since implicit external function declaration does not happen inthis case (again, because the function name is not followed by a"(").

    69. I've seen different methods used for calling through pointers tofunctions. What's the story?

    A: Originally, a pointer to a function had to be "turned into" a"real" function, with the * operator (and an extra pair ofparentheses, to keep the precedence straight), before calling:

    int r, f(), (*fp)() = f;r = (*fp)();

    Another analysis holds that functions are always called throughpointers, but that "real" functions decay implicitly into pointers(in expressions, as they do in initializations) and so cause no

  • 8/8/2019 C FAQ - Full

    26/38

    trouble. This reasoning, which was adopted in the ANSI standard,means that

    r = fp();

    is legal and works correctly, whether fp is a function or a pointerto one. (The usage has always been unambiguous; there is nothing

    you ever could have done with a function pointer followed by anargument list except call through it.) An explicit * is harmless,and still allowed (and recommended, if portability to oldercompilers is important).

    References: ANSI Sec. 3.3.2.2 p. 41, Rationale p. 41.

    Section 11. Boolean Expressions and Variables

    70. What is the right type to use for boolean values in C? Why isn'tit a standard type? Should #defines or enums be used for the trueand false values?

    A: C does not provide a standard boolean type, because picking oneinvolves a space/time tradeoff which is best decided by theprogrammer. (Using an int for a boolean may be faster, while usingchar may save data space.)

    The choice between #defines and enums is arbitrary and not terriblyinteresting. Use any of

    #define TRUE 1 #define YES 1#define FALSE 0 #define NO 0

    enum bool {false, true}; enum bool {no, yes};

    or use raw 1 and 0, as long as you are consistent within oneprogram or project. (An enum may be preferable if your debuggerexpands enum values when examining variables.)

    Some people prefer variants like

    #define TRUE (1==1)#define FALSE (!TRUE)

    or define "helper" macros such as

    #define Istrue(e) ((e) != 0)

    These don't buy anything (see below).

    71. Isn't #defining TRUE to be 1 dangerous, since any nonzero value isconsidered "true" in C? What if a built-in boolean or relationaloperator "returns" something other than 1?

    A: It is true (sic) that any nonzero value is considered true in C,but this applies only "on input", i.e. where a boolean value isexpected. When a boolean value is generated by a built-inoperator, it is guaranteed to be 1 or 0. Therefore, the test

    if((a == b) == TRUE)

    will work as expected (as long as TRUE is 1), but it is obviously

  • 8/8/2019 C FAQ - Full

    27/38

    silly. In general, explicit tests against TRUE and FALSE areundesirable, because some library functions (notably isupper,isalpha, etc.) return, on success, a nonzero value which is _not_necessarily 1. (Besides, if you believe that"if((a == b) == TRUE)" is an improvement over "if(a == b)", whystop there? Why not use "if(((a == b) == TRUE) == TRUE)"?) A goodrule of thumb is to use TRUE and FALSE (or the like) only for

    assignment to a Boolean variable, or as the return value from aBoolean function, never in a comparison.

    The preprocessor macros TRUE and FALSE (and, of course, NULL) areused for code readability, not because the underlying values mightever change. That "true" is 1 and "false" 0 is guaranteed by thelanguage. (See also question 7.)

    References: K&R I Sec. 2.7 p. 41; K&R II Sec. 2.6 p. 42,Sec. A7.4.7 p. 204, Sec. A7.9 p. 206; ANSI Secs. 3.3.3.3, 3.3.8,3.3.9, 3.3.13, 3.3.14, 3.3.15, 3.6.4.1, 3.6.5; Achilles and theTortoise.

    72. What is the difference between an enum and a series of preprocessor#defines?

    A: At the present time, there is little difference. Although manypeople might have wished otherwise, the ANSI standard says thatenumerations may be freely intermixed with integral types, withouterrors. (If such intermixing were disallowed without explicitcasts, judicious use of enums could catch certain programmingerrors.)

    The primary advantages of enums are that the numeric values areautomatically assigned, and that a debugger may be able to display

    the symbolic values when enum variables are examined. (A compilermay also generate nonfatal warnings when enums and ints areindiscriminately mixed, since doing so can still be considered badstyle even though it is not strictly illegal). A disadvantage isthat the programmer has little control over the size (or over thosenonfatal warnings).

    References: K&R II Sec. 2.3 p. 39, Sec. A4.2 p. 196; H&S Sec. 5.5p. 100; ANSI Secs. 3.1.2.5, 3.5.2, 3.5.2.2 .

    Section 12. Operating System Dependencies

    73. How can I read a single character from the keyboard without waitingfor a newline?

    A: Contrary to popular belief and many people's wishes, this is not aC-related question. The delivery of characters from a "keyboard"to a C program is a function of the operating system in use, andcannot be standardized by the C language. Some versions of curseshave a cbreak() function which does what you want. Under UNIX, useioctl to play with the terminal driver modes (CBREAK or RAW under"classic" versions; ICANON, c_cc[VMIN] and c_cc[VTIME] under SystemV or Posix systems). Under MS-DOS, use getch(). Under VMS, trythe Screen Management (SMG$) routines. Under other operatingsystems, you're on your own. Beware that some operating systems

    make this sort of thing impossible, because character collectioninto input lines is done by peripheral processors not under directcontrol of the CPU running your program.

  • 8/8/2019 C FAQ - Full

    28/38

    Operating system specific questions are not appropriate forcomp.lang.c . Many common questions are answered in frequently-asked questions postings in such groups as comp.unix.questions andcomp.sys.ibm.pc.misc . Note that the answers are often not uniqueeven across different variants of a system. Bear in mind whenanswering system-specific questions that the answer that applies to

    your system may not apply to everyone else's.

    References: PCS Sec. 10 pp. 128-9, Sec. 10.1 pp. 130-1.

    74. How can I find out if there are characters available for reading(and if so, how many)? Alternatively, how can I do a read thatwill not block if there are no characters available?

    A: These, too, are entirely operating-system-specific. Some versionsof curses have a nodelay() function. Depending on your system, youmay also be able to use "nonblocking I/O", or a system call named"select", or the FIONREAD ioctl, or kbhit(), or rdchk(), or the

    O_NDELAY option to open() or fcntl().

    75. How can my program discover the complete pathname to the executablefile from which it was invoked?

    A: argv[0] may contain all or part of the pathname, or it may containnothing. You may be able to duplicate the command languageinterpreter's search path logic to locate the executable if thename in argv[0] is present but incomplete. However, there is noguaranteed or portable solution.

    76. How can a process change an environment variable in its caller?

    A: In general, it cannot. Different operating systems implementname/value functionality similar to the Unix environment indifferent ways. Whether the "environment" can be usefully alteredby a running program, and if so, how, is system-dependent.

    Under Unix, a process can modify its own environment (some systemsprovide setenv() and/or putenv() functions to do this), and themodified environment is usually passed on to any child processes,but it is _not_ propagated back to the parent process.

    77. How can a file be shortened in-place without completely clearing orrewriting it?

    A: BSD systems provide ftruncate(), several others supply chsize(),and a few may provide a (possibly undocumented) fcntl optionF_FREESP, but there is no truly portable solution.

    Section 13. Stdio

    78. Why does errno contain ENOTTY after a call to printf?

    A: Many implementations of the stdio package adjust their behaviorslightly if stdout is a terminal. To make the determination, theseimplementations perform an operation which fails (with ENOTTY) ifstdout is not a terminal. Although the output operation goes on to

    complete successfully, errno still contains ENOTTY.

    Reference: CT&P Sec. 5.4 p. 73.

  • 8/8/2019 C FAQ - Full

    29/38

    79. My program's prompts and intermediate output don't always show upon the screen, especially when I pipe the output through anotherprogram.

    A: It is best to use an explicit fflush(stdout) whenever output shoulddefinitely be visible. Several mechanisms attempt to perform the

    fflush for you, at the "right time," but they tend to apply onlywhen stdout is a terminal. (See question 78.)

    80. When I read from the keyboard with scanf(), it seems to hang untilI type one extra line of input.

    A: scanf() was designed for free-format input, which is seldom whatyou want when reading from the keyboard. In particular, "\n" in aformat string means, not to expect a newline, but to read anddiscard characters as long as each is a whitespace character.

    It is usually better to fgets() to read a whole line, and then use

    sscanf() or other string functions to parse the line buffer.

    81. How can I recover the file name given an open file descriptor?

    A: This problem is, in general, insoluble. Under Unix, for instance,a scan of the entire disk, (perhaps requiring special permissions)would theoretically be required, and would fail if the filedescriptor was a pipe or referred to a deleted file (and could givea misleading answer for a file with multiple links). It is best toremember the names of open files yourself (perhaps with a wrapperfunction around fopen).

    Section 14. Style

    82. Here's a neat trick:

    if(!strcmp(s1, s2))

    Is this good style?

    A: No. This is a classic example of C minimalism carried to anobnoxious degree. The test succeeds if the two strings are equal,but its form strongly suggests that it tests for inequality.

    A much better solution is to use a macro:

    #define Streq(s1, s2) (strcmp(s1, s2) == 0)

    83. What's the best style for code layout in C?

    A: K&R, while providing the example most often copied, also supply agood excuse for avoiding it:

    The position of braces is less important, althoughpeople hold passionate beliefs. We have chosen oneof several popular styles. Pick a style that suitsyou, then use it consistently.

    It is more important that the layout chosen be consistent (withitself, and with nearby or common code) than that it be "perfect."If your coding environment (i.e. local custom or company policy)

  • 8/8/2019 C FAQ - Full

    30/38

    does not suggest a style, and you don't feel like inventing yourown, just copy K&R. (The tradeoffs between various indenting andbrace placement options can be exhaustively and minutely examined,but don't warrant repetition here. See also the Indian Hill StyleGuide.)

    Reference: K&R Sec. 1.2 p. 10.

    84. Where can I get the "Indian Hill Style Guide" and other codingstandards?

    A: Various documents are available for anonymous ftp from:

    Site: File or directory:

    cs.washington.edu ~ftp/pub/cstyle.tar.Z(128.95.1.4) (the updated Indian Hill guide)

    cs.toronto.edu doc/programming

    giza.cis.ohio-state.edu pub/style-guide

    Section 15. Miscellaneous

    85. What can I safely assume about the initial values of variableswhich are not explicitly initialized? If global variables startout as "zero," is that good enough for null pointers and floating-point zeroes?

    A: Variables with "static" duration (that is, those declared outsideof functions, and those declared with the storage class static),are guaranteed initialized to zero, as if the programmer had typed

    "= 0". Therefore, such variables are initialized to the nullpointer (of the correct type) if they are pointers, and to 0.0 ifthey are floating-point.

    Variables with "automatic" duration (i.e. local variables withoutthe static storage class) start out containing garbage, unless theyare explicitly initialized. Nothing useful can be predicted aboutthe garbage.

    Dynamically-allocated memory obtained with malloc and realloc isalso likely to contain garbage, and must be initialized by thecalling program, as appropriate. Memory obtained with calloccontains all-bits-0, but this is not necessarily useful for pointeror floating-point values (see question 55).

    86. I'm trying to sort an array of strings with qsort, using strcmp asthe comparison function, but it's not working.

    A: By "array of strings" you probably mean "array of pointers tochar." The arguments to qsort's comparison function are pointersto the objects being sorted, in this case, pointers to pointers tochar. The arguments are expressed as "generic pointers," void * orchar *. They must be cast back to what they "really are" (char **)and dereferenced, yielding char *'s which can be usefully compared.Write a comparison function like this:

    int pstrcmp(p1, p2) /* compare strings through pointers */char *p1, *p2; /* void * for ANSI C */

  • 8/8/2019 C FAQ - Full

    31/38

    {return strcmp(*(char **)p1, *(char **)p2);}

    87. Now I'm trying to sort an array of structures with qsort. Mycomparison routine takes pointers to structures, but the compilercomplains it's of the wrong type for qsort. How can I cast the

    function pointer to shut off the warning?

    A: The casts must be in the comparison function, which must bedeclared as accepting "generic pointers" (void * or char *).

    88. Can someone tell me how to write itoa (the inverse of atoi)?

    A: Just use sprintf. (You'll have to allocate space for the resultsomewhere anyway; see questions 49 and 50. Don't worry thatsprintf may be overkill, potentially wasting run time or codespace; it works well in practice.)

    References: K&R I Sec. 3.6 p. 60; K&R II Sec. 3.6 p. 64.

    89. I know that the library routine localtime will convert a time_tinto a broken-down struct tm, and that ctime will convert a time_tto a printable string. How can I perform the inverse operations ofconverting a struct tm or a string into a time_t?

    A: ANSI C specifies a library routine, mktime, which converts astruct tm to a time_t. Several public-domain versions of thisroutine are available in case your compiler does not support ityet.

    Converting a string to a time_t is harder, because of the wide

    variety of date and time formats which should be parsed. Public-domain routines have been written for performing this function(see, for example, the file partime.c, widely distributed with theRCS package), but they are less likely to become standardized.

    References: K&R II Sec. B10 p. 256; H&S Sec. 20.4 p. 361; ANSISec. 4.12.2.3 .

    90. How can I write data files which can be read on other machines withdifferent word size, byte order, or floating point formats?

    A: The best solution is to use text files (usually ASCII), writtenwith fprintf and read with fscanf or the like. (Similar advicealso applies to network protocols.) Be skeptical of argumentswhich imply that text files are too big, or that reading andwriting them is too slow. Not only is their efficiency frequentlyacceptable in practice, but the advantages of being able tomanipulate them with standard tools can be overwhelming.

    If you must use a binary format, you can improve portability, andperhaps take advantage of prewritten I/O libraries, by making useof standardized formats such as Sun's XDR, OSI's ASN.1, or CCITT'sX.409 .

    91. I seem to be missing the system header file . Can someone

    send me a copy?

    A: Standard headers exist in part so that definitions appropriate to

  • 8/8/2019 C FAQ - Full

    32/38

    your compiler, operating system, and processor can be supplied.You cannot just pick up a copy of someone else's header file andexpect it to work, unless that person is using exactly the sameenvironment. Ask your compiler vendor why the file was notprovided (or to send a replacement copy).

    92. How can I call Fortran (BASIC, Pascal, ADA, lisp) functions from C?

    (And vice versa?)

    A: The answer is entirely dependent on the machine and the specificcalling sequences of the various compilers in use, and may not bepossible at all. Read your compiler documentation very carefully;sometimes there is a "mixed-language programming guide," althoughthe techniques for passing arguments and ensuring correct run-timestartup are often arcane.

    93. Does anyone know of a program for converting Pascal (Fortran, lisp,"Old" C, ...) to C?

    A: Several public-domain programs are available:

    p2c written by Dave Gillespie, and posted to comp.sources.unixin March, 1990 (Volume 21).

    ptoc another comp.sources.unix contribution, this one written inPascal (comp.sources.unix, Volume 10, also patches in Volume13?).

    f2c jointly developed by people from Bell Labs, Bellcore, andCarnegie Mellon. To find about f2c, send the mail message"send index from f2c" to [email protected] orresearch!netlib. (It is also available via anonymous ftp on

    research.att.com, in directory dist/f2c.)

    A PL/M to C converter was posted to alt.sources in April, 1991.

    The following companies sell various translation tools andservices:

    Cobalt Blue2940 Union Ave., Suite CSan Jose, CA 95124 USA(+1) 408 723 0474

    Promula Development Corp.3620 N. High St., Suite 301Columbus, OH 43214 USA(+1) 614 263 5454

    Micro-Processor Services Inc92 Stone Hurst LaneDix Hills, NY 11746 USA(+1) 519 499 4461

    See also question 30.

    94. Where can I get copies of all these public-domain programs?

    A: If you have access to Usenet, see the regular postings in thecomp.sources.unix and comp.sources.misc newsgroups, which describe,

  • 8/8/2019 C FAQ - Full

    33/38

    in some detail, the archiving policies and how to retrieve copies.The usual approach is to use anonymous ftp and/or uucp from acentral, public-spirited site, such as uunet.uu.net (192.48.96.2).However, this article cannot track or list all of the availablearchive sites and how to access them. The comp.archives newsgroupcontains numerous announcements of anonymous ftp availability ofvarious items. The "archie" mailserver can tell you which

    anonymous ftp sites have which packages; send the mail message"help" to [email protected] for information.

    95. When will the next International Obfuscated C Contest (IOCCC) beheld? How can I get a copy of the current and previous winningentries?

    A: The contest typically runs from early March through mid-May. Toobtain a current copy of the rules, send email to:

    {pacbell,uunet,utzoo}!hoptoad!judges or [email protected]

    Contest winners are first announced at the Summer Usenix Conferencein mid-June, and posted to the net in July. Previous winners areavailable on uunet (see question 94) under the directory~/pub/ioccc.

    96. Why don't C comments nest? Are they legal inside quoted strings?

    A: Nested comments would cause more harm than good, mostly because ofthe possibility of accidentally leaving comments unclosed byincluding the characters "/*" within them. For this reason, it isusually better to "comment out" large sections of code, which mightcontain comments, with #ifdef or #if 0 (but see question 33).

    The character sequences /* and */ are not special within double-quoted strings, and do not therefore introduce comments, because aprogram (particularly one which is generating C code as output)might want to print them.

    Reference: ANSI Rationale Sec. 3.1.9 p. 33.

    97. How can I make this code more efficient?

    A: Efficiency, though a favorite comp.lang.c topic, is not importantnearly as often as people tend to think it is. Most of the code inmost programs is not time-critical. When code is not time-critical, it is far more important that it be written clearly andportably than that it be written maximally efficiently. (Rememberthat computers are very, very fast, and that even "inefficient"code can run without apparent delay.)

    It is notoriously difficult to predict what the "hot spots" in aprogram will be. When efficiency is a concern, it is important touse profiling software to determine which parts of the programdeserve attention. Often, actual computation time is swamped byperipheral tasks such as I/O and memory allocation, which can besped up by using buffering and cacheing techniques.

    For the small fraction of code that is time-critical, it is vital

    to pick a good algorithm; it is less important to "microoptimize"the coding details. Many of the "efficient coding tricks" whichare frequently suggested (e.g. substituting shift operators for

  • 8/8/2019 C FAQ - Full

    34/38

    multiplication by powers of two) are performed automatically byeven simpleminded compilers. Heavyhanded "optimization" attemptscan make code so bulky that performance is degraded.

    For more discussion of efficiency tradeoffs, as well as good adviceon how to increase efficiency when it is important, see chapter 7of Kernighan and Plauger's The Elements of Programming Style, and

    Jon Bentley's Writing Efficient Programs.

    98. Are pointers really faster than arrays? Do function calls reallyslow things down? Is ++i faster than i = i + 1?

    A: Precise answers to these and many similar questions depend ofcourse on the processor and compiler in use. If you simply mustknow, you'll have to time test programs carefully. (Often thedifferences are so slight that hundreds of thousands of iterationsare required even to see them. Check the compiler's assemblylanguage output, if available, to see if two purported alternativesaren't compiled identically.)

    It is "usually" faster to march through large arrays with pointersrather than array subscripts, but for some processors the reverseis true.

    Function calls, though obviously incrementally slower than in-linecode, contribute so much to modularity and code clarity that thereis rarely good reason to avoid them.

    Before rearranging expressions such as i = i + 1, remember that youare dealing with a C compiler, not a keystroke-programmablecalculator. A good compiler will generate identical code for ++i,i += 1, and i = i + 1. The reasons for using ++i or i += 1 over

    i = i + 1 have to do with style, not efficiency.

    99. My floating-point calculations are acting strangely and giving medifferent answers on different machines.

    A: Most digital computers use floating-point formats which provide aclose but by no means exact simulation of real number arithmetic.Among other things, the associative and distributive laws do nothold completely (i.e. order of operation may be important, repeatedaddition is not necessarily equivalent to multiplication).Underflow or cumulative precision loss is often a problem.

    Don't assume that floating-point results will be exact, andespecially don't assume that floating-point values can be comparedfor equality. (Don't throw haphazard "fuzz factors" in, either.)

    These problems are no worse for C than they are for any othercomputer language. Floating-point semantics are usually defined as"however the processor does them;" otherwise a compiler for amachine without the "right" model would have to do prohibitivelyexpensive emulations.

    This article cannot begin to list the pitfalls associated with, andworkarounds appropriate for, floating-point work. A goodprogramming text should cover the basics. Do make sure that you

    have #included , and correctly declared other functionsreturning double.

  • 8/8/2019 C FAQ - Full

    35/38

    References: K&P Sec. 6 pp. 115-8.

    100. I'm having trouble with a Turbo C program which crashes and sayssomething like "floating point not loaded."

    A: Some compilers for small machines, including Turbo C (and Ritchie'soriginal PDP-11 compiler), leave out floating point support if it

    looks like it will not be needed. In particular, the non-floating-point versions of printf and scanf save space by notincluding code to handle %e, %f, and %g. It happens that Turbo C'sheuristics for determining whether the program uses floating pointare occasionally insufficient, and the programmer must sometimesinsert a dummy explicit floating-point call to force loading offloating-point support.

    In general, questions about a particular compiler are inappropriatefor comp.lang.c . Problems with PC compilers, for instance, willfind a more receptive audience in a PC newsgroup (e.g.comp.os.msdos.programmer).

    101. This program crashes before it even runs! (When single-steppingwith a debugger, it dies before the first statement in main.)

    A: You probably have one or more very large (kilobyte or more) localarrays. Many systems have fixed-size stacks, and those whichperform dynamic stack allocation automatically (e.g. Unix) can beconfused when the stack tries to grow by a huge chunk all at once.

    It is often better to declare large arrays with static duration(unless of course you need a fresh set with each recursive call).

    (See also question 59.)

    102. Does anyone have a C compiler test suite I can use?

    A: Plum Hall (1 Spruce Ave., Cardiff, NJ 08232, USA), among others,sells one.

    103. Where can I get a YACC grammar for C?

    A: The definitive grammar is of course the one in the ANSI standard.Several copies are floating around; keep your eyes open. There isone on uunet.uu.net (192.48.96.2) in net.sources/ansi.c.grammar.Z .The FSF's GNU C compiler contains a grammar, as does the appendixto K&R II.

    References: ANSI Sec. A.2 .

    104. How do you pronounce "char"? What's that funny name for the "#"character?

    A: You can pronounce the C keyword "char" like the English words"char," "care," or "car;" the choice is arbitrary. Bell Labs onceproposed the (now obsolete) term "octothorpe" for the "#"character.

    Trivia questions like these aren't any more pertinent for

    comp.lang.c than they are for any of the other groups theyfrequently come up in. You can find lots of information in thenet.announce.newusers frequently-asked questions postings, the

  • 8/8/2019 C FAQ - Full

    36/38

    "jargon file" (also published as _The Hacker's Dictionary_), andthe Usenet ASCII pronunciation list.

    105. Where can I get extra copies of this list? What about back issues?

    A: For now, just pull it off the net; it is normally posted tocomp.lang.c on the first of each month, with an Expiration: line

    which should keep it around all month. Eventually, it may beavailable for anonymous ftp, or via a mailserver.

    This list is an evolving document, not just a collection of thismonth's interesting questions. Older copies are obsolete and don'tcontain much, except the occasional typo, that the current listdoesn't.

    Bibliography

    ANSI American National Standard for Information Systems --Programming Language -- C, ANSI X3.159-1989 (see question 29).

    Jon Louis Bentley, Writing Efficient Programs, Prentice-Hall,1982, ISBN 0-13-970244-X.

    H&S Samuel P. Harbison and Guy L. Steele, C: A Reference Manual,Second Edition, Prentice-Hall, 1987, ISBN 0-13-109802-0. (Athird edition has recently been released.)

    PCS Mark R. Horton, Portable C Software, Prentice Hall, 1990, ISBN0-13-868050-7.

    K&P Brian W. Kernighan and P.J. Plauger, The Elements of ProgrammingStyle, Second Edition, McGraw-Hill, 1978, ISBN 0-07-034207-5.

    K&R I Brian W. Kernighan and Dennis M. Ritchie, The C ProgrammingLanguage, Prentice Hall, 1978, ISBN 0-13-110163-3.

    K&R II Brian W. Kernighan and Dennis M. Ritchie, The C ProgrammingLanguage, 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, ISBN0-201-17928-8.

    There is a more extensive bibliography in the revised Indian Hill styleguide (see question 84).

    Acknowledgements

    Thanks to Jamshid Afshar, Sudheer Apte, Dan Bernstein, Joe Buehler,Raymond Chen, Christopher Calabrese, James Davies, Norm Diamond, RayDunn, Stephen M. Dunn, Bjorn Engsig, Ron Guilmette, Doug Gwyn, TonyHansen, Joe Harrington, Guy Harris, Blair Houghton, Kirk Johnson,AndrewKoenig, John Lauro, Christopher Lott, Tim McDaniel, Evan Manning, MarkMoraes, Francois Pinard, randall@virginia, Pat Rankin, Rich Salz, ChipSalzenberg, Paul Sand, Doug Schmidt, Patricia Shanahan, Peter da Silva,Joshua Simons, Henry Spencer, Erik Talvola, Clarke Thatcher, Chris

    Torek, Ed Vielmetti, Larry Virden, Freek Wiedijk, and Dave Wolverton,who have contributed, directly or indirectly, to this article. Specialthanks to Karl Heuer, and particularly to Mark Brader, who (to borrow a

  • 8/8/2019 C FAQ - Full

    37/38

    line from Steve Johnson) have goaded me beyond my inclination, andfrequently beyond my endurance, in relentless pursuit of a better FAQlist.

    Steve [email protected]%[email protected]

    mit-eddie!adam!scs

    This article is Copyright 1988, 1990, 1991 by Steve Summit.It may be freely redistributed so long as the author's name, and thisnotice, are retained.The C code in this article (vstrcat, error, etc.) is public domain andmay be used without restriction.

    _________________________________________________________________________Get Your Private, Free E-mail from MSN Hotmail athttp://www.hotmail.com.

    .Second Edition, Prentice-Hall, 1987, ISBN 0-13-109802-0. (Athird edition has recently been released.)

    PCS Mark R. Horton, Portable C Software, Prentice Hall, 1990, ISBN0-13-868050-7.

    K&P Brian W. Kernighan and P.J. Plauger, The Elements of ProgrammingStyle, Second Edition, McGraw-Hill, 1978, ISBN 0-07-034207-5.

    K&R I Brian W. Kernighan and Dennis M. Ritchie, The C ProgrammingLanguage, Prentice Hall, 1978, ISBN 0-13-110163-3.

    K&R II Brian W. Kernighan and Dennis M. Ritchie, The C ProgrammingLanguage, 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, ISBN0-201-17928-8.

    There is a more extensive bibliography in the revised Indian Hill styleguide (see question 84).

    Acknowledgements

    Thanks to Jamshid Afshar, Sudheer Apte, Dan Bernstein, Joe Buehler,Raymond Chen, Christopher Calabrese, James Davies, Norm Diamond, RayDunn, Stephen M. Dunn, Bjorn Engsig, Ron Guilmette, Doug Gwyn, TonyHansen, Joe Harrington, Guy Harris, Blair Houghton, Kirk Johnson,AndrewKoenig, John Lauro, Christopher Lott, Tim McDaniel, Evan Manning, MarkMoraes, Francois Pinard, randall@virginia, Pat Rankin, Rich Salz, ChipSalzenberg, Paul Sand, Doug Schmidt, Patricia Shanahan, Peter da Silva,Joshua Simons, Henry Spencer, Erik Talvola, Clarke Thatcher, ChrisTorek, Ed Vielmetti, Larry Virden, Freek Wiedijk, and Dave Wolverton,who have contributed, directly or indirectly, to this article. Specialthanks to Karl Heuer, and particularly to Mark Brader, who (to borrow aline from Steve Johnson) have goaded me beyond my inclination, and

    frequently beyond my endurance, in relentless pursuit of a better FAQlist.

  • 8/8/2019 C FAQ - Full

    38/38

    Steve [email protected]%[email protected]!adam!scs

    This article is Copyright 1988, 1990, 1991 by Steve Summit.It may be freely redistributed so long as the author's name, and this

    notice, are retained.The C code in this article (vstrcat, error, etc.) is public domain andmay be used without restriction.