Path: csiph.com!eternal-september.org!feeder.eternal-september.org!nntp.eternal-september.org!eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: Nice way of allocating flexible struct. Date: Mon, 15 Dec 2025 11:24:29 -0800 Organization: A noiseless patient Spider Lines: 61 Message-ID: <86cy4ftuoi.fsf@linuxsc.com> References: <20251007233002.852@kylheku.com> <10c63rl$1n5qf$1@dont-email.me> <87347thx9h.fsf@example.invalid> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Date: Mon, 15 Dec 2025 19:24:31 +0000 (UTC) Injection-Info: dont-email.me; posting-host="3b66f75cc16331490dd39d06d7ef9603"; logging-data="2231266"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+YcW7c40WFnWXKh9GcU6fVtWepxLn1dFk=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:kI2w276uUb+hZM1j7s8WqdObbRQ= sha1:Pkdy3WmkzzJ7RLjEdTHurt7UNLk= Xref: csiph.com comp.lang.c:395823 Keith Thompson writes: > 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. Clearly the italicized token member-designator is just a placeholder with no semantics implied, and the controlling text here is the word "subobject", which applies recursively. Note also that the phrase "from the beginning of any object of type *type*" precludes the use of 'offsetof(S, A[n])', if n is too large, since A[n] will not be a subobject of an arbitrary object of type *type*.