Wednesday, 5 December 2012

Null Pointer


-------------------------------------------------------------------------------------------------------------

 

                                               5. Null Pointers

 ----------------------------------------------------------------------------------

 Question 5.1

Q: What is this infamous null pointer, anyway?
A: The language definition states that for each pointer type, there is a special value--the ``null pointer''--which is distinguishable from all other pointer values and which is ``guaranteed to compare unequal to a pointer to any object or function.'' That is, a null pointer points definitively nowhere; it is not the address of any object or function. The address-of operator & will never yield a null pointer, nor will a successful call to malloc.[(malloc does return a null pointer when it fails, and this is a typical use of null pointers: as a ``special'' pointer value with some other meaning, usually ``not allocated'' or ``not pointing anywhere yet.'')
A null pointer is conceptually different from an uninitialized pointer. A null pointer is known not to point to any object or function; an uninitialized pointer might point anywhere. See also questions 1.30, 7.1, and 7.31.
As mentioned above, there is a null pointer for each pointer type, and the internal values of null pointers for different types may be different. Although programmers need not know the internal values, the compiler must always be informed which type of null pointer is required, so that it can make the distinction if necessary (see questions 5.2, 5.5, and 5.6).

----------------------------------------------------------------------------------

Question 5.2

Q: How do I get a null pointer in my programs?
A: With a null pointer constant.
According to the language definition, an ``integral constant expression with the value 0'' in a pointer context is converted into a null pointer at compile time. That is, in an initialization, assignment, or comparison when one side is a variable or expression of pointer type, the compiler can tell that a constant 0 on the other side requests a null pointer, and generate the correctly-typed null pointer value. Therefore, the following fragments are perfectly legal:
    char *p = 0;
    if(p != 0)
(See also question 5.3.)
However, an argument being passed to a function is not necessarily recognizable as a pointer context, and the compiler may not be able to tell that an unadorned 0 ``means'' a null pointer. To generate a null pointer in a function call context, an explicit cast may be required, to force the 0 to be recognized as a pointer. For example, the Unix system call execl takes a variable-length, null-pointer-terminated list of character pointer arguments, and is correctly called like this:
    execl("/bin/sh", "sh", "-c", "date", (char *)0);
If the (char *) cast on the last argument were omitted, the compiler would not know to pass a null pointer, and would pass an integer 0 instead. (Note that many Unix manuals get this example wrong; see also question 5.11.)
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 of which type, enabling it to correctly convert an unadorned 0. Function prototypes cannot provide the types for variable arguments in variable-length argument lists however, so explicit casts are still required for those arguments. (See also question 15.3.) It is probably safest to properly cast all null pointer constants in function calls, to guard against varargs functions or those without prototypes.
Here is a summary of the rules for when null pointer constants may be used by themselves, and when they require explicit casts:

----------------------------------------------------------------------------------

  Question 5.3

Q: Is the abbreviated pointer comparison ``if(p)'' to test for non-null pointers valid? What if the internal representation for null pointers is nonzero?
A: It is always valid.
When C requires the Boolean value of an expression, a false value is inferred when the expression compares equal to zero, and a true value otherwise. That is, whenever one writes
    if(expr)
where ``expr'' is any expression at all, the compiler essentially acts 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 actually a null pointer constant, and use the correct null pointer value. There is no trickery involved here; compilers do work this way, and generate identical code for both constructs. The internal representation of a null pointer does not matter.
The boolean negation operator, !, can be described as follows:
    !expr    is essentially equivalent to    (expr)?0:1
        or to    ((expr) == 0)
which leads to the conclusion that
    if(!p)    is equivalent to    if(p == 0)
``Abbreviations'' such as if(p), though perfectly legal[, are considered by some to be bad style (and by others to be good style; see question 17.10).
See also question 9.2.
References: K&R2 Sec. A7.4.7 p. 204
ISO Sec. 6.3.3.3, Sec. 6.3.9, Sec. 6.3.13, Sec. 6.3.14, Sec. 6.3.15, Sec. 6.6.4.1, Sec. 6.6.5
H&S Sec. 5.3.2 p. 122

----------------------------------------------------------------------------------

  Question 5.4

Q: What is NULL and how is it defined?
A: As a matter of style, many programmers prefer not to have unadorned 0's scattered through their programs, some representing numbers and some representing pointers. Therefore, the preprocessor macro NULL is defined (by several headers, including <stdio.h> and <stddef.h>) as a null pointer constant, typically 0 or ((void *)0) (see also question 5.6). A programmer who wishes to make explicit the distinction between 0 the integer and 0 the null pointer constant can then use NULL whenever a null pointer is required.
Using NULL is a stylistic convention only; the preprocessor turns NULL back into 0 which is then recognized by the compiler, in pointer 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 5.2 above applies for NULL as well as 0 (an unadorned NULL is equivalent to an unadorned 0).
NULL should be used only as a pointer constant; see question 5.9.

----------------------------------------------------------------------------------

Question 5.5

Q: How should NULL be defined on a machine which uses a nonzero bit pattern as the internal representation of a null pointer?
A: The same as on any other machine: as 0 (or some version of 0; see question 5.4).
Whenever a programmer requests a null pointer, either by writing ``0'' or ``NULL'', it is the compiler's responsibility to generate whatever bit pattern the machine uses for that null pointer. (Again, the compiler can tell that an unadorned 0 requests a null pointer when the 0 is in a pointer context; see question 5.2.) Therefore, #defining NULL as 0 on a machine for which internal null pointers are nonzero is as valid as on any other: the compiler must always be able to generate the machine's correct null pointers in response to unadorned 0's seen in pointer contexts. A constant 0 is a null pointer constant; NULL is just a convenient name for it (see also question 5.13).
(Section 4.1.5 of the C Standard states that NULL ``expands to an implementation-defined null pointer constant,'' which means that the implementation gets to choose which form of 0 to use and whether to use a void * cast; see questions 5.6 and 5.7. ``Implementation-defined'' here does not mean that NULL might be #defined to match some implementation-specific nonzero internal null pointer value.)

----------------------------------------------------------------------------------

Question 5.6

Q: 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 the most general case. The complication is that there are machines which use different internal representations for pointers to different types of data. The suggested definition would make uncast NULL arguments to functions expecting pointers to characters work correctly, but pointer arguments of other types could still (in the absence of prototypes) require explicit casts. Furthermore, legal constructions such as
    FILE *fp = NULL;
could fail.
Nevertheless, ANSI C allows the alternate definition
    #define NULL ((void *)0)
for NULL. [Besides potentially helping incorrect programs to work (but only on machines with homogeneous pointers, thus questionably valid assistance), this definition may catch programs which use NULL incorrectly (e.g. when the ASCII NUL character was really intended; see question 5.9). See also question 5.7.
At any rate, ANSI function prototypes ensure that most (though not quite all; see question 5.2) pointer arguments are converted correctly when passed as function arguments, so the question is largely moot.
Programmers who are accustomed to modern, ``flat'' memory architectures may find the idea of ``different kinds of pointers'' very difficult to accept. See question 5.17 for some examples.
References: Rationale Sec. 4.1.5

----------------------------------------------------------------------------------

  Question 5.7

Q: My vendor provides header files that #define NULL as 0L. Why?

A: Some programs carelessly attempt to generate null pointers by using the NULL macro, without casts, in non-pointer contexts. (Doing so is not guaranteed to work; see questions 5.2 and 5.11.) On machines which have pointers larger than integers (such as PC compatibles in ``large'' model; see also question 5.17), a particular definition of NULL such as 0L can help these incorrect programs to work. (0L is a perfectly valid definition of NULL; it is an ``integral constant expression with value 0.'') Whether it is wise to coddle incorrect programs is debatable; see also question 5.6 and section 17.
References: Rationale Sec. 4.1.5
H&S Sec. 5.3.2 pp. 121-2

----------------------------------------------------------------------------------

Question 5.8

Q: Is NULL valid for pointers to functions?
A: Yes (but see question 4.13).

----------------------------------------------------------------------------------

Question 5.9

Q: If NULL and 0 are equivalent as null pointer constants, which should I use?
A: Many programmers believe that NULL should be used in all pointer contexts, as a reminder that the value is to be thought of as a pointer. Others feel that the confusion surrounding NULL and 0 is only compounded by hiding 0 behind a macro, and prefer to use unadorned 0 instead. There is no one right answer. (See also questions 9.4 and 17.10.) C programmers must understand that NULL and 0 are interchangeable in pointer contexts, and that an uncast 0 is perfectly acceptable. Any usage of NULL (as opposed to 0) should be considered a gentle reminder that a pointer is involved; programmers should not depend on it (either for their own understanding or the compiler's) for distinguishing pointer 0's from integer 0's.
It is only in pointer contexts that NULL and 0 are equivalent. NULL should not be used when another kind of 0 is required, even though it might work, because doing so sends the wrong stylistic message. (Furthermore, ANSI allows the definition of NULL to be ((void *)0), which will not work at all in non-pointer contexts.) In particular, do not use NULL when the ASCII null character (NUL) is desired. Provide your own definition
    #define NUL '\0'
if you must.

----------------------------------------------------------------------------------

 Question 5.10

Q: But wouldn't it be better to use NULL (rather than 0), in case the value of NULL changes, perhaps on a machine with nonzero internal null pointers?
A: No. (Using NULL may be preferable, but not for this reason.) Although symbolic constants are often used in place of numbers because the numbers might change, this is not the reason that NULL is used in place of 0. Once again, the language guarantees that source-code 0's (in pointer contexts) generate null pointers. NULL is used only as a stylistic convention. See questions 5.5 and 9.4.

----------------------------------------------------------------------------------

  Question 5.11

Q: I once used a compiler that wouldn't work unless NULL was used.
A: Unless the code being compiled was nonportable, that compiler was probably broken.
Perhaps the code used something like this nonportable version of an example from question 5.2:
    execl("/bin/sh", "sh", "-c", "date", NULL);    /* WRONG */
Under a compiler which defines NULL to ((void *)0) (see question 5.6), this code will happen to work. [However, if pointers and integers have different sizes or representations, the (equally incorrect) code
    execl("/bin/sh", "sh", "-c", "date", 0);    /* WRONG */
may not work.
Correct, portable code uses an explicit cast:
    execl("/bin/sh", "sh", "-c", "date", (char *)NULL);
With the cast, the code works correctly no matter what the machine's integer and pointer representations are, and no matter which form of null pointer constant the compiler has chosen as the definition of NULL. (The code fragment in question 5.2, which used 0 instead of NULL, is equally correct; see also question 5.9.) (In general, making decisions about a language based on the behavior of one particular compiler is likely to be counterproductive.)

----------------------------------------------------------------------------------

  Question 5.12

Q: 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 and superficially attractive, does not buy much. It is not needed in assignments or comparisons; see question 5.2. (It does not even save keystrokes.) See also questions 9.1 and 10.2.

----------------------------------------------------------------------------------

  Question 5.13

Q: This is strange. NULL is guaranteed to be 0, but the null pointer is not?
A: When the term ``null'' or ``NULL'' is casually used, one of several things may be meant:
The conceptual null pointer, the abstract language concept defined in question 5.1. It is implemented with...
The internal (or run-time) representation of a null pointer, which may or may not be all-bits-0 and which may be different for different pointer types. The actual values should be of concern only to compiler writers. Authors of C programs never see them, since they use...
The null pointer constant, which is a constant integer 0 [(see question 5.2). It is often hidden behind...
The NULL macro, which is #defined to be 0 (see question 5.4). Finally, as red herrings, we have...
The ASCII null character (NUL), which does have all bits zero, but has no necessary relation to the null pointer except in name; and...
The ``null string,'' which is another name for the empty string (""). Using the term ``null string'' can be confusing in C, because an empty string involves a null ('\0') character, but not a null pointer, which brings us full circle...
In other words, to paraphrase the White Knight's description of his song in Through the Looking-Glass, the name of the null pointer is ``0'', but the name of the null pointer is called ``NULL'' (and we're not sure what the null pointer is).
This document uses the phrase ``null pointer'' (in lower case) for sense 1, the token ``0'' or the phrase ``null pointer constant'' for sense 3, and the capitalized word ``NULL'' for sense 4.[

----------------------------------------------------------------------------------

Question 5.14

Q: Why is there so much confusion surrounding null pointers? Why do these questions come up so often?

A: C programmers traditionally like to know a lot (perhaps more than they need to) about the underlying machine implementation. The fact that null pointers are represented both in source code, and internally to most machines, as zero invites unwarranted assumptions. The use of a preprocessor macro (NULL) may seem to suggest that the value could change some day, or on some weird machine. The construct ``if(p == 0)'' is easily misread as calling for conversion of p to an integral type, rather than 0 to a pointer type, before the comparison. Finally, the distinction between the several uses of the term ``null'' (listed in question 5.13) is often overlooked.
One good way to wade out of the confusion is to imagine that C used a keyword (perhaps nil, like Pascal) as a null pointer constant. The compiler could either turn nil into the appropriate type of null pointer when it could unambiguously determine that type from the source code, or complain when it could not. Now in fact, in C the keyword for a null pointer constant is not nil but 0, which works almost as well, except that an uncast 0 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 constant, the resulting program may not work.
Additional links: an article by Richard Stamp with another angle on the NULL/0 distinction

----------------------------------------------------------------------------------

Question 5.15

Q: I'm confused. I just can't understand all this null pointer stuff.
A: Here are two simple rules you can follow:
When you want a null pointer constant in source code, use ``0'' or ``NULL''.
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.
The rest of the discussion has to do with other people's misunderstandings, with the internal representation of null pointers (which you shouldn't need to know), and with the complexities of function prototypes. (Taking those complexities into account, we find that rule 2 is conservative[, of course; but it doesn't hurt.) Understand questions 5.1, 5.2, and 5.4, and consider 5.3, 5.9, 5.13, and 5.14, and you'll do fine.

----------------------------------------------------------------------------------

Question 5.16

Q: Given all the confusion surrounding null pointers, wouldn't it be easier simply to require them to be represented internally by zeroes?
A: Some implementations naturally represent null pointers by special, nonzero bit patterns, particularly when it can be arranged that inadvertently using those values triggers automatic hardware traps. Requiring null pointers to be represented internally as 0, and therefore disallowing use of the special, nonzero values, would be an unfortunate step backwards, because catching errors which result in invalid accesses is a Good Thing.
Besides, what would such a requirement really accomplish? Proper understanding of null pointers does not require knowledge of the internal representation, whether zero or nonzero. Assuming that null pointers are internally zero does not make any code easier to write (except for a certain ill-advised usage of calloc; see question 7.31). Known-zero internal pointers would not reduce the need for casts in function calls, because the size of the pointer might still be different from that of an int. (If ``nil'' were used to request null pointers, as mentioned in question 5.14, the urge to assume an internal zero representation would not even arise.)

----------------------------------------------------------------------------------

pointers

----------------------------------------------------------------------------------

Pointers

----------------------------------------------------------------------------------

Question 4.1

Q: What are pointers really good for, anyway?
A: They're good for lots of things, such as:
dynamically-allocated arrays (see questions 6.14 and 6.16)
generic access to several similar variables
(simulated) by-reference function parameters (see question 4.8 and 20.1)
malloc'ed data structures of all kinds, especially trees and linked lists
walking over arrays (for example, while parsing strings)
efficient, by-reference ``copies'' of arrays and structures, especially as function parameters
(Note that this is hardly a comprehensive list!)
See also question 6.8.

----------------------------------------------------------------------------------

  Question 4.2

Q: I'm trying to declare a pointer and allocate some space for it, but it's not working. What's wrong with this code?
char *p;
*p = malloc(10);
A: The pointer you declared is p, not *p. When you're manipulating the pointer itself (for example when you're setting it to make it point somewhere), you just use the name of the pointer:
    p = malloc(10);
It's when you're manipulating the pointed-to memory that you use * as an indirection operator:
    *p = 'H';
(It's easy to make the mistake shown in the question, though, because if you had used the malloc call as an initializer in the declaration of a local variable, it would have looked like this:
    char *p = malloc(10);
When you break an initialized pointer declaration up into a declaration and a later assignment, you have to remember to remove the *.)
In summary, in an expression, p is the pointer and *p is what it points to (a char, in this example).
See also questions 1.21, 7.1, 7.3c, and 8.3.

----------------------------------------------------------------------------------

  Question 4.3

Q: Does *p++ increment p, or what it points to?
A: The postfix ++ and -- operators essentially have higher precedence than the prefix unary operators. Therefore, *p++ is equivalent to *(p++); it increments p, and returns the value which p pointed to before p was incremented. To increment the value pointed to by p, use (*p)++ (or perhaps ++*p, if the evaluation order of the side effect doesn't matter).

----------------------------------------------------------------------------------

Question 4.4

Q: I'm trying to use pointers to manipulate an array of ints. What's wrong with this code?
    int array[5], i, *ip;
    for(i = 0; i < 5; i++) array[i] = i;
    ip = array;
    printf("%d\n", *(ip + 3 * sizeof(int)));
I expected the last line to print 3, but it printed garbage.
A: You're doing a bit more work than you have to, or should. Pointer arithmetic in C is always automatically scaled by the size of the objects pointed to. What you want to say is simply
    printf("%d\n", *(ip + 3));    /* or ip[3] -- see Q 6.3 */
which will print the third element of the array. In code like this, you don't need to worry about scaling by the size of the pointed-to elements--by attempting to do so explicitly, you inadvertently tried to access a nonexistent element past the end of the array (probably array[6] or array[12], depending on sizeof(int) on your machine).

----------------------------------------------------------------------------------

  Question 4.5

Q: I have a char * pointer that happens to point to some ints, and I want to step it over them. Why doesn't
((int *)p)++;
work?
A: In C, a cast operator does not mean ``pretend these bits have a different type, and treat them accordingly''; it is a conversion operator, and by definition it yields an rvalue, which cannot be assigned to, or incremented with ++. (It is either an accident or a deliberate but nonstandard extension if a particular compiler accepts expressions such as the above.) Say what you mean: use
    p = (char *)((int *)p + 1);
or (since p is a char *) simply
    p += sizeof(int);
or (to be really explicit)
    int *ip = (int *)p;
    p = (char *)(ip + 1);
When possible, however, you should choose appropriate pointer types in the first place, rather than trying to treat one type as another.
See also question 16.7.

----------------------------------------------------------------------------------

  Question 4.6

Q: Why can't I perform arithmetic on a void * pointer?

A: See question 11.24.

----------------------------------------------------------------------------------


Question 4.7

Q: I've got some code that's trying to unpack external structures, but it's crashing with a message about an ``unaligned access.'' What does this mean?
A: See question 16.7.

----------------------------------------------------------------------------------

  Question 4.8

Q: I have a function which accepts, and is supposed to initialize, a pointer:
    void f(int *ip)
    {
        static int dummy = 5;
        ip = &dummy;
    }
But when I call it like this:
    int *ip;
    f(ip);
the pointer in the caller remains unchanged.
A: Are you sure the function initialized what you thought it did? Remember that arguments in C are passed by value. In the code above, the called function alters only the passed copy of the pointer. To make it work as you expect, one fix is to pass the address of the pointer (the function ends up accepting a pointer-to-a-pointer; in this case, we're essentially simulating pass by reference):
    void f(ipp)
    int **ipp;
    {
        static int dummy = 5;
        *ipp = &dummy;
    }

    ...

    int *ip;
    f(&ip);
Another solution is to have the function return the pointer:
    int *f()
    {
        static int dummy = 5;
        return &dummy;
    }

    ...

    int *ip = f();

----------------------------------------------------------------------------------


  Question 4.9

Q: Suppose I want to write a function that takes a generic pointer as an argument and I want to simulate passing it by reference. Can I give the formal parameter type void **, and do something like this?
    void f(void **);
    double *dp;
    f((void **)&dp);

A: Not portably. Code like this may work and is sometimes recommended, but it relies on all pointer types having the same internal representation (which is common, but not universal; see question 5.17).
There is no generic pointer-to-pointer type in C. void * acts as a generic pointer only because conversions (if necessary) are applied automatically when other pointer types are assigned to and from void *'s; these conversions cannot be performed if an attempt is made to indirect upon a void ** value which points at a pointer type other than void *. When you make use of a void ** pointer value (for instance, when you use the * operator to access the void * value to which the void ** points), the compiler has no way of knowing whether that void * value was once converted from some other pointer type. It must assume that it is nothing more than a void *; it cannot perform any implicit conversions.
In other words, any void ** value you play with must be the address of an actual void * value somewhere; casts like (void **)&dp, though they may shut the compiler up, are nonportable (and may not even do what you want; see also question 13.9). If the pointer that the void ** points to is not a void *, and if it has a different size or representation than a void *, then the compiler isn't going to be able to access it correctly.
To make the code fragment above work, you'd have to use an intermediate void * variable:
    double *dp;
    void *vp = dp;
    f(&vp);
    dp = vp;
The assignments to and from vp give the compiler the opportunity to perform any conversions, if necessary.
Again, the discussion so far assumes that different pointer types might have different sizes or representations, which is rare today, but not unheard of. To appreciate the problem with void ** more clearly, compare the situation to an analogous one involving, say, types int and double, which probably have different sizes and certainly have different representations. If we have a function
    void incme(double *p)
    {
        *p += 1;
    }
then we can do something like
    int i = 1;
    double d = i;
    incme(&d);
    i = d;
and i will be incremented by 1. (This is analogous to the correct void ** code involving the auxiliary vp.) If, on the other hand, we were to attempt something like
    int i = 1;
    incme((double *)&i);    /* WRONG */
(this code is analogous to the fragment in the question), it would be highly unlikely to work.

----------------------------------------------------------------------------------

Question 4.10

Q: I have a function
    extern int f(int *);
which accepts a pointer to an int. How can I pass a constant by reference? A call like
    f(&5);
doesn't seem to work.

A: In C99, you can use a ``compound literal'':
    f((int[]){5});
Prior to C99, you couldn't do this directly; you had to declare a temporary variable, and then pass its address to the function:
    int five = 5;
    f(&five);
In C, a function that accepts a pointer to a value (rather than simply accepting the value itself) probably intends to modify the pointed-to value, so it may be a bad idea to pass pointers to constants. [footnote] Indeed, if f is in fact declared as accepting an int *, a diagnostic is required if you attempt to pass it a pointer to a const int. (f could be declared as accepting a const int * if it promises not to modify the pointed-to value.)

----------------------------------------------------------------------------------


Question 4.11

Q: Does C even have ``pass by reference''?

A: Not really.
Strictly speaking, C always uses pass by value. You can simulate pass by reference yourself, by defining functions which accept pointers and then using the & operator when calling, and the compiler will essentially simulate it for you when you pass an array to a function (by passing a pointer instead, see question 6.4 et al.).
Another way of looking at it is that if an parameter has type, say, int * then an integer is being passed by reference and a pointer to an integer is being passed by value.
Fundamentally, C has nothing truly equivalent to formal pass by reference or C++ reference parameters. (On the other hand, function-like preprocessor macros can provide a form of ``pass by name''.)
See also questions 4.8, 7.9, 12.27, and 20.1.
Additional links: A message of mine further explaining how a function can modify a caller's passed array.

----------------------------------------------------------------------------------

Question 4.12

Q: I've seen different syntax used for calling functions via pointers. What's the story?

A: Originally, a pointer to a function had to be ``turned into'' a ``real'' function, with the * operator, before calling:
    int r, (*fp)(), func();
    fp = func;
    r = (*fp)();
The interpretation of the last line is clear: fp is a pointer to function, so *fp is the function; append an argument list in parentheses (and extra parentheses around *fp to get the precedence right), and you've got a function call.
It can also be argued that functions are always called via pointers, and that ``real'' function names always decay implicitly into pointers (in expressions, as they do in initializations; see question 1.34). This reasoning means that
    r = fp();
is legal and works correctly, whether fp is the name of a function or a pointer to one. (The usage has always been unambiguous; there is nothing you ever could have done with a function pointer followed by an argument list except call the function pointed to.)
The ANSI C Standard essentially adopts the latter interpretation, meaning that the explicit * is not required, though it is still allowed.
See also question 1.34.

----------------------------------------------------------------------------------

Question 4.13

Q: What's the total generic pointer type? My compiler complained when I tried to stuff function pointers into a void *.

A: There is no ``total generic pointer type.''
void *'s are only guaranteed to hold object (i.e. data) pointers; it is not portable to convert a function pointer to type void *. (On some machines, function addresses can be very large, bigger than any data pointers.)
It is guaranteed, however, that all function pointers can be interconverted, as long as they are converted back to an appropriate type before calling. Therefore, you can pick any function type (usually int (*)() or void (*)(), that is, pointer to function of unspecified arguments returning int or void) as a generic function pointer. When you need a place to hold object and function pointers interchangeably, the portable solution is to use a union of a void * and a generic function pointer (of whichever type you choose).
See also questions 1.22 and 5.8.

----------------------------------------------------------------------------------

Question 4.14

Q: How are integers converted to and from pointers? Can I temporarily stuff an integer into a pointer, or vice versa?
A: Once upon a time, it was guaranteed that a pointer could be converted to an integer (though one never knew whether an int or a long might be required), and that an integer could be converted to a pointer, and that a pointer remained unchanged when converted to a (large enough) integer and back again, and that the conversions (and any mapping) were intended to be ``unsurprising to those who know the addressing structure of the machine.'' In other words, there is some precedent and support for integer/pointer conversions, but they have always been machine dependent, and hence nonportable. Explicit casts have always been required (though early compilers rarely complained if you left them out).
The ANSI/ISO C Standard, in order to ensure that C is widely implementable, has weakened those earlier guarantees. Pointer-to-integer and integer-to-pointer conversions are implementation-defined (see question 11.33), and there is no longer any guarantee that pointers can be converted to integers and back, without change.
Forcing pointers into integers, or integers into pointers, has never been good practice. When you need a generic slot that can hold either kind of data, a union is a much better idea.
See also questions 4.15, 5.18, and 19.25.

----------------------------------------------------------------------------------


  Question 4.15

Q: How do I convert an int to a char *? I tried a cast, but it's not working.
A: It depends on what you're trying to do. If you tried a cast but it's not working, you're probably trying to convert an integer to a string, in which case see question 13.1. If you're trying to convert an integer to a character, see question 8.6. If you're trying to set a pointer to point to a particular memory address, see question 19.25.

----------------------------------------------------------------------------------

  Question 4.16

Q: What's wrong with this declaration?
char* p1, p2;
I get errors when I try to use p2.
A: See question 1.5.

----------------------------------------------------------------------------------

  Question 4.17

Q: What are ``near'' and ``far'' pointers?

A: See question 19.40d.

----------------------------------------------------------------------------------

Expressions

                                                     

---------------------------------------------------------------------------------- 

                   3. Expressions

----------------------------------------------------------------------------------



 Question 3.1

Q: Why doesn't this code:
a[i] = i++;
work?
A: The subexpression i++ causes a side effect--it modifies i's value--which leads to undefined behavior since i is also referenced elsewhere in the same expression. There is no way of knowing whether the reference will happen before or after the side effect--in fact, neither obvious interpretation might hold; see question 3.9. (Note that although the language in K&R suggests that the behavior of this expression is unspecified, the C Standard makes the stronger statement that it is undefined--see question 11.33.)

----------------------------------------------------------------------------------


  Question 3.2

Q: Under my compiler, the code
int i = 7;
printf("%d\n", i++ * i++);
prints 49. Regardless of the order of evaluation, shouldn't it print 56?
A: It's true that the postincrement and postdecrement operators ++ and -- perform their operations after yielding the former value. What's often misunderstood are the implications and precise definition of the word ``after.'' It is not guaranteed that an increment or decrement is performed immediately after giving up the previous value and before any other part of the expression is evaluated. It is merely guaranteed that the update will be performed sometime before the expression is considered ``finished'' (before the next ``sequence point,'' in ANSI C's terminology; see question 3.8). In the example, the compiler chose to multiply the previous value by itself and to perform both increments later.
The behavior of code which contains multiple, ambiguous side effects has always been undefined. (Loosely speaking, by ``multiple, ambiguous side effects'' we mean any combination of increment, decrement, and assignment operators (++, --, =, +=, -=, etc.) in a single expression which causes the same object either to be modified twice or modified and then inspected. This is a rough definition; see question 3.8 for a precise one, question 3.11 for a simpler one, and question 11.33 for the meaning of ``undefined.'') Don't even try to find out how your compiler implements such things, let alone write code which depends on them (contrary to the ill-advised exercises in many C textbooks); as Kernighan and Ritchie wisely point out, ``if you don't know how they are done on various machines, that innocence may help to protect you.''

----------------------------------------------------------------------------------

  Question 3.3

Q: I've experimented with the code
int i = 3;
i = i++;
on several compilers. Some gave i the value 3, and some gave 4. Which compiler is correct?
A: There is no correct answer; the expression is undefined. See questions 3.1, 3.8, 3.9, and 11.33. (Also, note that neither i++ nor ++i is the same as i+1. If you want to increment i, use i=i+1, i+=1, i++, or ++i, not some combination. See also question 3.12b.)

Question 3.3b

Q: Here's a slick expression:
a ^= b ^= a ^= b
It swaps a and b without using a temporary.
A: Not portably, it doesn't. It attempts to modify the variable a twice between sequence points, so its behavior is undefined.
For example, it has been reported that when given the code
    int a = 123, b = 7654;
    a ^= b ^= a ^= b;
the SCO Optimizing C compiler (icc) sets b to 123 and a to 0.
See also questions 3.1, 3.8, 10.3, and 20.15c.

----------------------------------------------------------------------------------

Question 3.4

Q: Can I use explicit parentheses to force the order of evaluation I want, and control these side effects? Even if I don't, doesn't precedence dictate it?
A: Not in general.
Operator precedence and explicit parentheses impose only a partial ordering on the evaluation of an expression. In the expression
    f() + g() * h()
although we know that the multiplication will happen before the addition, there is no telling which of the three functions will be called first. In other words, precedence only partially specifies order of evaluation, where ``partially'' emphatically does not cover evaluation of operands.
Parentheses tell the compiler which operands go with which operators; they do not force the compiler to evaluate everything within the parentheses first. Adding explicit parentheses to the above expression to make it
    f() + (g() * h())
would make no difference in the order of the function calls. Similarly, adding explicit parentheses to the expression from question 3.2 to make it
    (i++) * (i++)        /* WRONG */
accomplishes nothing (since ++ already has higher precedence than *); the expression remains undefined with or without them.
When you need to ensure the order of subexpression evaluation, you may need to use explicit temporary variables and separate statements.

----------------------------------------------------------------------------------

  Question 3.5

Q: But what about the && and || operators?
I see code like ``while((c = getchar()) != EOF && c != '\n')'' ...
A: There is a special ``short-circuiting'' exception for these operators: the right-hand side is not evaluated if the left-hand side determines the outcome (i.e. is true for || or false for &&). Therefore, left-to-right evaluation is guaranteed, as it also is for the comma operator (but see question 3.7). Furthermore, all of these operators (along with ?:) introduce an extra internal sequence point (see question 3.8).

----------------------------------------------------------------------------------

  Question 3.6

Q: Is it safe to assume that the right-hand side of the && and || operators won't be evaluated if the left-hand side determines the outcome?
A: Yes. Idioms like
    if(d != 0 && n / d > 0)
        { /* average is greater than 0 */ }
and
    if(p == NULL || *p == '\0')
        { /* no string */ }
are quite common in C, and depend on this so-called short-circuiting behavior. In the first example, in the absence of short-circuiting behavior, the right-hand side would divide by 0--and perhaps crash--if d were equal to 0. In the second example, the right-hand side would attempt to reference nonexistent memory--and perhaps crash--if p were a null pointer.

----------------------------------------------------------------------------------

  Question 3.7

Q: Why did
printf("%d %d", f1(), f2());
call f2 first? I thought the comma operator guaranteed left-to-right evaluation.
A: The comma operator does guarantee left-to-right evaluation, but the commas separating the arguments in a function call are not comma operators.  The order of evaluation of the arguments to a function call is unspecified. (See question 11.33.)

----------------------------------------------------------------------------------

  Question 3.8

Q: How can I understand complex expressions like the ones in this section, and avoid writing undefined ones? What's a ``sequence point''?
A: A sequence point is a point in time at which the dust has settled and all side effects which have been seen so far are guaranteed to be complete. The sequence points listed in the C standard are:
at the end of the evaluation of a full expression (a full expression is an expression statement, or any other expression which is not a subexpression within any larger expression);
at the ||, &&, ?:, and comma operators; and
at a function call (after the evaluation of all the arguments, and just before the actual call).
The Standard states that
Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.
These two rather opaque sentences say several things. First, they talk about operations bounded by the ``previous and next sequence points''; such operations usually correspond to full expressions. (In an expression statement, the ``next sequence point'' is usually at the terminating semicolon, and the ``previous sequence point'' is at the end of the previous statement. An expression may also contain intermediate sequence points, as listed above.)
The first sentence rules out both the examples
    i++ * i++
and
    i = i++
from questions 3.2 and 3.3--in both cases, i has its value modified twice within the expression, i.e. between sequence points. (If we were to write a similar expression which did have an internal sequence point, such as
    i++ && i++
it would be well-defined, if questionably useful.)
The second sentence can be quite difficult to understand. It turns out that it disallows code like
    a[i] = i++
from question 3.1. (Actually, the other expressions we've been discussing are in violation of the second sentence, as well.) To see why, let's first look more carefully at what the Standard is trying to allow and disallow.
Clearly, expressions like
    a = b
and
    c = d + e
which read some values and use them to write others, are well-defined and legal. Clearly,  expressions like
    i = i++
which modify the same value twice are abominations which needn't be allowed (or in any case, needn't be well-defined, i.e. we don't have to figure out a way to say what they do, and compilers don't have to support them). Expressions like these are disallowed by the first sentence.
It's also clear  that we'd like to disallow expressions like
    a[i] = i++
which modify i and use it along the way, but not disallow expressions like
    i = i + 1
which use and modify i but only modify it later when it's reasonably easy to ensure that the final store of the final value (into i, in this case) doesn't interfere with the earlier accesses.
And that's what the second sentence says: if an object is written to within a full expression, any and all accesses to it within the same expression must be directly involved in the computation of the value to be written. This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification. For example, the old standby i = i + 1 is allowed, because the access of i is used to determine i's final value. The example
    a[i] = i++
is disallowed because one of the accesses of i (the one in a[i]) has nothing to do with the value which ends up being stored in i (which happens over in i++), and so there's no good way to define--either for our understanding or the compiler's--whether the access should take place before or after the incremented value is stored. Since there's no good way to define it, the Standard declares that it is undefined, and that portable programs simply must not use such constructs.
See also questions 3.9 and 3.11.
References: ISO Sec. 5.1.2.3, Sec. 6.3, Sec. 6.6, Annex C
Rationale Sec. 2.1.2.3
H&S Sec. 7.12.1 pp. 228-9

· Question 3.9
Q: So if I write
a[i] = i++;
and I don't care which cell of a[] gets written to, the code is fine, and i gets incremented by one, right?
A: Not necessarily! For one thing, if you don't care which cell of a[] gets written to, why write code which seems to write to a[] at all? More significantly, once an expression or program becomes undefined, all aspects of it become undefined. When an undefined expression has (apparently) two plausible interpretations, do not mislead yourself by imagining that the compiler will choose one or the other. The Standard does not require that a compiler make an obvious choice, and some compilers don't. In this case, not only do we not know whether a[i] or a[i+1] is written to, it is possible that a completely unrelated cell of the array (or any random part of memory) is written to, and it is also not possible to predict what final value i will receive. See questions 3.2, 3.3, 11.33, and 11.35.

----------------------------------------------------------------------------------

Question 3.10a

Q: People keep saying that the behavior of i = i++ is undefined, but I just tried it on an ANSI-conforming compiler, and got the results I expected.
A: See question 11.35.

----------------------------------------------------------------------------------

  Question 3.10b

Q: People told me that if I evaluated an undefined expression, or accessed an uninitialized variable, I'd get a random, garbage value. But I tried it, and got zero. What's up with that?
A: It's hard to answer this question, because it's hard to see what the citation of the ``unexpected'' value of 0 is supposed to prove. C does guarantee that certain values will be initialized to 0 (see question 1.30), but for the rest (and certainly for the results of those undefined expressions), it is true that you might get garbage. The fact that you happened to get 0 one time does not mean you were wrong to have expected garbage, nor does it mean that you can depend on this happening next time (much less that you should write code which depends on it!).
Most memory blocks newly delivered by the operating system, and most as-yet-untouched stack frames, do tend to be zeroed, so the first time you access them, they may happen to contain 0, but after a program has run for a while, these regularities rapidly disappear. (And programs which unwittingly depend on a circumstantial initial value of an uninitialized variable can be very difficult to debug, because the ``expected'' values may coincidentally arise in all the small, easy test cases, while the unexpeccted values and the attendant crashes happen only in the larger, longer-running, much-harder-to-trace-through invocations.)

----------------------------------------------------------------------------------

  Question 3.11

Q: How can I avoid these undefined evaluation order difficulties if I don't feel like learning the complicated rules?
A: The easiest answer is that if you steer clear of expressions which don't have reasonably obvious interpretations, for the most part you'll steer clear of the undefined ones, too. (Of course, ``reasonably obvious'' means different things to different people. This answer works as long as you agree that a[i] = i++ and i = i++ are not ``reasonably obvious.'')
To be a bit more precise, here are some simpler rules which, though slightly more conservative than the ones in the Standard, will help to make sure that your code is ``reasonably obvious'' and equally understandable to both the compiler and your fellow programmers:
Make sure that each expression modifies at most one object. By ``object'' we mean either a simple variable, or a cell of an array, or the location pointed to by a pointer (e.g. *p). A ``modification'' is either simple assignment with the = operator, or a compound assignment with an operator like +=, -=, or *=, or an increment or decrement with ++ or -- (in either pre or post forms).
If an object (as defined above) appears more than once in an expression, and is the object modified in the expression, make sure that all appearances of the object which fetch its value participate in the computation of the new value which is stored. This rule allows the expression
    i = i + 1
because although the object i appears twice and is modified, the appearance (on the right-hand side) which fetches i's old value is used to compute i's new value.
If you want to break rule 1, make sure that the several objects being modified are distinctly different, and try to limit yourself to two or at most three modifications, and of a style matching those of the following examples. (Also, make sure that you continue to follow rule 2 for each object modified.) The expression
    c = *p++
is allowed under this rule, because the two objects modified (c and p) are distinct. The expression
    *p++ = c
is also allowed, because p and *p (i.e. p itself and what it points to) are both modified but are almost certainly distinct. Similarly, both
    c = a[i++]
and
    a[i++] = c
are allowed, because c, i, and a[i] are presumably all distinct. Finally, expressions like
    *p++ = *q++
and
    a[i++] = b[j++]
in which three things are modified (p, q, and *p in the first expression, and i, j, and a[i] in the second), are allowed if all three objects are distinct, i.e. only if two different pointers p and q or two different array indices i and j are used.
You may also break rule 1 or 2 as long as you interpose a defined sequence point operator between the two modifications, or between the modification and the access. The expression
    (c = getchar()) != EOF && c != '\n'
(commonly seen in a while loop while reading a line) is legal because the second access of the variable c occurs after the sequence point implied by &&. (Without the sequence point, the expression would be illegal because the access of c while comparing it to '\n' on the right does not ``determine the value to be stored'' on the left.)

----------------------------------------------------------------------------------

  Question 3.12a

Q: What's the difference between ++i and i++?
A: If your C book doesn't explain, get a better one. Briefly: ++i adds one to the stored value of i and ``returns'' the new, incremented value to the surrounding expression; i++ adds one to i but returns the prior, unincremented value.

----------------------------------------------------------------------------------

Question 3.12b

Q: If I'm not using the value of the expression, should I use ++i or i++ to increment a variable?
A: Since the two forms differ only in the value yielded, they are entirely equivalent when only their side effect is needed. (However, the prefix form is preferred in C++.) Some people will tell you that in the old days one form was preferred over the other because it utilized a PDP-11 autoincrement addressing mode, but those people are confused. An autoincrement addressing mode can only help if a pointer variable is being incremented and indirected upon, as in
    register char c, *cp;
    c = *cp++;
See also question 3.3.
References: K&R1 Sec. 2.8 p. 43
K&R2 Sec. 2.8 p. 47
ISO Sec. 6.3.2.4, Sec. 6.3.3.1
H&S Sec. 7.4.4 pp. 192-3, Sec. 7.5.8 pp. 199-200

----------------------------------------------------------------------------------

  Question 3.13

Q: I need to check whether one number lies between two others. Why doesn't
if(a < b < c)
work?
A: The relational operators, such as <, are all binary; they compare two operands and return a true or false (1 or 0) result. Therefore, the expression a < b < c compares a to b, and then checks whether the resulting 1 or 0 is less than c. (To see it more clearly, imagine that it had been written as (a < b) < c, because that's how the compiler interprets it.) To check whether one number lies between two others, use code like this:
    if(a < b && b < c)

----------------------------------------------------------------------------------


  Question 3.14

Q: Why doesn't the code
int a = 1000, b = 1000;
long int c = a * b;
work?
A: Under C's integral promotion rules, the multiplication is carried out using int arithmetic, and the result may overflow or be truncated before being promoted and assigned to the long int left-hand side. Use an explicit cast on at least one of the operands to force long arithmetic:
    long int c = (long int)a * b;
or perhaps
    long int c = (long int)a * (long int)b;
(both forms are equivalent).
Notice that the expression (long int)(a * b) would not have the desired effect. An explicit cast of this form (i.e. applied to the result of the multiplication) is equivalent to the implicit conversion which would occur anyway when the value is assigned to the long int left-hand side, and like the implicit conversion, it happens too late, after the damage has been done.
See also question 3.15.

----------------------------------------------------------------------------------

  Question 3.14b

Q: How can I ensure that integer arithmetic doesn't overflow?
A: See question 20.6b.

----------------------------------------------------------------------------------


  Question 3.15

Q: Why does the code
double degC, degF;
degC = 5 / 9 * (degF - 32);
keep giving me 0?
A: If both operands of a binary operator are integers, C performs an integer operation, regardless of the type of the rest of the expression. In this case, the integer operation is truncating division, yielding 5 / 9 = 0. (Note, though, that the problem of having subexpressions evaluated in an unexpected type is not restricted to division, nor for that matter to type int.) If you cast one of the operands to float or double, or use a floating-point constant, i.e.
    degC = (double)5 / 9 * (degF - 32);
or
    degC = 5.0 / 9 * (degF - 32);
it will work as you expect. Note that the cast must be on one of the operands; casting the result (as in (double)(5 / 9) * (degF - 32)) would not help.

----------------------------------------------------------------------------------

  Question 3.16

Q: I have a complicated expression which I have to assign to one of two variables, depending on a condition. Can I use code like this?
    ((condition) ? a : b) = complicated_expression;
A: No. The ?: operator, like most operators, yields a value, and you can't assign to a value. (In other words, ?: does not yield an lvalue.) If you really want to, you can try something like
    *((condition) ? &a : &b) = complicated_expression;
although this is admittedly not as pretty.
References: ISO Sec. 6.3.15
H&S Sec. 7.1 pp. 179-180

----------------------------------------------------------------------------------

  Question 3.17

Q: I have some code containing expressions like
a ? b = c : d
and some compilers are accepting it but some are not.
A: In the original definition of the language, = was of lower precedence than ?:, so early compilers tended to trip up on an expression like the one above, attempting to parse it as if it had been written
    (a ? b) = (c : d)
Since it has no other sensible meaning, however, later compilers have allowed the expression, and interpret it as if an inner set of parentheses were implied:
    a ? (b = c) : d
Here, the left-hand operand of the = is simply b, not the invalid a ? b. In fact, the grammar specified in the ANSI/ISO C Standard effectively requires this interpretation. (The grammar in the Standard is not precedence-based, and says that any expression may appear between the ? and : symbols.)
An expression like the one in the question is perfectly acceptable to an ANSI compiler, but if you ever have to compile it under an older compiler, you can always add the explicit, inner parentheses.

----------------------------------------------------------------------------------

Question 3.18

Q: What does the warning ``semantics of `>' change in ANSI C'' mean?
A: This message represents an attempt by certain (perhaps overzealous) compilers to warn you that some code may perform differently under the ANSI C ``value preserving'' rules than under the older ``unsigned preserving'' rules.
The wording of this message is rather confusing because what has changed is not really the semantics of the > operator itself (in fact, almost any C operator can appear in the message), but rather the semantics of the implicit conversions which always occur when two dissimilar types meet across a binary operator, or when a narrow integral type must be promoted.

----------------------------------------------------------------------------------

  Question 3.19

Q: What's the difference between the ``unsigned preserving'' and ``value preserving'' rules?
A: These rules concern the behavior when an unsigned type must be promoted to a ``larger'' type. Should it be promoted to a larger signed or unsigned type? (To foreshadow the answer, it may depend on whether the larger type is truly larger.)
Under the unsigned preserving (also called ``sign preserving'') rules, the promoted type is always unsigned. This rule has the virtue of simplicity, but it can lead to surprises (see the first example below).
Under the value preserving rules, the conversion depends on the actual sizes of the original and promoted types. If the promoted type is truly larger--which means that it can represent all the values of the original, unsigned type as signed values--then the promoted type is signed. If the two types are actually the same size, then the promoted type is unsigned (as for the unsigned preserving rules).
Since the actual sizes of the types are used in making the determination, the results will vary from machine to machine. On some machines, short int is smaller than int, but on some machines, they're the same size. On some machines, int is smaller than long int, but on some machines, they're the same size.
In practice, the difference between the unsigned and value preserving rules matters most often when one operand of a binary operator is (or promotes to) int and the other one might, depending on the promotion rules, be either int or unsigned int. If one operand is unsigned int, the other will be converted to that type--almost certainly causing an undesired result if its value was negative (again, see the first example below). When the ANSI C Standard was established, the value preserving rules were chosen, to reduce the number of cases where these surprising results occur. (On the other hand, the value preserving rules also reduce the number of predictable cases, because portable programs cannot depend on a machine's type sizes and hence cannot know which way the value preserving rules will fall.)
Here is a contrived example showing the sort of surprise that can occur under the unsigned preserving rules:
    unsigned short us = 10;
    int i = -5;
    if(i > us)
        printf("whoops!\n");
The important issue is how the expression i > us is evaluated. Under the unsigned preserving rules (and under the value preserving rules on a machine where short integers and plain integers are the same size), us is promoted to unsigned int. The usual integral conversions say that when types unsigned int and int meet across a binary operator, both operands are converted to unsigned, so i is converted to unsigned int, as well. The old value of i, -5, is converted to some large unsigned value (65,531 on a 16-bit machine). This converted value is greater than 10, so the code prints ``whoops!''
Under the value preserving rules, on a machine where plain integers are larger than short integers, us is converted to a plain int (and retains its value, 10), and i remains a plain int. The expression is not true, and the code prints nothing. (To see why the values can be preserved only when the signed type is larger, remember that a value like 40,000 can be represented as an unsigned 16-bit integer but not as a signed one.)
Unfortunately, the value preserving rules do not prevent all surprises. The example just presented still prints ``whoops'' on a machine where short and plain integers are the same size. The value preserving rules may also inject a few surprises of their own--consider the code:
    unsigned char uc = 0x80;
    unsigned long ul = 0;
    ul |= uc << 8;
    printf("0x%lx\n", ul);
Before being left-shifted, uc is promoted. Under the unsigned preserving rules, it is promoted to an unsigned int, and the code goes on to print 0x8000, as expected. Under the value preserving rules, however, uc is promoted to a signed int (as long as int's are larger than char's, which is usually the case). The intermediate result uc << 8 goes on to meet ul, which is unsigned long. The signed, intermediate result must therefore be promoted as well, and if int is smaller than long, the intermediate result is sign-extended, becoming 0xffff8000 on a machine with 32-bit longs. On such a machine, the code prints 0xffff8000, which is probably not what was expected. (On machines where int and long are the same size, the code prints 0x8000 under either set of rules.)
To avoid surprises (under either set of rules, or due to an unexpected change of rules), it's best to avoid mixing signed and unsigned types in the same expression, although as the second example shows, this rule is not always sufficient. You can always use explicit casts to indicate, unambiguously, exactly where and how you want conversions performed; see questions 12.42 and 16.7 for examples. (Some compilers attempt to warn you when they detect ambiguous cases or expressions which would have behaved differently under the unsigned preserving rules, although sometimes these warnings fire too often; see also question 3.18.)


----------------------------------------------------------------------------------

Structures, Unions, and Enumerations

----------------------------------------------------------------------------------

  Structures, Unions, and Enumerations

----------------------------------------------------------------------------------


 Question 2.1

Q: What's the difference between these two declarations?
    struct x1 { ... };
    typedef struct { ... } x2;

A: The first form declares a structure tag; the second declares a typedef. The main difference is that the second declaration is of a slightly more abstract type--its users don't necessarily know that it is a structure, and the keyword struct is not used when declaring instances of it:
    x2 b;
Structures declared with tags, on the other hand, must be defined with the
    struct x1 a;
form. [footnote]
(It's also possible to play it both ways:
    typedef struct x3 { ... } x3;
It's legal, if potentially obscure, to use the same name for both the tag and the typedef, since they live in separate namespaces. See question 1.29.)

----------------------------------------------------------------------------------

  Question 2.2

Q: Why doesn't
struct x { ... };
x thestruct;
work?
A: C is not C++. Typedef names are not automatically generated for structure tags. Either declare structure instances using the struct keyword:
    struct x thestruct;
or declare a typedef when you declare a structure:
    typedef struct { ... } x;

    x thestruct;
See also questions 1.14 and 2.1.

----------------------------------------------------------------------------------

Question 2.3

Q: Can a structure contain a pointer to itself?
A: Most certainly. A problem can arise if you try to use typedefs; see questions 1.14 and 1.15.

----------------------------------------------------------------------------------


  Question 2.4

Q: How can I implement opaque (abstract) data types in C?

A: One good way is for clients to use structure pointers (perhaps additionally hidden behind typedefs) which point to structure types which are not publicly defined. In other words, a client uses structure pointers (and calls functions accepting and returning structure pointers) without knowing anything about what the fields of the structure are. (As long as the details of the structure aren't needed--e.g. as long as the -> and sizeof operators are not used--C is perfectly happy to handle pointers to structures of incomplete type.[footnote] ) Only within the source files implementing the abstract data type are complete declarations for the structures actually in scope.
See also question 11.5.

----------------------------------------------------------------------------------

Question 2.4b

Q: Is there a good way of simulating OOP-style inheritance, or other OOP features, in C?
A: It's straightforward to implement simple ``methods'' by placing function pointers in structures. You can make various clumsy, brute-force attempts at inheritance using the preprocessor or by having structures contain ``base types'' as initial subsets, but it won't be perfect. There's obviously no operator overloading, and overriding (i.e. of ``methods'' in ``derived classes'') would have to be done by hand.
Obviously, if you need ``real'' OOP, you'll want to use a language that supports it, such as C++.

----------------------------------------------------------------------------------

  Question 2.5

Q: Why does the declaration
extern int f(struct x *p);
give me an obscure warning message about ``struct x declared inside parameter list''?
A: See question 11.5.

----------------------------------------------------------------------------------

 Question 2.6

Q: I came across some code that declared a structure like this:
struct name {
    int namelen;
    char namestr[1];
};
and then did some tricky allocation to make the namestr array act like it had several elements, with the number recorded by namelen. How does this work? Is it legal or portable?
A: It's not clear if it's legal or portable, but it is rather popular. An implementation of the technique might look something like this:
#include <stdlib.h>
#include <string.h>

struct name *makename(char *newname)
{
    struct name *ret =
        malloc(sizeof(struct name)-1 + strlen(newname)+1);
                /* -1 for initial [1]; +1 for \0 */
    if(ret != NULL) {
        ret->namelen = strlen(newname);
        strcpy(ret->namestr, newname);
    }

    return ret;
}
This function allocates an instance of the name structure with the size adjusted so that the namestr field can hold the requested name (not just one character, as the structure declaration would suggest).
Despite its popularity, the technique is also somewhat notorious: Dennis Ritchie has called it ``unwarranted chumminess with the C implementation,'' and an official interpretation has deemed that it is not strictly conforming with the C Standard, although it does seem to work under all known implementations. (Compilers which check array bounds carefully might issue warnings.)
Another possibility is to declare the variable-size element very large, rather than very small. The above example could be rewritten like this:
#include <stdlib.h>
#include <string.h>

#define MAXSIZE 100

struct name {
    int namelen;
    char namestr[MAXSIZE];
};

struct name *makename(char *newname)
{
    struct name *ret =
        malloc(sizeof(struct name)-MAXSIZE+strlen(newname)+1);
                                /* +1 for \0 */
    if(ret != NULL) {
        ret->namelen = strlen(newname);
        strcpy(ret->namestr, newname);
    }

    return ret;
}
where MAXSIZE is larger than any name which will be stored. However, it looks like this technique is disallowed by a strict interpretation of the Standard as well. Furthermore, either of these ``chummy'' structures must be used with care, since the programmer knows more about their size than the compiler does.
Of course, to be truly safe, the right thing to do is use a character pointer instead of an array:
#include <stdlib.h>
#include <string.h>

struct name {
    int namelen;
    char *namep;
};

struct name *makename(char *newname)
{
    struct name *ret = malloc(sizeof(struct name));
    if(ret != NULL) {
        ret->namelen = strlen(newname);
        ret->namep = malloc(ret->namelen + 1);
        if(ret->namep == NULL) {
            free(ret);
            return NULL;
        }
        strcpy(ret->namep, newname);
    }

    return ret;
}
(Obviously, the ``convenience'' of having the length and the string stored in the same block of memory has now been lost, and freeing instances of this structure will require two calls to free; see question 7.23.)
When the data type being stored is characters, as in the above examples, it is straightforward to coalesce the two calls to malloc into one, to preserve contiguity (and therefore rescue the ability to use a single call to free):
struct name *makename(char *newname)
{
    char *buf = malloc(sizeof(struct name) +
                strlen(newname) + 1);
    struct name *ret = (struct name *)buf;
    ret->namelen = strlen(newname);
    ret->namep = buf + sizeof(struct name);
    strcpy(ret->namep, newname);

    return ret;
}
However, piggybacking a second region onto a single malloc call like this is only portable if the second region is to be treated as an array of char. For any larger type, alignment (see questions 2.12 and 16.7) becomes significant and would have to be preserved.
C99 introduces the concept of a flexible array member, which allows the size of an array to be omitted if it is the last member in a structure, thus providing a well-defined solution.
References: Rationale Sec. 3.5.4.2
C9X Sec. 6.5.2.1

----------------------------------------------------------------------------------

  Question 2.7

Q: I heard that structures could be assigned to variables and passed to and from functions, but K&R1 says not.
A: What K&R1 said (though this was quite some time ago by now) was that the restrictions on structure operations would be lifted in a forthcoming version of the compiler, and in fact the operations of assigning structures, passing structures as function arguments, and returning structures from functions were fully functional in Ritchie's compiler even as K&R1 was being published. A few ancient compilers may have lacked these operations, but all modern compilers support them, and they are part of the ANSI C standard, so there should be no reluctance to use them. [footnote]
(Note that when a structure is assigned, passed, or returned, the copying is done monolithically. This means that the copies of any pointer fields will point to the same place as the original. In other words, the data pointed to is not copied.)
See the code fragments in question 14.11 for an example of structure operations in action.

----------------------------------------------------------------------------------

Question 2.8

Q: Is there a way to compare structures automatically?
A: No. There is not a good way for a compiler to implement structure comparison (i.e. to support the == operator for structures) which is consistent with C's low-level flavor. A simple byte-by-byte comparison could founder on random bits present in unused ``holes'' in the structure (such padding is used to keep the alignment of later fields correct; see question 2.12). A field-by-field comparison might require unacceptable amounts of repetitive code for large structures. Any compiler-generated comparison could not be expected to compare pointer fields appropriately in all cases: for example, it's often appropriate to compare char * fields with strcmp rather than == (see also question 8.2).
If you need to compare two structures, you'll have to write your own function to do so, field by field.

----------------------------------------------------------------------------------

  Question 2.9

Q: How are structure passing and returning implemented?
A: When structures are passed as arguments to functions, the entire structure is typically pushed on the stack, using as many words as are required. (Programmers often choose to use pointers to structures instead, precisely to avoid this overhead.) Some compilers merely pass a pointer to the structure, though they may have to make a local copy to preserve pass-by-value semantics.
Structures are often returned from functions in a location pointed to by an extra, compiler-supplied ``hidden'' argument to the function. Some older compilers used a special, static location for structure returns, although this made structure-valued functions non-reentrant, which ANSI C disallows.

----------------------------------------------------------------------------------

  Question 2.10

Q: How can I pass constant values to functions which accept structure arguments? How can I create nameless, immediate, constant structure values?
A: Traditional C had no way of generating anonymous structure values; you had to use a temporary structure variable or a little structure-building function; see question 14.11 for an example.
C99 introduces ``compound literals'', one form of which provides for structure constants. For example, to pass a constant coordinate pair to a hypothetical plotpoint function which expects a struct point, you can call
    plotpoint((struct point){1, 2});
Combined with ``designated initializers'' (another C99 feature), it is also possible to specify member values by name:
    plotpoint((struct point){.x=1, .y=2});

----------------------------------------------------------------------------------

 Question 2.11

Q: How can I read/write structures from/to data files?
A: It is relatively straightforward to write a structure out using fwrite:
    fwrite(&somestruct, sizeof somestruct, 1, fp);
and a corresponding fread invocation can read it back in. What happens here is that fwrite receives a pointer to the structure, and writes (or fread correspondingly reads) the memory image of the structure as a stream of bytes. The sizeof operator determines how many bytes the structure occupies.
(The call to fwrite above is correct under an ANSI compiler as long as a prototype for fwrite is in scope, usually because <stdio.h> is #included.
However, data files written as memory images in this way will not be portable, particularly if they contain floating-point fields or pointers. The memory layout of structures is machine and compiler dependent. Different compilers may use different amounts of padding (see question 2.12), and the sizes and byte orders of fundamental types vary across machines. Therefore, structures written as memory images cannot necessarily be read back in by programs running on other machines (or even compiled by other compilers), and this is an important concern if the data files you're writing will ever be interchanged between machines. See also questions 2.12 and 20.5.
Also, if the structure contains any pointers (char * strings, or pointers to other data structures), only the pointer values will be written, and they are most unlikely to be valid when read back in. Finally, note that for widespread portability you must use the "b" flag when opening the files; see question 12.38.
A more portable solution, though it's a bit more work initially, is to write a pair of functions for writing and reading a structure, field-by-field, in a portable (perhaps even human-readable) way.
References: H&S Sec. 15.13 p. 381

----------------------------------------------------------------------------------

Question 2.12

Q: Why is my compiler leaving holes in structures, wasting space and preventing ``binary'' I/O to external data files? Can I turn this off, or otherwise control the alignment of structure fields?
A: Many machines access values in memory most efficiently when the values are appropriately aligned. (For example, on a byte-addressed machine, short ints of size 2 might best be placed at even addresses, and long ints of size 4 at addresses which are a multiple of 4.) Some machines cannot perform unaligned accesses at all, and require that all data be appropriately aligned.
Therefore, if you declare a structure like
    struct {
        char c;
        int i;
    };
the compiler will usually leave an unnamed, unused hole between the char and int fields, to ensure that the int field is properly aligned. (This incremental alignment of the second field based on the first relies on the fact that the structure itself is always properly aligned, with the most conservative alignment requirement. The compiler guarantees this alignment for structures it allocates, as does malloc.)
Your compiler may provide an extension to give you control over the packing of structures (i.e. whether they are padded or not), perhaps with a #pragma (see question 11.20), but there is no standard method.
If you're worried about wasted space, you can minimize the effects of padding by ordering the members of a structure based on their base types, from largest to smallest. You can sometimes get more control over size and alignment by using bit-fields, although they have their own drawbacks. (See question 2.26.)
See also questions 2.13, 16.7, and 20.5.

----------------------------------------------------------------------------------


  Question 2.13

Q: Why does sizeof report a larger size than I expect for a structure type, as if there were padding at the end?
A: Padding at the end of a structure may be necessary to preserve alignment when an array of contiguous structures is allocated. Even when the structure is not part of an array, the padding remains, so that sizeof can always return a consistent size. See also question 2.12.

----------------------------------------------------------------------------------

  Question 2.14

Q: How can I determine the byte offset of a field within a structure?
A: ANSI C defines the offsetof() macro in <stddef.h>, which lets you compute the offset of field f in struct s as offsetof(struct s, f). If for some reason you have to code this sort of thing yourself, one possibility is
    #define offsetof(type, f) ((size_t) \
        ((char *)&((type *)0)->f - (char *)(type *)0))
This implementation is not 100% portable; some compilers may legitimately refuse to accept it.
(The complexities of the definition above bear a bit of explanation. The subtraction of a carefully converted null pointer is supposed to guarantee that a simple offset is computed even if the internal representation of the null pointer is not 0. The casts to (char *) arrange that the offset so computed is a byte offset. The nonportability is in pretending, if only for the purposes of address calculation, that there is an instance of the type sitting at address 0. Note, however, that since the pretend instance is not actually referenced, an access violation is unlikely.)

----------------------------------------------------------------------------------


  Question 2.15

Q: How can I access structure fields by name at run time?
A: Keep track of the field offsets as computed using the offsetof() macro (see question 2.14). If structp is a pointer to an instance of the structure, and field f is an int having offset offsetf, f's value can be set indirectly with
    *(int *)((char *)structp + offsetf) = value;

----------------------------------------------------------------------------------

 Question 2.16

Q: Does C have an equivalent to Pascal's with statement?
A: See question 20.23.

----------------------------------------------------------------------------------

Question 2.17

Q: If an array name acts like a pointer to the base of an array, why isn't the same thing true of a structure?
A: The rule (see question 6.3) that causes array references to ``decay'' into pointers is a special case which applies only to arrays, and reflects their ``second class'' status in C. (An analogous rule applies to functions.) Structures, however, are first class objects: when you mention a structure, you get the entire structure.

----------------------------------------------------------------------------------

  Question 2.18

Q: This program works correctly, but it dumps core after it finishes. Why?
    struct list {
        char *item;
        struct list *next;
    }

    /* Here is the main program. */

    main(argc, argv)
    { ... }

A: A missing semicolon at the end of the structure declaration causes main to be declared as returning a structure. (The connection is hard to see because of the intervening comment.) Since structure-valued functions are usually implemented by adding a hidden return pointer (see question 2.9), the generated code for main() tries to accept three arguments, although only two are passed (in this case, by the C start-up code).

----------------------------------------------------------------------------------

Question 2.19

Q: What's the difference between a structure and a union, anyway?
A: A union is essentially a structure in which all of the fields overlay each other; you can only use one field at a time. (You can also cheat by writing to one field and reading from another, to inspect a type's bit patterns or interpret them differently, but that's obviously pretty machine-dependent.) The size of a union is the maximum of the sizes of its individual members, while the size of a structure is the sum of the sizes of its members. (In both cases, the size may be increased by padding; see questions 2.12 and 2.13.)
References: ISO Sec. 6.5.2.1
H&S Sec. 5.7 pp. 140-145 esp. Sec. 5.7.4

----------------------------------------------------------------------------------

  Question 2.20

Q: Can I initialize unions?
A: In the original ANSI C, an initializer was allowed only for the first-named member of a union. C99 introduces ``designated initializers'' which can be used to initialize any member.
In the absence of designated initializers, if you're desperate, you can sometimes define several variant copies of a union, with the members in different orders, so that you can declare and initialize the one having the appropriate first member. (These variants are guaranteed to be implemented compatibly, so it's okay to ``pun'' them by initializing one and then using the other.)
References: K&R2 Sec. 6.8 pp. 148-9
ISO Sec. 6.5.7
C9X Sec. 6.5.8
H&S Sec. 4.6.7 p. 100

----------------------------------------------------------------------------------

 Question 2.21

Q: Is there an automatic way to keep track of which field of a union is in use?
A: No. You can implement an explicitly ``tagged'' union yourself:
struct taggedunion {
    enum {UNKNOWN, INT, LONG, DOUBLE, POINTER} code;
    union {
        int i;
        long l;
        double d;
        void *p;
    } u;
};
You will have to make sure that the code field is always set appropriately when the union is written to; the compiler won't do any of this for you automatically. (C unions are not like Pascal variant records.)

----------------------------------------------------------------------------------


  Question 2.22

Q: What's the difference between an enumeration and a set of preprocessor #defines?
A: There is little difference. The C Standard says that enumerations have integral type and that enumeration constants are of type int, so both may be freely intermixed with other integral types, without errors. (If, on the other hand, such intermixing were disallowed without explicit casts, judicious use of enumerations could catch certain programming errors.)
Some advantages of enumerations are that the numeric values are automatically assigned, that a debugger may be able to display the symbolic values when enumeration variables are examined, and that they obey block scope. (A compiler may also generate nonfatal warnings when enumerations are indiscriminately mixed, since doing so can still be considered bad style even though it is not strictly illegal.) A disadvantage is that the programmer has little control over those nonfatal warnings; some programmers also resent not having control over the sizes of enumeration variables.

----------------------------------------------------------------------------------

  Question 2.23

Q: Are enumerations really portable?
Aren't they Pascalish?
A: Enumerations were a mildly late addition to the language (they were not in K&R1), but they are definitely part of the language now: they're in the C Standard, and all modern compilers support them. They're quite portable, although historical uncertainty about their precise definition led to their specification in the Standard being rather weak (see question 2.22).


----------------------------------------------------------------------------------

 Question 2.24

Q: Is there an easy way to print enumeration values symbolically?
A: No. You can write a little function (one per enumeration) to map an enumeration constant to a string, either by using a switch statement or by searching an array. (For debugging purposes, a good debugger should automatically print enumeration constants symbolically.)


----------------------------------------------------------------------------------

  Question 2.25

Q: I came across some structure declarations with colons and numbers next to certain fields, like this:
struct record {
    char *name;
    int refcount : 4;
    unsigned dirty : 1;
};
What gives?

A: Those are bit-fields; the number gives the exact size of the field, in bits. (See any complete book on C for the details.) Bit-fields can be used to save space in structures having several binary flags or other small fields, and they can also be used in an attempt to conform to externally-imposed storage layouts. (Their success at the latter task is mitigated by the fact that bit-fields are assigned left-to-right on some machines and right-to-left on others).
Note that the colon notation for specifying the size of a field in bits is only valid in structures (and in unions); you cannot use this mechanism to specify the size of arbitrary variables. (See questions 1.2 and 1.3.)

----------------------------------------------------------------------------------

  Question 2.26

Q: Why do people use explicit masks and bit-twiddling code so much, instead of declaring bit-fields?
A: Bit-fields are thought to be nonportable, although they are no less portable than other parts of the language. (You don't know how big they can be, but that's equally true for values of type int. You don't know by default whether they're signed, but that's equally true of type char. You don't know whether they're laid out from left to right or right to left in memory, but that's equally true of the bytes of all types, and only matters if you're trying to conform to externally-imposed storage layouts, which is always nonportable; see also questions 2.12 and 20.5.)
Bit-fields are inconvenient when you also want to be able to manipulate some collection of bits as a whole (perhaps to copy a set of flags). You can't have arrays of bit-fields; see also question 20.8. Many programmers suspect that the compiler won't generate good code for bit-fields (historically, this was sometimes true).
Straightforward code using bit-fields is certainly clearer than the equivalent explicit masking instructions; it's too bad that bit-fields can't be used more often.

----------------------------------------------------------------------------------