variable-length struct hack

Karl Heuer karl at haddock.ima.isc.com
Thu Dec 7 11:46:35 AEST 1989


[Snarfed from comp.std.unix, thread "Query about <dirent.h>"]
In article <450 at longway.TIC.COM> Doug Gwyn <uunet!brl.mil!gwyn> writes:
>In article <448 at longway.TIC.COM> dmr at research.att.com (Dennis Ritchie) writes:
>>I wish Gwyn et. al had sounded a bit more embarrassed about using
>>`char d_name[1]' in struct dirent.
>
>Here is the line in question taken directly from my PD dirent implementation:
>	char		d_name[1];	/* name of file */	/* non-ANSI */
>You will note that I'm well aware that a trick is being used here.  ...
>Certainly it is unportable usage, i.e. not guaranteed to work by the C
>language specification.

I question this.  It seems to me that
	typedef struct { junk_t xx; char name[1]; } T;
	T *p = (T *)malloc(sizeof(T) + strlen(s));
	strcpy(p->name, s);
is legal and portable, and I believe I can rigorously prove it from the rule
"objects are composed of bytes":

0.  The result of malloc() is an aligned chunk of sizeof(T)+strlen(s) bytes

1.  Casting such into a (T *), and then back into a (void *), yields the same
    result (else free() wouldn't work; see recent commentary in this group!)

2.  (void *) has the same format as (char *)

3.  Hence, (char *)p points into an array [sizeof(T)+strlen(s)] of char

4.  (char *)(p->name) = (char *)p + offsetof(T, name)

5.  Hence, (char *)(p->name) points into an array [sizeof(T)+strlen(s)-\
    offsetof(T, name)] of char

6.  The cast is a no-op, since p->name in an rvalue context will already be of
    type (char *)

7.  sizeof(T) >= offsetof(T, name) + 1 /* 1 = sizeof(p->name) */

8.  Hence, p->name points into an array of at least strlen(s)+1 chars

9.  Hence, strcpy(p->name, s) is legal.

So it would appear that Doug's implementation of <dirent.h> will work on any
ANSI C implementation.  Have I overlooked anything?

Karl W. Z. Heuer (ima!haddock!karl or karl at haddock.isc.com), The Walking Lint
________
Btw, I agree with Dennis that one should be embarrassed about using this
hack.  I can also prove that one valid way to initialize to a null pointer is
	char *p = '\0';
, but that doesn't make it a good idea.



More information about the Comp.std.c mailing list