Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!snorkelwacker!spdcc!ima!haddock!karl From: karl@haddock.ima.isc.com (Karl Heuer) Newsgroups: comp.std.c Subject: variable-length struct hack Summary: looks legal to me Message-ID: <15364@haddock.ima.isc.com> Date: 7 Dec 89 00:46:35 GMT References: <448@longway.TIC.COM> <450@longway.TIC.COM> Reply-To: karl@haddock.ima.isc.com (Karl Heuer) Organization: Interactive Systems, Cambridge, MA 02138-5302 Lines: 51 [Snarfed from comp.std.unix, thread "Query about "] In article <450@longway.TIC.COM> Doug Gwyn writes: >In article <448@longway.TIC.COM> dmr@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 will work on any ANSI C implementation. Have I overlooked anything? Karl W. Z. Heuer (ima!haddock!karl or karl@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.