• Re: Nice way of allocating flexible struct.

    From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Dec 14 22:48:42 2025
    From Newsgroup: comp.lang.c

    Kaz Kylheku <643-408-1753@kylheku.com> writes:

    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.

    Clever but inadvisable. Even if the offsetof() expression compiles,
    in most cases it either gives a value that is too small or is
    undefined behavior.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Dec 14 23:55:24 2025
    From Newsgroup: comp.lang.c

    pozz <pozzugno@gmail.com> writes:

    Il 08/10/2025 08:35, Kaz Kylheku ha scritto:

    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.

    struct S {
    unsigned int size;
    unsigned char mode;
    unsigned char array[];
    }

    In a 32-bits integer machine, sizeof(struct S) is 8, because there are
    3 bytes of padding after mode and array is considered empty.

    Now I want to store 9 bytes in array[]. I could use:

    malloc(sizeof(struct S) + 9 * sizeof(unsigned char))=malloc(17)

    or I could use:

    malloc(offsetof(struct S, array[9]))=malloc(14)

    Is the second better (and correct) than the first?

    And another question. Suppose I need an array of struct S. All
    elements have 7-bytes array[] member. How to allocate this array and
    access each element?

    I think I can't use the first malloc (17), neither the second
    (14). Both aren't a multiple of alignment length.

    Right. Getting the (minimum) size exactly right is tricky enough
    to merit writing a macro to compute the desired value.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Mon Dec 15 11:24:29 2025
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    BGB <cr88192@gmail.com> 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*.
    --- Synchronet 3.21a-Linux NewsLink 1.2