• Re: AVL tree insertion - programming exercise

    From Tim Rentsch@3:633/10 to All on Wed Oct 21 02:34:39 2026
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <11amvh2$14pou$3@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <86v775h9o2.fsf@linuxsc.com>,
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    [...]

    I don't think this is true anymore, as of C23. Here's the
    relevant text from n3220, sec 7.24.3, describing the return
    value from `malloc` et al:

    |The pointer returned if the allocation succeeds is suitably
    |aligned so that it may be assigned to a pointer to any type of
    |object with a fundamental alignment requirement and size less
    |than or equal to the size requested.

    This sentence is somewhat ambiguous because it's unclear whether
    the "less than or equal to the size requested" criteria applies
    to both the pointer type's size _and_ fundamental alignment
    requirement, or _just_ the size, [...]

    It's poorly constructed but in English it has only one
    grammatical parse.

    Nope. Not even close.

    I think Tim is right about there being only one grammatical parse.

    No, he is not. There are over a thousand ways to parse that
    sentence "grammatically", [...]

    If I've told you once I've told you ULLONG_MAX times -
    don't exaggerate.

    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Wed Oct 21 02:41:12 2026
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <86v775h9o2.fsf@linuxsc.com>,
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <11al4mu$fqtl$1@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    David Brown <david.brown@hesbynett.no> writes:

    On 13/10/2026 10:38, chine.bleu wrote:

    Tim Rentsch wrote:

    sons[2] array. These links are basically pointers, but held
    as uintptr_t so they can take one of the bits to mean "tall",
    which means taller than the other subtree of the node. The
    tree structure should maintain an invariant that at most one

    Memory is cheap.

    Not any more!

    Do not encode tags into low bits of an address unless you really
    know what your doing.

    Agreed, as a principle.

    As a precaution check the return address of malloc
    p = malloc(m);
    if ((uintptr_t)p)&1!=0) abort();

    malloc always returns an address with suitable alignment for any
    fundamental alignment on the target.

    I don't think this is true anymore, as of C23. Here's the
    relevant text from n3220, sec 7.24.3, describing the return
    value from `malloc` et al:

    |The pointer returned if the allocation succeeds is suitably
    |aligned so that it may be assigned to a pointer to any type of
    |object with a fundamental alignment requirement and size less
    |than or equal to the size requested.

    This sentence is somewhat ambiguous because it's unclear whether
    the "less than or equal to the size requested" criteria applies
    to both the pointer type's size _and_ fundamental alignment
    requirement, or _just_ the size, [...]

    It's poorly constructed but in English it has only one
    grammatical parse.

    Nope. Not even close.

    I am confident my assessment is on fairly solid ground in this
    case.

    That said, I don't think there is any value in taking part in a
    shouting match, which seems to be all you want to do here.

    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Wed Oct 21 11:22:16 2026
    In article <86h5ifo0g7.fsf@linuxsc.com>,
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <86v775h9o2.fsf@linuxsc.com>,
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <11al4mu$fqtl$1@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    David Brown <david.brown@hesbynett.no> writes:

    On 13/10/2026 10:38, chine.bleu wrote:

    Tim Rentsch wrote:

    sons[2] array. These links are basically pointers, but held
    as uintptr_t so they can take one of the bits to mean "tall",
    which means taller than the other subtree of the node. The
    tree structure should maintain an invariant that at most one

    Memory is cheap.

    Not any more!

    Do not encode tags into low bits of an address unless you really >>>>>>> know what your doing.

    Agreed, as a principle.

    As a precaution check the return address of malloc
    p = malloc(m);
    if ((uintptr_t)p)&1!=0) abort();

    malloc always returns an address with suitable alignment for any
    fundamental alignment on the target.

    I don't think this is true anymore, as of C23. Here's the
    relevant text from n3220, sec 7.24.3, describing the return
    value from `malloc` et al:

    |The pointer returned if the allocation succeeds is suitably
    |aligned so that it may be assigned to a pointer to any type of
    |object with a fundamental alignment requirement and size less
    |than or equal to the size requested.

    This sentence is somewhat ambiguous because it's unclear whether
    the "less than or equal to the size requested" criteria applies
    to both the pointer type's size _and_ fundamental alignment
    requirement, or _just_ the size, [...]

    It's poorly constructed but in English it has only one
    grammatical parse.

    Nope. Not even close.

    I am confident my assessment is on fairly solid ground in this
    case.

    You don't have to be confident; you can simply show data.

    One can simply run the sentence through a grammar analyzer using
    a standard grammar model, like spaCy (Stanford), using a tool
    like the NLP toolkit, and see what comes out. Using a fairly
    strict model, that single sentence yields well over a thousand
    valid parses.

    That said, I don't think there is any value in taking part in a
    shouting match, which seems to be all you want to do here.

    I am not shouting. You simply made a factual error. *shrug*

    - Dan C.


    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Kaz Kylheku@3:633/10 to All on Wed Oct 21 23:55:28 2026
    On 2026-10-13, Dan Cross <cross@spitfire.i.gajendra.net> wrote:
    In article <11al4mu$fqtl$1@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 13/10/2026 10:38, chine.bleu wrote:
    Tim Rentsch wrote:
    sons[2] array.ÿ These links are basically pointers, but held
    as uintptr_t so they can take one of the bits to mean "tall",
    which means taller than the other subtree of the node.ÿ The
    tree structure should maintain an invariant that at most one
    Memory is cheap.

    Not any more!

    Do not encode tags into low bits of an address unless you really
    know what your doing.

    Agreed, as a principle.

    As a precaution check the return address of malloc
    ÿÿÿÿp = malloc(m);
    ÿÿÿÿif ((uintptr_t)p)&1!=0) abort();

    malloc always returns an address with suitable alignment for any
    fundamental alignment on the target.

    I don't think this is true anymore, as of C23. Here's the
    relevant text from n3220, sec 7.24.3, describing the return
    value from `malloc` et al:

    |The pointer returned if the allocation succeeds is suitably
    |aligned so that it may be assigned to a pointer to any type of
    |object with a fundamental alignment requirement and size less
    |than or equal to the size requested.

    This sentence is somewhat ambiguous because it's unclear whether
    the "less than or equal to the size requested" criteria applies
    to both the pointer type's size _and_ fundamental alignment
    requirement, or _just_ the size, but I believe that it is the
    former (that is, the "and" applies the size requirement to both
    alignment and returned size). Thus, a conforming implementation
    of `malloc(1)` could return a pointer that is `alignof(char)`,
    but not `alignof(int)`.

    Certainly a grammatic parse is possible such that the "less than or
    equal" only applies to the size. But then it is saying "pointer any type
    of object with a fundamental alignment requirement". That doesn't make
    sense. We can regard all objects types as having an alignment
    requirement (which may be relaxed: byte alignment). The mention of
    alignment in this manner is redundant under this view. Or we can regard
    those which have stricter than byte alignment as having a fundamental
    alignment requirement. The above parse then doesn't make sense;
    malloced pointers can be assigned to pointer types with byte alignment.

    Either way it doesn't make a difference because ... ta daa ...
    alignment is constrained by size. No type has an alignment requirement exceeding its size: size being M, but required to be allocated on
    an address divisible by N, where N > M.

    So that is to say, if we state a requirement that the pointer
    successfully returned out of an allocator for a request of size S
    may be used as a pointer to any type whose size is <= S,
    we need no additional wording about alignment! All such types
    have an alignment necessarily <= S.

    Then if we add wording that the pointer may be used to access
    such an object, then that implies the alignment is good also;
    we need not mention alignment.

    If a pointer is good for accessing any type of object whose size is <=
    S, then that pointer must be suitably aligned for any such type.




    I argue that examining the text from C17 and how it has changed
    further supports this interpretation. From C17, sec 7.22.3:

    |The pointer returned if the allocation succeeds is suitably
    |aligned so that it may be assigned to a pointer to any type of
    |object with a fundamental alignment requirement and then used
    |to access such an object or an array of such objects in the
    |space allocated (until the space is explicitly deallocated).

    Here, no mention of the requested size is made at all, and this
    clearly requires the return value from a successful allocation
    by `malloc(1)` to be aligned so that one could assign it to a
    pointer referring to any object of any type with a "fundamental
    alignment requirement."

    "fundamental alignment requirement" is a normative term;
    it refers to alignment <= _Alignof(max_align_t).

    (An alignment that is larger would be something that the program
    declares, not reflecting anything coming form the host machine.)

    The requirement is saying that all successful mallocs (including
    zero sized ones, if not returning null?) must be aligned for
    a max_align_t, and may be accessed as such "in the space provided".

    So malloc(7) can be converted to a pointer to something that has
    8 byte alignment, but only the first 7 bytes of that can be
    accessed.

    For instance struct s { char x[7]; long long y; } could
    be such a type on some platform. If long long has 8 byte alignment,
    the structure type will have that alignment (and padding after x).

    The result of malloc(7) may be converted to "struct s *",
    and the pointer can be used to access p->x[0] through p->x[6].
    x[7] may be trampling on internal malloc bits.
    The allocator could allocate 7 byte (or smaller) objects in 8 byte
    chunks with some flags packed in there.

    Playing games with pointer values can be useful for implementing
    things like, e.g, Harris's lock-free linked list.

    But suppose you need two bits in a pointer, thus four byte alignment. It
    would not make sense to rely on malloc(1) giving you pointers with two
    bits to spare, yet what's what C17 was saying.

    Of course, you're going to malloc at least four bytes, if you
    want four byte alignment.

    The allocator should be allowed to satisfy malloc(1) requests
    out of a byte array (with a separately stored free bitmask
    or radix tree for keeping track of free vs allocated bytes).

    It doesn't make sense to require malloc(1) to provide max_align_t
    alignment.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Thu Oct 22 00:43:13 2026
    Subject: malloc() and alignment (was Re: AVL tree insertion - programming exercise)

    You're a bit late to the thread here, Kaz. :-)

    In article <20261021162805.270@kylheku.com>,
    Kaz Kylheku <046-301-5902@kylheku.com> wrote:
    On 2026-10-13, Dan Cross <cross@spitfire.i.gajendra.net> wrote:
    In article <11al4mu$fqtl$1@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 13/10/2026 10:38, chine.bleu wrote:
    Tim Rentsch wrote:
    sons[2] array.ÿ These links are basically pointers, but held
    as uintptr_t so they can take one of the bits to mean "tall",
    which means taller than the other subtree of the node.ÿ The
    tree structure should maintain an invariant that at most one
    Memory is cheap.

    Not any more!

    Do not encode tags into low bits of an address unless you really
    know what your doing.

    Agreed, as a principle.

    As a precaution check the return address of malloc
    ÿÿÿÿp = malloc(m);
    ÿÿÿÿif ((uintptr_t)p)&1!=0) abort();

    malloc always returns an address with suitable alignment for any
    fundamental alignment on the target.

    I don't think this is true anymore, as of C23. Here's the
    relevant text from n3220, sec 7.24.3, describing the return
    value from `malloc` et al:

    |The pointer returned if the allocation succeeds is suitably
    |aligned so that it may be assigned to a pointer to any type of
    |object with a fundamental alignment requirement and size less
    |than or equal to the size requested.

    This sentence is somewhat ambiguous because it's unclear whether
    the "less than or equal to the size requested" criteria applies
    to both the pointer type's size _and_ fundamental alignment
    requirement, or _just_ the size, but I believe that it is the
    former (that is, the "and" applies the size requirement to both
    alignment and returned size). Thus, a conforming implementation
    of `malloc(1)` could return a pointer that is `alignof(char)`,
    but not `alignof(int)`.

    Certainly a grammatic parse is possible such that the "less than or
    equal" only applies to the size. But then it is saying "pointer any type
    of object with a fundamental alignment requirement". That doesn't make
    sense. We can regard all objects types as having an alignment
    requirement (which may be relaxed: byte alignment). The mention of
    alignment in this manner is redundant under this view. Or we can regard >those which have stricter than byte alignment as having a fundamental >alignment requirement. The above parse then doesn't make sense;
    malloced pointers can be assigned to pointer types with byte alignment.

    Yes, they can. You can always take a pointer that is aligned to
    a stricter boundary and assign it to some type with a weaker
    alignment policy; a pointer that is page aligned is also byte
    aligned.

    The term, "fundamental alignment requirement" is a term of art
    defined in the C standard. All types have alignment
    requirements; an alignment requirement is "fundamental" if it is
    less than or equal to `alignof(max_align_t)`; any time with such
    an alignment requirement has a fundamental alignment
    requirement.

    But not all alignment requirements are fundamental. The
    introduction of `alignas(...)` introduced types with alignment
    requirements greater than `alignof(max_align_t)`; the standard
    introduces another term of art, "extended alignment requirement"
    to describe the alignment requirements of that category of
    types.

    Either way it doesn't make a difference because ... ta daa ...
    alignment is constrained by size. No type has an alignment requirement >exceeding its size: size being M, but required to be allocated on
    an address divisible by N, where N > M.

    So that is to say, if we state a requirement that the pointer
    successfully returned out of an allocator for a request of size S
    may be used as a pointer to any type whose size is <= S,
    we need no additional wording about alignment! All such types
    have an alignment necessarily <= S.

    Then if we add wording that the pointer may be used to access
    such an object, then that implies the alignment is good also;
    we need not mention alignment.

    If a pointer is good for accessing any type of object whose size is <=
    S, then that pointer must be suitably aligned for any such type.

    Yes, that describes the behavior of pointers with respect to the
    types of things they can point to, but not necessary the
    behavior of `malloc` (and calloc and realloc). In particular,
    `malloc` may return a pointer that is _over aligned_ for a given
    size. The language in C17 read to me, and many others, as
    though it _required_ that.

    However, it did not read that way to all implementers, and not
    all implementations did that. It was therefore unsafe to rely
    on that behavior in portable code, even across sytems where `alignof(max_align_t)` is greater than 1. Hence, C23 relaxing
    the behavior.

    I argue that examining the text from C17 and how it has changed
    further supports this interpretation. From C17, sec 7.22.3:

    |The pointer returned if the allocation succeeds is suitably
    |aligned so that it may be assigned to a pointer to any type of
    |object with a fundamental alignment requirement and then used
    |to access such an object or an array of such objects in the
    |space allocated (until the space is explicitly deallocated).

    Here, no mention of the requested size is made at all, and this
    clearly requires the return value from a successful allocation
    by `malloc(1)` to be aligned so that one could assign it to a
    pointer referring to any object of any type with a "fundamental
    alignment requirement."

    "fundamental alignment requirement" is a normative term;
    it refers to alignment <= _Alignof(max_align_t).

    (An alignment that is larger would be something that the program
    declares, not reflecting anything coming form the host machine.)

    The requirement is saying that all successful mallocs (including
    zero sized ones, if not returning null?) must be aligned for
    a max_align_t, and may be accessed as such "in the space provided".

    So malloc(7) can be converted to a pointer to something that has
    8 byte alignment, but only the first 7 bytes of that can be
    accessed.

    I think most readers believe that C17 said that `malloc(7)` must
    return addressed aligned to whatever `alignof(max_align_t)` is,
    full stop.

    C23 relaxes this. All types have an alignment requirement;
    the "weakest" such requirement is whatever `alignof(char)` is;
    usually just 1. The primitive types all have a _fundamental
    alignment requirement_; that is, an alignment requirement that
    is _fundamental_, which is defined to be <= `align(max_align_t)`
    (I suspect this is called "fundamental" because of something
    about the basic types being the "fundamental types" or something
    like that).

    The proposal for change, and the text adopted in C23, is clear
    in its intent: it is meant to say that, if a type has a
    fundamental alignment requirement and that is <= the size of the
    allocation requested in a successful call to `malloc`, then the
    address returned from malloc is suitable for assignment to a
    pointer to that type. However, if the alignment of a type is
    greater than than the size, but still fundamental, then the
    address returned _does not need_ to be suitably aligned for
    assignment to a pointer of that type. (As you correctly point
    out, it can't really be used anyway.)

    For instance struct s { char x[7]; long long y; } could
    be such a type on some platform. If long long has 8 byte alignment,
    the structure type will have that alignment (and padding after x).

    The result of malloc(7) may be converted to "struct s *",
    and the pointer can be used to access p->x[0] through p->x[6].
    x[7] may be trampling on internal malloc bits.

    The allocator could allocate 7 byte (or smaller) objects in 8 byte
    chunks with some flags packed in there.

    No, I don't think that's required under the C23 rules. The
    result of `malloc(7)` must be an address aligned to 4, but need
    not be aligned to 8 (assuming both are fundamental alignments).

    Playing games with pointer values can be useful for implementing
    things like, e.g, Harris's lock-free linked list.

    But suppose you need two bits in a pointer, thus four byte alignment. It >would not make sense to rely on malloc(1) giving you pointers with two
    bits to spare, yet what's what C17 was saying.

    I agree! But people do weird things.

    Of course, you're going to malloc at least four bytes, if you
    want four byte alignment.

    The allocator should be allowed to satisfy malloc(1) requests
    out of a byte array (with a separately stored free bitmask
    or radix tree for keeping track of free vs allocated bytes).

    It doesn't make sense to require malloc(1) to provide max_align_t
    alignment.

    Yes. The C17 "strong alignment" reading made very little sense
    to me.

    - Dan C.


    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Thu Oct 29 13:38:20 2026
    antispam@fricas.org (Waldek Hebisch) writes:

    [I am responding to two postings, restoring a sentence that
    was removed by an intermediate poster]

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2026-10-18 10:42, Waldek Hebisch wrote:

    [...]

    I read the above as: "can access part of object that fits in
    allocated space". That is important for structs and unions: you
    clearly do not want to allocate memory for biggest union variant.
    And I think that allocating memory only for used part of a struct
    is pretty common.

    I confess to being rather incredulous of these claims. More
    below.

    Mind to explain that opinion?

    If I define a (C-)'union' type I definitely want to store anything
    possible in those unions, changing the value to another type as
    necessary. - Mind the 'sizeof' of a union is [at least?] the size
    of its largest component. - If I 'malloc' a union I use that size.
    In cases where I want to use just one [smaller] component-type of
    a union I just allocate that specific type not the union-type.

    But maybe you meant something different?

    I meant what I wrote.

    I think your alleged facts are wrong. The vast majority of C
    programs, and the vast majority of C programmers, have no
    interest in doing either of those things. If C code needs to use
    some part of an allocated struct, a whole struct is allocated.
    Evidence for this pattern can be seen in the common idiom for
    calling malloc():

    struct foo *pfoo = malloc( sizeof *pfoo );

    The case for unions is even weaker. First it is unusual for a
    union to have highly disparate sizes for its members; a factor
    of two or four, maybe, but rarely more than that. Furthermore if
    it were important to access only a subset of the smaller members
    then it is simple to create a new union type with only those
    members. At some level it's necessary to do that to know what
    size to give to malloc(). (And if it's only one member that is
    meant to be accessed, then we expect to follow the above common
    idiom for malloc() with a pointer to the member's type.)

    Of course it is not impossible that there are C programs that
    violate these norms. But they are at best few and far between,
    and anyone who would write such programs runs a greatly increased
    risk of undefined behavior. No sensible developer would do so.

    In any case any use of such anti-patterns is so rare that it
    wasn't a factor in deciding how malloc() is supposed to work.

    C type system is too weak to give exact
    type for more complicated data structure. Instead, real program
    give approximate type and depend on holes in type system.

    This idea is nonsense. The C type system is powerful enough to
    express any type that C code can deal with.

    So you
    have pointer to union and program is structured so that it only
    accesses valid variant. Allocating memory for whole using may
    waste a lot of memory and does not buy you any real safety:
    accessing wrong variant is likely to lead to troubles regardless
    of amount of memory that you allocated. And in case of
    interesting data structures variant does not change after
    creation (allocation). If you need different variant you simply
    allocate new piece of memory.

    BTW: Pascal 'new' for variant records takes extra argument
    specifying requested variant. Corresponding pointer has type
    'pointer to variant record', but legally can only be used with
    the variant specified in call to 'new'. In C you get equivalent
    effect (with essentially the same conditions) by allocating
    amount of memory needed for specific variant of union, but
    assigning 'malloc' result to pointer to union.

    Here we see the key to understanding the earlier comments: we
    have someone who wants to write code in C but who wants to think
    in Pascal. There is no reason to think the C standard means to
    support the anti-patterns being advocated.

    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Waldek Hebisch@3:633/10 to All on Sat Oct 31 13:34:06 2026
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    antispam@fricas.org (Waldek Hebisch) writes:

    [I am responding to two postings, restoring a sentence that
    was removed by an intermediate poster]

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2026-10-18 10:42, Waldek Hebisch wrote:

    [...]

    I read the above as: "can access part of object that fits in
    allocated space". That is important for structs and unions: you
    clearly do not want to allocate memory for biggest union variant.
    And I think that allocating memory only for used part of a struct
    is pretty common.

    I confess to being rather incredulous of these claims. More
    below.

    Mind to explain that opinion?

    If I define a (C-)'union' type I definitely want to store anything
    possible in those unions, changing the value to another type as
    necessary. - Mind the 'sizeof' of a union is [at least?] the size
    of its largest component. - If I 'malloc' a union I use that size.
    In cases where I want to use just one [smaller] component-type of
    a union I just allocate that specific type not the union-type.

    But maybe you meant something different?

    I meant what I wrote.

    I think your alleged facts are wrong. The vast majority of C
    programs, and the vast majority of C programmers, have no
    interest in doing either of those things. If C code needs to use
    some part of an allocated struct, a whole struct is allocated.
    Evidence for this pattern can be seen in the common idiom for
    calling malloc():

    struct foo *pfoo = malloc( sizeof *pfoo );

    I think that allocating smaller size for C struct is rare,
    but that it happens in real C programs.

    The case for unions is even weaker. First it is unusual for a
    union to have highly disparate sizes for its members; a factor
    of two or four, maybe, but rarely more than that.

    One of big guys in programming (maybe Knuth) wrote something
    like "7% saving would be considered significant in most
    branches of engineering". Factor of 2 disparity easily may
    lead to 25% saving from allocating only actually used memory.

    Furthermore if
    it were important to access only a subset of the smaller members
    then it is simple to create a new union type with only those
    members. At some level it's necessary to do that to know what
    size to give to malloc(). (And if it's only one member that is
    meant to be accessed, then we expect to follow the above common
    idiom for malloc() with a pointer to the member's type.)

    If you do not use pointers to unions, then you need to do something
    else, most probably uses casts. Unions limit possible errors,
    so in this case are much better than casts.

    Of course it is not impossible that there are C programs that
    violate these norms. But they are at best few and far between,
    and anyone who would write such programs runs a greatly increased
    risk of undefined behavior. No sensible developer would do so.

    Main data structure in gcc is tree node. It was a C union with
    variants corresponding to various node types. Each node contained
    pointers to other nodes, and such pointers at C level usually
    were just pointers to union. When allocating nodes gcc allocated
    amount of memory for needed variant and after creation of a node
    variant never changed.

    Maybe that was due to exposure to Pascal, IIUC in his first shot
    at gcc Stalmann used part of (extended) Pascal compiler (later
    had to remove this part due to licence restrictions).

    But this is safer alternative to say using 'void *' and casting
    pointers to right structure type.

    In any case any use of such anti-patterns is so rare that it
    wasn't a factor in deciding how malloc() is supposed to work.

    C type system is too weak to give exact
    type for more complicated data structure. Instead, real program
    give approximate type and depend on holes in type system.

    This idea is nonsense. The C type system is powerful enough to
    express any type that C code can deal with.

    Maybe you should write "any C type that ...", which is tautologically
    true, but free of information content. It is well known that
    C type system can not express object oriented concepts. More
    to the point, C type system can not express parametrized types
    and more general recursive types. As a simple example consider
    function that should reverse order of a linked list. At logical
    level we have pointer to next element and a payload, which can
    be arbitrary. That can be expressed using paramtrized type like

    typedef struct {NODE(T) * next; T payload;} NODE(T);

    except for that the above is not valid C. What we can do is
    to write code like:

    typedef struct gen_lst gen_lst;
    struct gen_lst {gen_lst * next;};

    gen_lst *
    nreverse_gen0(gen_lst * el, gen_lst * prev) {
    while(el) {
    gen_lst * next = el->next;
    el->next = prev;
    prev = el;
    el = next;
    }
    return prev;
    }

    and at points of use cast pointer to actual list type to pointer
    to 'gen_lst'. So a program can work with parametrized types,
    but it depends on hole in type safety, that is using casts
    (and explicit provision of C standard about layout of common
    prefix of two structs).

    So you
    have pointer to union and program is structured so that it only
    accesses valid variant. Allocating memory for whole using may
    waste a lot of memory and does not buy you any real safety:
    accessing wrong variant is likely to lead to troubles regardless
    of amount of memory that you allocated. And in case of
    interesting data structures variant does not change after
    creation (allocation). If you need different variant you simply
    allocate new piece of memory.

    BTW: Pascal 'new' for variant records takes extra argument
    specifying requested variant. Corresponding pointer has type
    'pointer to variant record', but legally can only be used with
    the variant specified in call to 'new'. In C you get equivalent
    effect (with essentially the same conditions) by allocating
    amount of memory needed for specific variant of union, but
    assigning 'malloc' result to pointer to union.

    Here we see the key to understanding the earlier comments: we
    have someone who wants to write code in C but who wants to think
    in Pascal. There is no reason to think the C standard means to
    support the anti-patterns being advocated.

    --
    Waldek Hebisch

    --- PyGate Linux v1.5.19
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)