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