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*.