C99 errata, etc

A collection of errata and general problems with the C99 standard.

3.1 Does modification of a volatile value by “forces unknown” constitute access?

Similarly, does modification of a value which occurs due to a library call, or a call to an external function which may not be written in C, constitute access? If so, then these “accesses” are presumably regulated by 6.5p7 and any other relevant sections, which means that they must occur via an lvalue of appropriate type, or “as if” so.

3.1p4 What is meant by “expression accesses an object”?

Surely object access actually occurs during to evaluation of an expression. If an expression “accesses an object” does that mean any expression containing that expression (as a subexpression) also accesses the object? It is difficult to see how, without further elaboration, the answer could be “no”, yet other parts of the standard seem to rely on exactly this interpretation (see 6.5p7).

For example: does the expression ‘(float) i’ access the object referred to by ‘i’? 6.5p7 seems to require that the answer is ‘no’.

6.2.5p26 Says that “All pointers to structure types shall have the same representation and alignment requirements as each other.”

Surely what is meant is that “all pointers to the same structure type shall have the same representation and alignment requirements as each other”? (Similarly for union types).

6.3.2.3 Pointer conversion is not very well defined

It doesn’t seem like pointer conversion is defined well enough to allow that casting from a “void *” to another pointer type necessarily points at the same object – specifically it doesn’t seem to require that the resulting pointer need “compare equal” with the original pointer. This would make malloc etc. unusable (except that malloc specifically allows that its return value can be assigned to a pointer of a different type and will then still point at the allocated object); it certainly seems to preclude development of custom memory management routines.

Similarly, performing a cast from one pointer type to another (eg. a “struct A *” to a “struct B *”) does not, from 6.3.2.3 alone, guarantee that the resulting pointer will compare equal with the original pointer nor point at the same object, even if alignment requirements are met, since this is not explicitly required (but see discussion of 6.7.2.1p13 below; It seems likely that this form of conversion is intended to be supported and that the result will point to the same ‘location’, i.e. the pointers will compare equal, in the case that one of the pointed-to types is the first member of the other pointed-to type).

The best-defined of all pointer conversions seems to be that to the “char *” type. In this case the result “points to the lowest addressed byte of the object”. However it is still unclear whether the “char *” value must compare equal with the original pointer, i.e. is the “lowest addressed byte” the same as “a subobject at its beginning” as per 6.5.9p6? Note, we canĀ use p7 in general to devise a standard way to convert a pointer to an object into a pointer to a sub-object at its beginning, and back, by first casting to ‘char *’. Eg:

struct a { int a1; int a2; };
struct b { struct a b1; int b2; };

struct b bb;
struct a * aptr = &bb.b1;
char * cptr = (char *) aptr;
struct b * bptr = (struct b *) cptr;
char * cptr2 = (char *) bptr;

… we know that cptr and cptr2 must compare equal since ‘when converted back again, the result shall compare equal to the original pointer.’ We can then be almost sure that bptr and aptr point into the same object, due to ‘When a pointer to an object is converted to a pointer of character type, the result points to the lowest addressed byte of the object’; i.e. cptr must point to the lowest addressed byte of the ‘struct a’ object and cptr2 must point to the lowest addressed byte of the ‘struct b’ object, which is presumably the same byte in both cases (see 6.7.2.1p13).

The term “address” does not appear to be properly defined anywhere in the standard. The expression “points to” is only indirectly (and vaguely) defined.

6.5p6 Unintended(?) differentiation between heap-allocated objects and objects that are variables (have declared type)?

It is stated that “if a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type” of the object becomes the type that was copied from. This is different for when the object copied to does have a declared type. It is not clear what the rationale behind this difference is. It disallows, for instance, allocating space for an int via malloc(), and then copying a float’s byte representation into it, and then accessing it via a pointer-to-int, whereas the same would be allowed if the space were allocated instead by declaring a local variable.

6.5p7 Multiple problems

(For an in-depth discussion see this entry.)

This paragraph implies that access is to the “stored value” of an object, rather than the object itself; this is contradictory to 3.1p4.

The word “expression” in “lvalue expression” is redundant, as an lvalue is defined to be a kind of expression.

Questions raised by the wording of this paragraph include:

- is access intended to be limited only to lvalues? that is, is it intended that stored object values can only be accessed via lvalues, and not by any other means? (6.5p6 provides limited support for this idea; if not, then we can read the paragraph to place limitations only on the lvalues that can perform access, and to place no limitations on other expressions). If so:

- does calling a library function which modifies the memory constitute access?

- does changing of a value in a volatile object by “forces unknown” constitute access?

Perhaps we could allow that both these cases might happen “as if” by an lvalue, in the absence of anything which contradicts that, but this is something of a leap. It’s probably best to assume that 6.5p7 attempts only to limit the kinds of lvalue that can be used to perform access, and note independently that expressions that are not lvalues generally do not cause access to objects.

What is meant by “expression accesses an object”? – see also discussion of 3.1p4. If an expression accesses the objects that all its subexpressions do, this disallows most useful accesses.

6.7.2.1p13 It is not defined what “suitably converted” means.

(In “A pointer to a structure object, suitably converted, points to its initial member”).

It’s probably safe to assume that in this sentence ‘suitably converted’ means ‘when converted to a suitable type’ and that 6.7.2.1p13 is meant to restrict the result of such conversion rather than be non-normative; however, this should really be explicit, and seems to belong in 6.7.2.1 rather than here.

6.7.3p6 Unlimited license for implementation to redefine “access” to volatile objects?

The wording allows that the implementation may define what “constitutes access” to a volatile object. It is not clear what is really meant by this nor why it is necessary. Perhaps it is to allow volatiles to be accessed by means not otherwise allowed by 6.5p7; but then perhaps that is not required.

Gcc documentation states that an expression-statement “*p;” where ‘p’ is a volatile qualified pointer may or may not cause access depending on the type of ‘p’ (scalar types yes, most other types no). It seems that the standard neither requires nor prohibits access in this case. Perhaps this disambiguation is all that the clause really intends to allow.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.