Thursday, July 1, 2010

C Structures Questions

1. 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 would be lifted in a forthcoming version of the compiler, and in fact struct assignment and passing were fully functional in
Ritchie's compilers even as K&R I was being published. Although a few early C compilers lacked struct assignment, all modern compilers support it, and it is part of the ANSI C standard, so there should be no reluctance to use it.


2. How does struct passing and returning work?

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

Structures are typically returned from functions in a location pointed to by an extra, compiler-supplied "hidden" argument to the function. Older compilers often used a special, static location for structure returns, although this made struct-valued functions nonreentrant, which ANSI C disallows.

Reference: ANSI Sec. 2.2.3 p. 13.

3. The following 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 causes the compiler to believe that main return a struct list. (The connection is hard to see because of the intervening comment.) Since struct-valued functions are usually implemented by adding a hidden return pointer, the generated code for main() actually expects three arguments, although only two were passed (in this case, by the C start-up code). See also question 101.


4. Why can't you compare structs?

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

If you want to compare two structures, you must write your own function to do so. C++ would let you arrange for the == operator to map to your function.

5. 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 like it 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 known implementations. (Compilers which check array bounds carefully might issue warnings.)

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

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

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

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

See the next question for a usage hint.


7. 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 is an int field with offset as computed above, b's value can be set indirectly with

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

No comments:

Post a Comment