Path: csiph.com!eternal-september.org!feeder.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: Nice way of allocating flexible struct. Date: Wed, 08 Oct 2025 14:57:46 -0700 Organization: None to speak of Lines: 69 Message-ID: <87347thx9h.fsf@example.invalid> References: <20251007233002.852@kylheku.com> <10c63rl$1n5qf$1@dont-email.me> MIME-Version: 1.0 Content-Type: text/plain Injection-Date: Wed, 08 Oct 2025 21:57:48 +0000 (UTC) Injection-Info: dont-email.me; posting-host="f99763ce38f98fe25d4318f53bd1517f"; logging-data="1985318"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18PXVb/ApbASIzPJJscoSlX" User-Agent: Gnus/5.13 (Gnus v5.13) Cancel-Lock: sha1:0UsruzBaY4iYVUv7x3lZtvJGFS8= sha1:zkgYRzo/SuhMWjb41PezzQy4PuA= Xref: csiph.com comp.lang.c:394514 BGB writes: > On 10/8/2025 1:35 AM, Kaz Kylheku wrote: >> Jonas Lund of https://whizzter.woorlic.org/ mentioned this >> trick in a HackerNews comment: >> Given: >> struct S { >> // ... >> T A[]; >> }; >> Don't do this: >> malloc(offsetof(S, A) + n * sizeof (T)); >> But rather this: >> malloc(offsetof(S, A[n])); >> It's easy to forget that the second argument of offsetof is a >> designator, not simply a member name. > > This is assuming offsetof and can deal with general expressions (vs > just field names). IIRC, it is only required to work with field names > (and with plain structs). I just read that part of the standard, and it's not clear whether the second argument to offsetof() has to be a member name or whether it can be something more elaborate. Quoting the N3096 draft of C23, 7.21: offsetof(type, member-designator) which expands to an integer constant expression that has type `size_t`, the value of which is the offset in bytes, to the subobject (designated by *member-designator*), from the beginning of any object of type *type*. The type and member designator shall be such that given static type t; then the expression &(t. *member-designator*) evaluates to an address constant. If the specified *type* defines a new type or if the specified member is a bit-field, the behavior is undefined. The requirements imply that the type can be a struct or a union. The term "member designator" is not used elsewhere in the standard. If the term to be taken literally, then it has to designate a *member*, not an element of a member. But the term "subobject", along with the address constant requirement, could imply that it could be an arbitrary sequence of members and array elements. But in addition to that, in Kaz's example, n is not a constant expression, so `&(t.member-designator)` is not an address constant and therefore `offsetof(S, A[n])` has undefined behavior. Every compiler I've tried handles this "correctly", and I tend to think that a compiler would have to go out of its way not to do so. I'd like to see a future standard make offsetof more flexible, with defined behavior for cases like this. The C99 Rationale shows these possible definitions: (size_t)&(((s_name*)0)->m_name) (size_t)(char*)&(((s_name*)0)->m_name) which, if they work, should handle Kaz's example correctly. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com void Void(void) { Void(); } /* The recursive call of the void */