• Re: AVL tree insertion - programming exercise

    From cross@[email protected] (Dan Cross) to comp.lang.c on Tue Oct 13 21:17:00 2026
    From Newsgroup: comp.lang.c

    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:
    [email protected] (Dan Cross) writes:

    In article <11al4mu$fqtl$[email protected]>,
    Keith Thompson <[email protected]> wrote:

    David Brown <[email protected]> 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.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Tue Oct 13 21:30:52 2026
    From Newsgroup: comp.lang.c

    In article <fHtzS.249963$[email protected]>,
    Scott Lurndal <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <11al4mu$fqtl$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    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."

    Note that the processor ABI documents for x86_64 and
    arm64 also require the return value always be aligned
    congruent to zero module 16.

    Is that true? Looking at the psABI for x86_64, I don't see that
    mentioned anywhere; it does say that `_Alignof(max_align_t)` is
    16, but it doesn't mention `malloc` at all. POSIX is closer,
    and uses language close to that in C17. Arm64 refers to the
    AAPCS64, which doesn't go into the return type of `malloc`,
    either.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From chine.bleu@[email protected] to comp.lang.c on Tue Oct 13 18:01:12 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) wrote:
    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.

    Suitable alignment does not guarantee anything about how bits are used
    in the integer representation. That leads to machine dependent
    assumptions. Assumptions are deadly if not documented. Testing
    assumptions is better,
    --
    Siri Seal of Disavowal #777-000. Disavowed. Denied. @
    NO KINGS For I desire mercy not sacrifice. /|\
    The Church of the Holey Apple .signature 5.5 / \
    of Discordian Mysteries. This post insults Islam. Mohamed
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Tue Oct 13 20:45:56 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) writes:
    In article <11al0l0$eefv$[email protected]>,
    James Kuyper <[email protected]> wrote:
    On 13/10/2026 10:38, chine.bleu wrote:
    As a precaution check the return
    address of malloc

    p = malloc(m);
    if ((uintptr_t)p)&1!=0) abort();

    Just to be pedantic, the standard doesn't guarantee that the expression
    you use will test what you want to test. Since there is a guarantee that >>malloc() returns a universally aligned memory, I think it's actually
    more likely that the test will fail than that malloc() will fail, though >>both probabilities are quite negligible, so it's hard to be sure.

    The syntax and precedence errors aside, the test is clearly
    meant to ensure that the low bit in the return from `malloc` is
    clear, so that it can be repurposed for other things. It should
    likely be:

    p = malloc(m);
    if (p == NULL || ((uintptr_t)p & 1) != 0) abort();

    I'm not sure the NULL test adds anything useful. The low-order bits of
    a null pointer are very likely to be 0 on any real-world system.

    Also, the test only looks at the low-order bits of the result of
    the current malloc() call. If you have an exotic system where
    an aligned pointer can have a low-order bit set to 1, it might
    *sometimes* be set to 0.
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Tue Oct 13 21:13:21 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) writes:
    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:
    [email protected] (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.

    I'll try to use indentation to indicate (imperfectly) how I parse it:

    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.

    So "a fundamental alignment requirement" and "size less than or equal
    to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    I do see a problem with the size requirement being *under* the
    "suitably aligned" requirement. It wrongly implies that satisfying
    the size requirement contributes to the "suitably aligned" requirement.
    But that's not an ambiguous parse.

    Do you see a different way to parse the sentence?
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@[email protected] to comp.lang.c on Wed Oct 14 09:43:01 2026
    From Newsgroup: comp.lang.c

    On 14/10/2026 06:13, Keith Thompson wrote:
    [email protected] (Dan Cross) writes:
    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:
    [email protected] (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.

    I'll try to use indentation to indicate (imperfectly) how I parse it:

    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.

    So "a fundamental alignment requirement" and "size less than or equal
    to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    I do see a problem with the size requirement being *under* the
    "suitably aligned" requirement. It wrongly implies that satisfying
    the size requirement contributes to the "suitably aligned" requirement.
    But that's not an ambiguous parse.

    Do you see a different way to parse the sentence?


    I do. "Operators" in English do not have the same clearly defined
    grammar rules as operators in C :

    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.

    equivalent to :

    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
    less than or equal to the size requested.
    and
    size
    less than or equal to the size requested.



    If you consider "fundamental alignment requirement" as evaluating to a
    number - the result of "alignas" - then I think this parse is also
    possible. I would even go as far as to say this was the intended
    parsing, because the parsing you gave above would mean the paragraph
    only applies to "objects with a fundamental alignment requirement" - and
    all complete object types have alignment requirements.

    Let us suppose a hypothetical implementation where CHAR_BIT == 8,
    sizeof(int) == 4, and alignof(int) == 8.

    What guarantees do you have for "void * p = malloc(4);" ?

    With your parsing, "p" would be correctly aligned for an int - the
    alignment of the type "int" is not relevant to whether the "p" is
    correctly aligned or not, only the size of "int" matters.

    With the second parsing, "p" may not be correctly aligned for an int -
    it is only guaranteed to be aligned for types with alignment
    requirements of 4 or less.


    That second parsing seems to result in a more logical and consistent conclusion. But the first parsing seems more reasonable when reading it.

    (I do not think any of this will make a difference in practice.)

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Wed Oct 14 14:13:47 2026
    From Newsgroup: comp.lang.c

    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote: >[email protected] (Dan Cross) writes:
    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote: >>>[email protected] (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", according to the "standard" rules of
    the English language (a language infamous for having little in
    the way of standard rules).

    We tend to discount those since we're close to the domain, but
    ask a linguist what they think about this sentence and they will
    point out ambiguities. Or use a tool like spaCy to visualize
    the parse trees (from which one can see that those ambiguities
    lead to a combinatorial explosion of valid parses).

    Fortunately, most of these have overlapping meanings, and there
    are only two potentially meaningful interpretations, one where
    the size requirement of the request attaches only to the size of
    the pointed-to type, or one where it attaches to both the size
    and fundamental alignment requirement of the pointed-to type.

    I'll try to use indentation to indicate (imperfectly) how I parse it:

    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.

    So "a fundamental alignment requirement" and "size less than or equal
    to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see a problem with the size requirement being *under* the
    "suitably aligned" requirement. It wrongly implies that satisfying
    the size requirement contributes to the "suitably aligned" requirement.
    But that's not an ambiguous parse.

    Do you see a different way to parse the sentence?

    Adopting your notation, the other meaningful way is

    If the allocation succeeds:
    The pointer returned is suitably aligned so that:
    It may be asigned to a pointer to any type of object with:
    a fundamental alignment requirement _and_ size,
    less than or equal to the size requested.

    This is a grammatically valid parse of that sentence. Given the
    existence of `aligned_alloc`, I can't think of a great reason
    why this second interpretation should be prohibited; there are
    esoteric hardware structures that do things like align 32-bit
    registers on 128-bit boundaries (e.g., the MMIO registers for
    the x86 xAPIC), but pointers to those are not the domain of
    `malloc`.

    Btw, this kind of ambiguity is the primary objection I have to
    attempts at using informal English prose to precisely define a
    programming language.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Wed Oct 14 14:25:59 2026
    From Newsgroup: comp.lang.c

    In article <11amttk$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote: >[email protected] (Dan Cross) writes:
    In article <11al0l0$eefv$[email protected]>,
    James Kuyper <[email protected]> wrote:
    On 13/10/2026 10:38, chine.bleu wrote:
    As a precaution check the return
    address of malloc

    p = malloc(m);
    if ((uintptr_t)p)&1!=0) abort();

    Just to be pedantic, the standard doesn't guarantee that the expression >>>you use will test what you want to test. Since there is a guarantee that >>>malloc() returns a universally aligned memory, I think it's actually
    more likely that the test will fail than that malloc() will fail, though >>>both probabilities are quite negligible, so it's hard to be sure.

    The syntax and precedence errors aside, the test is clearly
    meant to ensure that the low bit in the return from `malloc` is
    clear, so that it can be repurposed for other things. It should
    likely be:

    p = malloc(m);
    if (p == NULL || ((uintptr_t)p & 1) != 0) abort();

    I'm not sure the NULL test adds anything useful. The low-order bits of
    a null pointer are very likely to be 0 on any real-world system.

    It was just meant to `abort` if `malloc` could not allocate for
    whatever reason, and was unrelated to this business of stealing
    low bits to cache other data. The assertion is that, if the
    program survives these tests, then `p` is suitable for use.

    Sorry, I should have been clear about that.

    Also, the test only looks at the low-order bits of the result of
    the current malloc() call. If you have an exotic system where
    an aligned pointer can have a low-order bit set to 1, it might
    *sometimes* be set to 0.

    I think the point is that if one wants to repurpose the low bits
    of the integer representation of `p` for some purpose, then one
    ought to verify the base assumption that those bits are
    available. One need not do that on every malloc call, however.

    In the face of weird implementations and esoteric hardware, I
    don't know that one can have a more general test suitable for
    e.g. a `static_assert()`.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@[email protected] to comp.lang.c on Wed Oct 14 18:03:26 2026
    From Newsgroup: comp.lang.c

    On 2026-10-14 16:13, Dan Cross wrote:

    Btw, this kind of ambiguity is the primary objection I have to
    attempts at using informal English prose to precisely define a
    programming language.

    I'm curious; are there any more "recent" languages (i.e. younger
    than 50 years old) that don't use English (or any other informal
    language) (as opposed to a ["fully"] formal specification)?

    Beyond Algol 68 I found a respective statement only about ML/SML;
    is that all?

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@[email protected] to comp.lang.c on Wed Oct 14 10:39:56 2026
    From Newsgroup: comp.lang.c

    Keith Thompson <[email protected]> writes:

    [email protected] (Dan Cross) writes:

    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:

    [email protected] (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.
    [...]

    Thanks Keith! I owe you one. :)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Wed Oct 14 18:21:57 2026
    From Newsgroup: comp.lang.c

    In article <11ao94e$4o6l$[email protected]>,
    Janis Papanagnou <[email protected]> wrote:
    On 2026-10-14 16:13, Dan Cross wrote:

    Btw, this kind of ambiguity is the primary objection I have to
    attempts at using informal English prose to precisely define a
    programming language.

    I'm curious; are there any more "recent" languages (i.e. younger
    than 50 years old) that don't use English (or any other informal
    language) (as opposed to a ["fully"] formal specification)?

    Beyond Algol 68 I found a respective statement only about ML/SML;
    is that all?

    Beyond SML? I'm afraid I don't know.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Wed Oct 14 20:11:28 2026
    From Newsgroup: comp.lang.c

    In article <11anbq5$16fmd$[email protected]>,
    David Brown <[email protected]> wrote:
    On 14/10/2026 06:13, Keith Thompson wrote:
    [snip]
    So "a fundamental alignment requirement" and "size less than or equal
    to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    I do see a problem with the size requirement being *under* the
    "suitably aligned" requirement. It wrongly implies that satisfying
    the size requirement contributes to the "suitably aligned" requirement.
    But that's not an ambiguous parse.

    Do you see a different way to parse the sentence?

    I do. "Operators" in English do not have the same clearly defined
    grammar rules as operators in C :

    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.

    equivalent to :

    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
    less than or equal to the size requested.
    and
    size
    less than or equal to the size requested.

    Indeed. And in fact, I found some direct evidence for exactly
    this interpretation (the one I claimed was correct). Draft 3054 (https://open-std.org/JTC1/SC22/WG14/www/docs/n3054.pdf)
    includes this in the abstract:

    |The following documents, for all intents and purposes, have
    |been applied to this draft from before and during the October
    |2019 Meeting:
    | ... (on down to the 6th from the top on the second page):
    | - N2293 Alignment requirements for memory management functions

    In turn, N2293 goes into detail about the change, the motivation
    and the present wording in C23; specifically, it is clear that
    they mean for the requested allocation size to factor in the
    fundamental alignment requirement of the pointed-to type. The
    Summary section reads:

    |The alignment requirements of `malloc`, `calloc`, and `realloc`
    |are somewhat confusingly phrased, in a way that affects small
    |allocations (sizes less than `_Alignof(max_align_t))`. Some
    |readers (and implementations) interpret them to demand |`_Alignof(max_align_t)`-alignment even for allocation sizes
    |that could not hold an object with that alignment. We call
    |this the "strong-alignment" reading. Other readers (and
    |implementations) interpret them as requiring the returned
    |memory to be aligned only enough to accommodate those types
    |that could inhabit the returned memory. In particular, since
    |`sizeof(T) >= _Alignof(T)` for all portably defined types `T`,
    |allocations with sizes smaller than `_Alignof(max_align_t)`
    |need only be aligned to the largest power of two less than or
    |equal to the requested size. We call this the "weak-alignment"
    |reading.
    |
    |Many implementations only provide weak-alignment guarantees,
    |and some cases these guarantees are important in conserving
    |both CPU and memory. Therefore, we propose clarifying the
    |wording such that the weak-alignment implementation is
    |unambiguously allowed. Strong-alignment implementations remain
    |correct, and may document the guarantee as an extension. (https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2293.htm)

    I don't think we can get much more definitive than that.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Wed Oct 14 15:25:09 2026
    From Newsgroup: comp.lang.c

    David Brown <[email protected]> writes:
    On 14/10/2026 06:13, Keith Thompson wrote:
    [email protected] (Dan Cross) writes:
    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:
    [email protected] (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.
    I'll try to use indentation to indicate (imperfectly) how I parse
    it:

    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.

    So "a fundamental alignment requirement" and "size less than or equal
    to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    I do see a problem with the size requirement being *under* the
    "suitably aligned" requirement. It wrongly implies that satisfying
    the size requirement contributes to the "suitably aligned" requirement.
    But that's not an ambiguous parse.

    Do you see a different way to parse the sentence?

    I do. "Operators" in English do not have the same clearly defined
    grammar rules as operators in C :

    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.

    equivalent to :

    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
    less than or equal to the size requested.
    and
    size
    less than or equal to the size requested.

    If you consider "fundamental alignment requirement" as evaluating to a
    number - the result of "alignas" - then I think this parse is also
    possible. I would even go as far as to say this was the intended
    parsing, because the parsing you gave above would mean the paragraph
    only applies to "objects with a fundamental alignment requirement" -
    and all complete object types have alignment requirements.

    I think you're missing the fact that "fundamental alignment" is a
    defined term. N3220 6.2.8p2:

    A *fundamental alignment* is a valid alignment less than or equal to
    alignof(max_align_t).

    And p3:

    An *extended alignment* is represented by an alignment greater
    than alignof(max_align_t). It is implementation-defined whether
    any extended alignments are supported and the storage durations
    for which they are supported. A type having an extended alignment
    requirement is an *over-aligned* type.

    The point is that malloc() cannot reliably allocate objects of
    over-aligned types. C23 adds aligned_alloc() for that.

    I read "a fundamental alignment requirement" as a predicate; a
    give object type always has an alignment, and may or may not have
    a fundamental alignment requirement.

    I think I now agree about the ambiguous parse, but I don't think it
    matters, because an object type's alignment cannot be greater than its
    size. I'm not sure that's stated explicitly, but otherwise arrays
    wouldn't work (or the language would have to allow alignment gaps
    between array elements).

    Let us suppose a hypothetical implementation where CHAR_BIT == 8,
    sizeof(int) == 4, and alignof(int) == 8.

    That's not possible. Arrays of int cannot have gaps between the
    elements.

    [...]
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Wed Oct 14 15:42:00 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote: >>>>[email protected] (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", according to the "standard" rules of
    the English language (a language infamous for having little in
    the way of standard rules).

    We tend to discount those since we're close to the domain, but
    ask a linguist what they think about this sentence and they will
    point out ambiguities. Or use a tool like spaCy to visualize
    the parse trees (from which one can see that those ambiguities
    lead to a combinatorial explosion of valid parses).

    Fortunately, most of these have overlapping meanings, and there
    are only two potentially meaningful interpretations, one where
    the size requirement of the request attaches only to the size of
    the pointed-to type, or one where it attaches to both the size
    and fundamental alignment requirement of the pointed-to type.

    I'll try to use indentation to indicate (imperfectly) how I parse it:

    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.

    So "a fundamental alignment requirement" and "size less than or equal
    to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful
    malloc(1) returns a value that can be assigned to a pointer of type
    char*, which points to the object type "char", which does have (a
    fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    I do see a problem with the size requirement being *under* the
    "suitably aligned" requirement. It wrongly implies that satisfying
    the size requirement contributes to the "suitably aligned" requirement.
    But that's not an ambiguous parse.

    Do you see a different way to parse the sentence?

    Adopting your notation, the other meaningful way is

    If the allocation succeeds:
    The pointer returned is suitably aligned so that:
    It may be asigned to a pointer to any type of object with:
    a fundamental alignment requirement _and_ size,
    less than or equal to the size requested.

    This is a grammatically valid parse of that sentence. Given the
    existence of `aligned_alloc`, I can't think of a great reason
    why this second interpretation should be prohibited; there are
    esoteric hardware structures that do things like align 32-bit
    registers on 128-bit boundaries (e.g., the MMIO registers for
    the x86 xAPIC), but pointers to those are not the domain of
    `malloc`.

    If you allow an object type to have an alignment greater than its
    size, then the ambiguity can result in an actual difference in the requirements. But then arrays don't work.

    Btw, this kind of ambiguity is the primary objection I have to
    attempts at using informal English prose to precisely define a
    programming language.

    I'm not sure that anything other than English (or other human
    language) prose would be a practical way to define C. There have
    been attempts to define formal semantics for C, but they haven't
    caught on.
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@[email protected] to comp.lang.c on Wed Oct 14 20:54:58 2026
    From Newsgroup: comp.lang.c

    Keith Thompson <[email protected]> writes:

    [email protected] (Dan Cross) writes:

    In article <11al0l0$eefv$[email protected]>,
    James Kuyper <[email protected]> wrote:

    On 13/10/2026 10:38, chine.bleu wrote:
    As a precaution check the return

    address of malloc

    p = malloc(m);
    if ((uintptr_t)p)&1!=0) abort();

    Just to be pedantic, the standard doesn't guarantee that the
    expression you use will test what you want to test. Since there
    is a guarantee that malloc() returns a universally aligned memory,
    I think it's actually more likely that the test will fail than
    that malloc() will fail, though both probabilities are quite
    negligible, so it's hard to be sure.

    The syntax and precedence errors aside, the test is clearly
    meant to ensure that the low bit in the return from `malloc` is
    clear, so that it can be repurposed for other things. It should
    likely be:

    p = malloc(m);
    if (p == NULL || ((uintptr_t)p & 1) != 0) abort();

    I'm not sure the NULL test adds anything useful. The low-order bits
    of a null pointer are very likely to be 0 on any real-world system.

    Surely we would like to protect against a null pointer being used
    to access a non-existent object.

    Also, the test only looks at the low-order bits of the result of
    the current malloc() call. If you have an exotic system where
    an aligned pointer can have a low-order bit set to 1, it might
    *sometimes* be set to 0.

    As long as the bit in question /is/ zero it doesn't matter if it
    might have been one. The bit will always be cleared before using
    the pointer as a pointer, so the pointer value in use will match
    the pointer value originally returned.

    Thus we might write a safe allocation function as follows:

    void *
    safe_new( size_t n ){
    void *p = 0;

    while( !p || (uintptr_t)p %2 != 0 ) free( p ), p = malloc( n++ );

    return p;
    }

    Of course it might not succeed in some cases, but it will be
    quite obvious if it doesn't. :)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Wed Oct 14 22:47:53 2026
    From Newsgroup: comp.lang.c

    Tim Rentsch <[email protected]> writes:
    Keith Thompson <[email protected]> writes:
    [email protected] (Dan Cross) writes:
    In article <11al0l0$eefv$[email protected]>,
    James Kuyper <[email protected]> wrote:
    On 13/10/2026 10:38, chine.bleu wrote:
    As a precaution check the return

    address of malloc

    p = malloc(m);
    if ((uintptr_t)p)&1!=0) abort();

    Just to be pedantic, the standard doesn't guarantee that the
    expression you use will test what you want to test. Since there
    is a guarantee that malloc() returns a universally aligned memory,
    I think it's actually more likely that the test will fail than
    that malloc() will fail, though both probabilities are quite
    negligible, so it's hard to be sure.

    The syntax and precedence errors aside, the test is clearly
    meant to ensure that the low bit in the return from `malloc` is
    clear, so that it can be repurposed for other things. It should
    likely be:

    p = malloc(m);
    if (p == NULL || ((uintptr_t)p & 1) != 0) abort();

    I'm not sure the NULL test adds anything useful. The low-order bits
    of a null pointer are very likely to be 0 on any real-world system.

    Surely we would like to protect against a null pointer being used
    to access a non-existent object.

    Also, the test only looks at the low-order bits of the result of
    the current malloc() call. If you have an exotic system where
    an aligned pointer can have a low-order bit set to 1, it might
    *sometimes* be set to 0.

    As long as the bit in question /is/ zero it doesn't matter if it
    might have been one. The bit will always be cleared before using
    the pointer as a pointer, so the pointer value in use will match
    the pointer value originally returned.

    Thus we might write a safe allocation function as follows:

    void *
    safe_new( size_t n ){
    void *p = 0;

    while( !p || (uintptr_t)p %2 != 0 ) free( p ), p = malloc( n++ );

    return p;
    }

    Of course it might not succeed in some cases, but it will be
    quite obvious if it doesn't. :)

    I might be missing the point of "chine.bleu"'s suggestion. I was
    assuming that the point was to check whether it's possible *in
    general* to play with the low-order bits of malloc'ed pointers.

    On most systems, you can probably make that assumption based on
    what you know about the system's memory model. But it can't be
    checked reliably at run time.

    If I wanted my program to play with low-order bits of pointers, and
    refuse to run if the system doesn't support that, I might write a
    program that does a *lot* of malloc() calls and invoke it during
    configuration, refusing to proceed with the build if it reports that
    malloc() can return an "odd" pointer. Aborting during execution
    feels sloppy.
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@[email protected] to comp.lang.c on Thu Oct 15 09:28:30 2026
    From Newsgroup: comp.lang.c

    On 15/10/2026 00:25, Keith Thompson wrote:
    David Brown <[email protected]> writes:
    On 14/10/2026 06:13, Keith Thompson wrote:
    [email protected] (Dan Cross) writes:
    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:
    [email protected] (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.
    I'll try to use indentation to indicate (imperfectly) how I parse
    it:

    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.

    So "a fundamental alignment requirement" and "size less than or equal
    to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    I do see a problem with the size requirement being *under* the
    "suitably aligned" requirement. It wrongly implies that satisfying
    the size requirement contributes to the "suitably aligned" requirement.
    But that's not an ambiguous parse.

    Do you see a different way to parse the sentence?

    I do. "Operators" in English do not have the same clearly defined
    grammar rules as operators in C :

    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.

    equivalent to :

    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
    less than or equal to the size requested.
    and
    size
    less than or equal to the size requested.

    If you consider "fundamental alignment requirement" as evaluating to a
    number - the result of "alignas" - then I think this parse is also
    possible. I would even go as far as to say this was the intended
    parsing, because the parsing you gave above would mean the paragraph
    only applies to "objects with a fundamental alignment requirement" -
    and all complete object types have alignment requirements.

    I think you're missing the fact that "fundamental alignment" is a
    defined term. N3220 6.2.8p2:

    A *fundamental alignment* is a valid alignment less than or equal to
    alignof(max_align_t).


    I am aware of that definition, and did not miss it.

    And p3:

    An *extended alignment* is represented by an alignment greater
    than alignof(max_align_t). It is implementation-defined whether
    any extended alignments are supported and the storage durations
    for which they are supported. A type having an extended alignment
    requirement is an *over-aligned* type.

    Again, I read that too.


    The point is that malloc() cannot reliably allocate objects of
    over-aligned types. C23 adds aligned_alloc() for that.

    Yes.


    I read "a fundamental alignment requirement" as a predicate; a
    give object type always has an alignment, and may or may not have
    a fundamental alignment requirement.

    Yes, I can see that is how you read it. And I also think that is the
    more obvious reading of the paragraph. I just don't think it is the
    only one.


    I think I now agree about the ambiguous parse, but I don't think it
    matters, because an object type's alignment cannot be greater than its
    size. I'm not sure that's stated explicitly, but otherwise arrays
    wouldn't work (or the language would have to allow alignment gaps
    between array elements).

    Let us suppose a hypothetical implementation where CHAR_BIT == 8,
    sizeof(int) == 4, and alignof(int) == 8.

    That's not possible. Arrays of int cannot have gaps between the
    elements.


    That does sound right. An implementation that wanted to keep its 32-bit
    ints in 64-bit slots would have to have the size of the int as the full 64-bit, with 32 of those bits being padding. So my hypothetical implementation here could not exist.

    (Dan's recent post - as is often the case - is more complete and
    considered, and better researched than mine, so I defer to that for why
    the changes were made to the standard and how they should be interpreted.)



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@[email protected] to comp.lang.c on Thu Oct 15 04:42:05 2026
    From Newsgroup: comp.lang.c

    Keith Thompson <[email protected]> writes:

    Tim Rentsch <[email protected]> writes:

    Keith Thompson <[email protected]> writes:

    [email protected] (Dan Cross) writes:

    In article <11al0l0$eefv$[email protected]>,
    James Kuyper <[email protected]> wrote:

    On 13/10/2026 10:38, chine.bleu wrote:
    As a precaution check the return

    address of malloc

    p = malloc(m);
    if ((uintptr_t)p)&1!=0) abort();

    Just to be pedantic, the standard doesn't guarantee that the
    expression you use will test what you want to test. Since there
    is a guarantee that malloc() returns a universally aligned memory,
    I think it's actually more likely that the test will fail than
    that malloc() will fail, though both probabilities are quite
    negligible, so it's hard to be sure.

    The syntax and precedence errors aside, the test is clearly
    meant to ensure that the low bit in the return from `malloc` is
    clear, so that it can be repurposed for other things. It should
    likely be:

    p = malloc(m);
    if (p == NULL || ((uintptr_t)p & 1) != 0) abort();

    I'm not sure the NULL test adds anything useful. The low-order bits
    of a null pointer are very likely to be 0 on any real-world system.

    Surely we would like to protect against a null pointer being used
    to access a non-existent object.

    Also, the test only looks at the low-order bits of the result of
    the current malloc() call. If you have an exotic system where
    an aligned pointer can have a low-order bit set to 1, it might
    *sometimes* be set to 0.

    As long as the bit in question /is/ zero it doesn't matter if it
    might have been one. The bit will always be cleared before using
    the pointer as a pointer, so the pointer value in use will match
    the pointer value originally returned.

    Thus we might write a safe allocation function as follows:

    void *
    safe_new( size_t n ){
    void *p = 0;

    while( !p || (uintptr_t)p %2 != 0 ){
    free( p ), p = malloc( n++ );
    }

    return p;
    }

    Of course it might not succeed in some cases, but it will be
    quite obvious if it doesn't. :)

    I might be missing the point of "chine.bleu"'s suggestion. I was
    assuming that the point was to check whether it's possible *in
    general* to play with the low-order bits of malloc'ed pointers.

    I took the tests with the abort() calls to be simply protection
    against an unsuitable pointer being returned. Nothing more
    complicated than that.

    The point I was making is different. It doesn't matter if the
    bit in question is always zero as long as it is zero a fair
    amount of the time. Also it doesn't have to be a low order bit,
    or any pre-chosen bit, as long as some particular bit is zero
    often enough. We can imagine doing a lot of malloc() calls at
    program startup and tallying how often a zero appears at each bit
    position, and using one of the bit positions with a high count.
    Simply do enough malloc() calls until the value returned has a
    zero bit at the selected bit position. The code above looks at a
    fixed low order bit, but it could just as well look at a bit
    whose position was chosen at run time during program startup.

    On most systems, you can probably make that assumption based on
    what you know about the system's memory model. But it can't be
    checked reliably at run time.

    If I wanted my program to play with low-order bits of pointers, and
    refuse to run if the system doesn't support that, I might write a
    program that does a *lot* of malloc() calls and invoke it during configuration, refusing to proceed with the build if it reports that
    malloc() can return an "odd" pointer. Aborting during execution
    feels sloppy.

    I understand what you're saying. It's mostly orthogonal to what
    I was saying.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Thu Oct 15 14:25:36 2026
    From Newsgroup: comp.lang.c

    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote: >[email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [snip]
    So "a fundamental alignment requirement" and "size less than or equal
    to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful
    malloc(1) returns a value that can be assigned to a pointer of type
    char*, which points to the object type "char", which does have (a
    fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    Under that reading, even if the requested size can't hold an
    object of a type with a stricter fundamental alignment
    requirement because it's too small, the return value from malloc
    is artificially constrained so that the pointer refers to an
    address that is aligned to `alignof(max_align_t)`. For example,
    an x86_64 implementation following the System V ABI would always
    return a pointer to an address aligned on a 16-byte boundary,
    even for `malloc(1)`.

    The N2293 wording adopted in C23 says that that need not be the
    case. An implementation can choose to return a pointer that is
    more strictly aligned for a small allocation (internally, lots
    of `malloc`s have some minimum allocation size that is greater
    than `sizeof(max_align_t)` and return all pointers aligned to
    whatever that is), but it does not have to (e.g., in some
    applications space is at a premium, so small allocations might
    be handled specially).

    [snip]
    If you allow an object type to have an alignment greater than its
    size, then the ambiguity can result in an actual difference in the >requirements. But then arrays don't work.

    That's ok; this is only about the properties vis alignment of
    the pointer returned from malloc. Consider x86_64 with the
    System V ABI again or ARM64 with the AAPCS; suppose that
    `char *p = malloc(char[3]);` assigns a non-NULL ponter to `p`.
    The question is only whether or not the address `p` refers to is
    required to be aligned on a 16-byte boundary; under the
    '"strong-alignment" reading' in N2293, it is, even though the
    allocated size is only 3. Under the '"weak-alignment" reading',
    the return address can aligned on an even boundary, and C23
    codified the weak-alignment reading. Note that neither reading
    says anything about the size of elements or their alignment in
    an allocation that is treated like an array; it only affects
    the alignment of the starting address.

    N2293 mentions this, but it probably bears repeating here: the
    utility of forcing `malloc` to align small allocations to e.g., `alignof(max_align_t)` seems mostly restricted to having some
    hope of overloading the low bits of the returned address for
    some program-specific purpose, which is how this came up in the
    first place in this thread. As has already been mentioned, the
    standard makes no guarnatee about the integer representation of
    addresses, so this is already somewhat fraught, but on many
    platforms we may assume that as a practical matter, for a
    pointer to a suitably aligned address, those bits will be clear
    as a practical matter; I suppose if one wants that for a small
    allocation, use `aligned_alloc`. Caveat emptor.

    Btw, this kind of ambiguity is the primary objection I have to
    attempts at using informal English prose to precisely define a
    programming language.

    I'm not sure that anything other than English (or other human
    language) prose would be a practical way to define C. There have
    been attempts to define formal semantics for C, but they haven't
    caught on.

    Yeah. For C, I think that it would be nearly impossible to
    retrofit onto the language at this point. I have high hopes for
    Rust, but folks I've spoken to who have been active in the Rust
    formal methods interesting group seem pessimistic about a
    formally defined semantics, which is a shame.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@[email protected] to comp.lang.c on Thu Oct 15 14:07:51 2026
    From Newsgroup: comp.lang.c

    On 10/14/2026 10:47 PM, Keith Thompson wrote:
    Tim Rentsch <[email protected]> writes:
    Keith Thompson <[email protected]> writes:
    [email protected] (Dan Cross) writes:
    In article <11al0l0$eefv$[email protected]>,
    James Kuyper <[email protected]> wrote:
    On 13/10/2026 10:38, chine.bleu wrote:
    As a precaution check the return

    address of malloc

    p = malloc(m);
    if ((uintptr_t)p)&1!=0) abort();

    Just to be pedantic, the standard doesn't guarantee that the
    expression you use will test what you want to test. Since there
    is a guarantee that malloc() returns a universally aligned memory,
    I think it's actually more likely that the test will fail than
    that malloc() will fail, though both probabilities are quite
    negligible, so it's hard to be sure.

    The syntax and precedence errors aside, the test is clearly
    meant to ensure that the low bit in the return from `malloc` is
    clear, so that it can be repurposed for other things. It should
    likely be:

    p = malloc(m);
    if (p == NULL || ((uintptr_t)p & 1) != 0) abort();

    I'm not sure the NULL test adds anything useful. The low-order bits
    of a null pointer are very likely to be 0 on any real-world system.

    Surely we would like to protect against a null pointer being used
    to access a non-existent object.

    Also, the test only looks at the low-order bits of the result of
    the current malloc() call. If you have an exotic system where
    an aligned pointer can have a low-order bit set to 1, it might
    *sometimes* be set to 0.

    As long as the bit in question /is/ zero it doesn't matter if it
    might have been one. The bit will always be cleared before using
    the pointer as a pointer, so the pointer value in use will match
    the pointer value originally returned.

    Thus we might write a safe allocation function as follows:

    void *
    safe_new( size_t n ){
    void *p = 0;

    while( !p || (uintptr_t)p %2 != 0 ) free( p ), p = malloc( n++ ); >>
    return p;
    }

    Of course it might not succeed in some cases, but it will be
    quite obvious if it doesn't. :)

    I might be missing the point of "chine.bleu"'s suggestion. I was
    assuming that the point was to check whether it's possible *in
    general* to play with the low-order bits of malloc'ed pointers.

    On most systems, you can probably make that assumption based on
    what you know about the system's memory model. But it can't be
    checked reliably at run time.

    If I wanted my program to play with low-order bits of pointers, and
    refuse to run if the system doesn't support that, I might write a
    program that does a *lot* of malloc() calls and invoke it during configuration, refusing to proceed with the build if it reports that
    malloc() can return an "odd" pointer. Aborting during execution
    feels sloppy.


    Playing the the low order bits is common in some exotic lock-free
    algorihtms. BUT, its much better to over allocate using say malloc, then manually align the pointer it returns. Then we can say that pointer is
    aligned on say a 8192 boundary. We know it size if 8192. But we had to
    ask malloc for more memory to make sure we can align and have the right
    size data block. THEN! We can play with the aligned address and store
    whatever data we want. Just make sure to strip it out before we use the aligned and packed pointer.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Thu Oct 15 15:54:33 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) writes:
    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [snip]
    So "a fundamental alignment requirement" and "size less than or equal >>>>to the size requested" are parallel requirements, both applying to
    the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful >>malloc(1) returns a value that can be assigned to a pointer of type
    char*, which points to the object type "char", which does have (a >>fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I don't see any other way to interpret that. It requires that
    a pointer returned by a successful malloc() must *always* be
    suitably aligned. A pointer returned by malloc(1) must be aligned
    so that it can point to a long long, for example.

    It overlooks the fact that it can't point to a long long, because
    there isn't room for it. And there's no portable way to tell if an implementation violates this requirement. If an implementation's
    malloc(1) returns an "odd" pointer, it violates the standard's
    requirement, but that violation likely has no real effect.

    For C23, it was recognized that requiring strict alignment for
    tiny allocations is neither useful nor necessarily meaningful.
    malloc(1) might as well be allowed to return an "odd" pointer.

    Under that reading, even if the requested size can't hold an
    object of a type with a stricter fundamental alignment
    requirement because it's too small, the return value from malloc
    is artificially constrained so that the pointer refers to an
    address that is aligned to `alignof(max_align_t)`. For example,
    an x86_64 implementation following the System V ABI would always
    return a pointer to an address aligned on a 16-byte boundary,
    even for `malloc(1)`.

    And of course that conforms to both the C17 and the C2y requirements.

    The N2293 wording adopted in C23 says that that need not be the
    case. An implementation can choose to return a pointer that is
    more strictly aligned for a small allocation (internally, lots
    of `malloc`s have some minimum allocation size that is greater
    than `sizeof(max_align_t)` and return all pointers aligned to
    whatever that is), but it does not have to (e.g., in some
    applications space is at a premium, so small allocations might
    be handled specially).

    Right.

    [snip]
    If you allow an object type to have an alignment greater than its
    size, then the ambiguity can result in an actual difference in the >>requirements. But then arrays don't work.

    That's ok; this is only about the properties vis alignment of
    the pointer returned from malloc. Consider x86_64 with the
    System V ABI again or ARM64 with the AAPCS; suppose that
    `char *p = malloc(char[3]);` assigns a non-NULL ponter to `p`.

    Alas, that's a syntax error. The argument to malloc is an expression,
    not a type. (I'm certain you knew that.) You can write
    `malloc(sizeof (char[3])`, but that's just malloc(3); no type
    information is passed.

    And the alignment of char[3] is 1. An array of two char[3] objects has
    a size of 6; there are no alignment gaps between the elements.

    The question is only whether or not the address `p` refers to is
    required to be aligned on a 16-byte boundary; under the
    '"strong-alignment" reading' in N2293, it is, even though the
    allocated size is only 3. Under the '"weak-alignment" reading',
    the return address can aligned on an even boundary, and C23
    codified the weak-alignment reading. Note that neither reading
    says anything about the size of elements or their alignment in
    an allocation that is treated like an array; it only affects
    the alignment of the starting address.

    Under C17, the result of malloc(3) must be strongly aligned (8
    bytes, for example). Under C23, it has to be at least 2 if there
    are any types with 2-byte alignment.

    N2293 mentions this, but it probably bears repeating here: the
    utility of forcing `malloc` to align small allocations to e.g., `alignof(max_align_t)` seems mostly restricted to having some
    hope of overloading the low bits of the returned address for
    some program-specific purpose, which is how this came up in the
    first place in this thread.

    It hadn't occurred to me that that might be the reason. I assumed
    it was just because strong alignment for small allocations is not
    useful and should not be required. Allocating single bytes for
    malloc(1) calls could have real advantages for some programs, and
    the standard shouldn't prohibit that.

    As I recall, there was a discussion about reusing low-order bits of
    pointers, and the fact that malloc(1) no longer has to be strongly
    aligned became relevant to that. It doesn't imply that that was
    the reason.

    There might be an ambiguity in the English grammar of the C23
    statement about the alignment of malloc()ed pointers, but I don't
    *think* we've seen a case where the two (or more?) different
    interpretations actually lead to different semantics.

    As has already been mentioned, the
    standard makes no guarnatee about the integer representation of
    addresses, so this is already somewhat fraught, but on many
    platforms we may assume that as a practical matter, for a
    pointer to a suitably aligned address, those bits will be clear
    as a practical matter; I suppose if one wants that for a small
    allocation, use `aligned_alloc`. Caveat emptor.

    Yes, if you need aligned pointers, aligned_alloc() is the way to
    get them. aligned_alloc(8, 1) gives you an 8-byte aligned pointer
    to a 1-byte object; if that's useful, it's because you're doing
    something weird. And if you need to fiddle with the low-order
    bits of aligned pointers, you could run into the problems we've
    already discussed.

    [snip]
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kaz Kylheku@[email protected] to comp.lang.c on Thu Oct 15 23:51:18 2026
    From Newsgroup: comp.lang.c

    On 2026-10-15, Keith Thompson <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote: >>>[email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [snip]
    So "a fundamental alignment requirement" and "size less than or equal >>>>>to the size requested" are parallel requirements, both applying to >>>>>the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful >>>malloc(1) returns a value that can be assigned to a pointer of type >>>char*, which points to the object type "char", which does have (a >>>fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I suspect that this should be read as "any type of object which can
    fit into the requested size".

    I don't see any other way to interpret that.

    It can be interpreted a a defect.

    Note that in a single sentence, the requirement is being given that:

    - the pointer from a successful allocation is aligned for any object

    - the pointer can be used to access such an object ... in the space
    allocated

    From that we can infer that "any type" cannot literally be any type
    Because if we have a successful malloc(1) and we choose "long long" to
    be any type, the literally interpreted requirements are that we can
    convert the pointer to "long long *" and use the resulting pointer for
    access. That requires a malloc(1) to be adjusted to malloc(sizeof (long
    long)); the requirement goes beyond alignment.

    It must be that "any type" means "any type that can be
    stored in the space allocated".

    Then the whole thing makes sense; yes, the pointer can be converted
    to that type and used to access such an object in the space allocated,
    because it fits.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @[email protected]
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Thu Oct 15 18:14:07 2026
    From Newsgroup: comp.lang.c

    Kaz Kylheku <[email protected]> writes:
    On 2026-10-15, Keith Thompson <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote: >>>>[email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [snip]
    So "a fundamental alignment requirement" and "size less than or equal >>>>>>to the size requested" are parallel requirements, both applying to >>>>>>the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful >>>>malloc(1) returns a value that can be assigned to a pointer of type >>>>char*, which points to the object type "char", which does have (a >>>>fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I suspect that this should be read as "any type of object which can
    fit into the requested size".

    I don't see any other way to interpret that.

    It can be interpreted a a defect.

    Sure. My point is that I see no ambiguity in the C17 wording. A
    non-null pointer returned by malloc() must be "suitably aligned".

    Yes, it's a defect. What it says is clear. What it says is also
    logically inconsistent.

    Given a typical memory model (not mandated by the standard), it could
    mean that a successful malloc(1) must return a pointer whose low-order N
    bits are zero, where N is typically about 3 or 4. Of course the
    standard couldn't express it that way.

    Note that in a single sentence, the requirement is being given that:

    - the pointer from a successful allocation is aligned for any object

    - the pointer can be used to access such an object ... in the space
    allocated

    From that we can infer that "any type" cannot literally be any type
    Because if we have a successful malloc(1) and we choose "long long" to
    be any type, the literally interpreted requirements are that we can
    convert the pointer to "long long *" and use the resulting pointer for access. That requires a malloc(1) to be adjusted to malloc(sizeof (long long)); the requirement goes beyond alignment.

    It must be that "any type" means "any type that can be
    stored in the space allocated".

    But that's not what it says.

    Then the whole thing makes sense; yes, the pointer can be converted
    to that type and used to access such an object in the space allocated, because it fits.

    Yes, the whole thing makes sense if you change the wording. That's
    what C23 did (perhaps not entirely successfully); it corrected
    the defect.

    There is at least one possibly visible semantic change from the
    C23 wording. Assume alignof(long double) > 16. In C17, this
    is well defined (if malloc succeeds, and given the most nearly
    reasonable interpretation of the defective C17 wording):

    long double *p = malloc(1);

    though any attempt to dereference p has undefined behavior. In C23,
    it can have undefined behavior if the pointer returned by malloc
    is not appropriately aligned. (On most implementations, the UB
    doesn't do anything bad; the misaligned pointer value is simply
    stored in p.)

    The behavior isn't *always* undefined. The pointer returned by
    malloc(1) could be appropriately aligned (always or just in this
    one instance), or long double could have an alignment of 1 byte.
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Fri Oct 16 03:09:29 2026
    From Newsgroup: comp.lang.c

    In article <11arlj9$2q39c$[email protected]>,
    Keith Thompson <[email protected]> wrote: >[email protected] (Dan Cross) writes:
    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [snip]
    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I don't see any other way to interpret that.

    Nor do I, but N2293 says that some implementers did. Here's how
    David Goldblatt explains it:

    |The current wording
    |
    |C17, 7.22.3 states: "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".
    |
    |The two clauses of interest here are "it may be assigned to a
    |pointer to any type of object with a fundamental alignment
    |requirement" and "and then used to access such an object or an
    |array of such objects in the space allocated". Strong-alignment
    |readers tend to see both clauses as independent, whereas
    |weak-alignment readers tend to see the second as an adverbial
    |clause modifying the first. Neither reading is completely
    |satisfactory. The strong-alignment view implies an independent
    |clause with a tense mismatch or a missing verb; the
    |weak-alignment view uses "and then" when "then" would suffice,
    |which could imply a sequencing at odds with the interpretation.

    I don't see it, myself, and I'm a "strong-alignment reader", but
    he goes on:

    |In the author's view, attempting to declare one of these two
    |parsings correct is a waste of time. Many different
    |implementers, fluent in English and acting in good faith, have
    |parsed the sentence both ways. The question is how to
    |reconcile the divergence.

    Indeed.

    It requires that
    a pointer returned by a successful malloc() must *always* be
    suitably aligned. A pointer returned by malloc(1) must be aligned
    so that it can point to a long long, for example.

    It overlooks the fact that it can't point to a long long, because
    there isn't room for it. And there's no portable way to tell if an >implementation violates this requirement. If an implementation's
    malloc(1) returns an "odd" pointer, it violates the standard's
    requirement, but that violation likely has no real effect.

    Agreed.

    For C23, it was recognized that requiring strict alignment for
    tiny allocations is neither useful nor necessarily meaningful.
    malloc(1) might as well be allowed to return an "odd" pointer.

    Yes.

    [snip]
    If you allow an object type to have an alignment greater than its
    size, then the ambiguity can result in an actual difference in the >>>requirements. But then arrays don't work.

    That's ok; this is only about the properties vis alignment of
    the pointer returned from malloc. Consider x86_64 with the
    System V ABI again or ARM64 with the AAPCS; suppose that
    `char *p = malloc(char[3]);` assigns a non-NULL ponter to `p`.

    Alas, that's a syntax error. The argument to malloc is an expression,
    not a type. (I'm certain you knew that.)

    Indeed; I meant to write, `malloc(sizeof(char[3]))` but as you
    correctly surmised, I mistyped.

    You can write
    `malloc(sizeof (char[3])`, but that's just malloc(3); no type
    information is passed.

    And the alignment of char[3] is 1. An array of two char[3] objects has
    a size of 6; there are no alignment gaps between the elements.

    Right. The only reason I wanted to write it that way was to
    drive that point home; in the "strong-alignment" reading, for
    small allocations, the alignment of the address the pointer
    returned from `malloc` refers to is totally decoupled from the
    requested size: `sizeof(char[3])` is three, which reflects the
    fundamental alignment requirement of `char[3]` being 1; because
    `malloc` doesn't know whether the resulting allocation will be
    used to store something with a fundamental alignment criteria of
    two or not (assuming such a type exists in the implementation),
    it must return a pointer to an address that is at least aligned
    to an even boundary. In any event, whether `malloc` must return
    something with stronger alignment, or merely can at its own
    convenience, is the interesting high order bit here.

    But, you know, I can't type so flubbed the example. :-D

    The question is only whether or not the address `p` refers to is
    required to be aligned on a 16-byte boundary; under the
    '"strong-alignment" reading' in N2293, it is, even though the
    allocated size is only 3. Under the '"weak-alignment" reading',
    the return address can aligned on an even boundary, and C23
    codified the weak-alignment reading. Note that neither reading
    says anything about the size of elements or their alignment in
    an allocation that is treated like an array; it only affects
    the alignment of the starting address.

    Under C17, the result of malloc(3) must be strongly aligned (8
    bytes, for example). Under C23, it has to be at least 2 if there
    are any types with 2-byte alignment.

    Yes, that's my interpretation.

    N2293 mentions this, but it probably bears repeating here: the
    utility of forcing `malloc` to align small allocations to e.g.,
    `alignof(max_align_t)` seems mostly restricted to having some
    hope of overloading the low bits of the returned address for
    some program-specific purpose, which is how this came up in the
    first place in this thread.

    It hadn't occurred to me that that might be the reason. I assumed
    it was just because strong alignment for small allocations is not
    useful and should not be required. Allocating single bytes for
    malloc(1) calls could have real advantages for some programs, and
    the standard shouldn't prohibit that.

    N2293 says that existing implementations differed; Goldblatt
    gives a fairly concise list of major implementations and which
    does what.

    David Goldblatt is one of the maintainers of `jemalloc`, and
    probably cares about the space efficiency of small allocations;
    I suspect that motivated researching the differing
    interpretations and became the real motivation.

    It came up in this thread specifically because of discussions
    around stealing the low bits from the integer representation of
    a pointer.

    As I recall, there was a discussion about reusing low-order bits of
    pointers, and the fact that malloc(1) no longer has to be strongly
    aligned became relevant to that. It doesn't imply that that was
    the reason.

    See above. I suspect Goldblatt wanted to reconcile the
    different interpretations so that the `jemalloc` behavior could
    be considering conformant with the standard.

    There might be an ambiguity in the English grammar of the C23
    statement about the alignment of malloc()ed pointers, but I don't
    *think* we've seen a case where the two (or more?) different
    interpretations actually lead to different semantics.

    I suppose it depends on which semantics you mean. There are
    definitely implementations that the "weak-alignment" reading
    and others that take the "strong-alignment" reading. **On those
    latter implementations**, assuming `malloc` of some small size
    is successful, you can almost certainly cast the resulting
    pointer to `uintptr_t` and use the low bits of the result for
    stuff, whereas on the former implementations, you can't, unless
    you know that the fundamental alignment requirement for the
    size you request is sufficiently large so that the low bits will
    be clear; for `malloc(1)` it basically won't be.

    As has already been mentioned, the
    standard makes no guarnatee about the integer representation of
    addresses, so this is already somewhat fraught, but on many
    platforms we may assume that as a practical matter, for a
    pointer to a suitably aligned address, those bits will be clear
    as a practical matter; I suppose if one wants that for a small
    allocation, use `aligned_alloc`. Caveat emptor.

    Yes, if you need aligned pointers, aligned_alloc() is the way to
    get them. aligned_alloc(8, 1) gives you an 8-byte aligned pointer
    to a 1-byte object; if that's useful, it's because you're doing
    something weird. And if you need to fiddle with the low-order
    bits of aligned pointers, you could run into the problems we've
    already discussed.

    Yup. Agreed on all counts. There _are_ some valid uses for
    stealing low bits, but they're pretty esoteric; for instance,
    this: https://dl.acm.org/doi/10.1145/3731569.3764807

    But if someone is dabbling in those dark magicks, they'd better
    know just exactly what they're doing.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Thu Oct 15 21:34:29 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) writes:
    [Snip stuff I agree with]

    Right. The only reason I wanted to write it that way was to
    drive that point home; in the "strong-alignment" reading, for
    small allocations, the alignment of the address the pointer
    returned from `malloc` refers to is totally decoupled from the
    requested size: `sizeof(char[3])` is three, which reflects the
    fundamental alignment requirement of `char[3]` being 1; because
    `malloc` doesn't know whether the resulting allocation will be
    used to store something with a fundamental alignment criteria of
    two or not (assuming such a type exists in the implementation),
    it must return a pointer to an address that is at least aligned
    to an even boundary. In any event, whether `malloc` must return
    something with stronger alignment, or merely can at its own
    convenience, is the interesting high order bit here.

    Just to avoid any misunderstanding, you refer to "the fundamental
    alignment requirement of `char[3]` being 1".

    The phrase "fundamental alignment requirement" might be misleading.
    It could imply to someone unfamiliar with the terminology that
    char[3] could have a fundamental alignment requirement and a
    differing non-fundamental alignment requirement. (I had thought
    the term "fundamental alignment" was new in C23, but it was added
    in C11.)

    It's possible that I'm the only one who thought this might be
    confusing. If so, ignore this.

    To over-explain: Each object type has an alignment, which is an integer
    value.

    A *fundamental alignment* is a valid alignment less than or equal to
    alignof(max_align_t).

    An *extended alignment* is represented by an alignment greater than
    alignof(max_align_t).

    As far as the language is concerned `char[3]` has an alignment of
    1, and that alignment happens to be a fundamental alignment. (A
    compiler might allocate standalone `char[3]` objects at a stricter
    alignment, but the standard doesn't address that; compilers can
    over-align anything they like.)

    Which might be part of the confusion about the malloc() wording in C23.

    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.

    I read the phrase "with a fundamental alignment requirement" as a
    predicate. A type has a fundamental alignment requirement or it
    doesn't.

    The alternative reading is "with a fundamental alignment requirement
    (and size) less than or equal to the size requested", which says that
    the alignment requirement (a) is fundamental *and* (b) is less than or
    equal to the size requested. I think that's a rather strained
    interpretation -- and I can't think of any cases where the distinction
    between the two readings of the C23 wording could be significant, given
    that an object type can't have an alignment bigger than its size.

    [Snip more stuff I agree with]
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Fri Oct 16 11:27:47 2026
    From Newsgroup: comp.lang.c

    In article <11as9gl$2vtou$[email protected]>,
    Keith Thompson <[email protected]> wrote: >[email protected] (Dan Cross) writes:
    [Snip stuff I agree with]

    Right. The only reason I wanted to write it that way was to
    drive that point home; in the "strong-alignment" reading, for
    small allocations, the alignment of the address the pointer
    returned from `malloc` refers to is totally decoupled from the
    requested size: `sizeof(char[3])` is three, which reflects the
    fundamental alignment requirement of `char[3]` being 1; because
    `malloc` doesn't know whether the resulting allocation will be
    used to store something with a fundamental alignment criteria of
    two or not (assuming such a type exists in the implementation),
    it must return a pointer to an address that is at least aligned
    to an even boundary. In any event, whether `malloc` must return
    something with stronger alignment, or merely can at its own
    convenience, is the interesting high order bit here.

    Just to avoid any misunderstanding, you refer to "the fundamental
    alignment requirement of `char[3]` being 1".

    What I'm trying to say (that I think you said better), is that
    the alignment requirement of `char` is fundamental, and that the
    aggregate type (the array, `char[3]`) has the same alignment
    requirement. In this case, the that is `1`.

    The phrase "fundamental alignment requirement" might be misleading.
    It could imply to someone unfamiliar with the terminology that
    char[3] could have a fundamental alignment requirement and a
    differing non-fundamental alignment requirement. (I had thought
    the term "fundamental alignment" was new in C23, but it was added
    in C11.)

    It's possible that I'm the only one who thought this might be
    confusing. If so, ignore this.

    Hmm, I don't know that anyone else thought that, but I cannot
    say that they did not, and it was useful to make the point.

    To over-explain: Each object type has an alignment, which is an integer >value.

    A *fundamental alignment* is a valid alignment less than or equal to
    alignof(max_align_t).

    An *extended alignment* is represented by an alignment greater than
    alignof(max_align_t).

    As far as the language is concerned `char[3]` has an alignment of
    1, and that alignment happens to be a fundamental alignment. (A
    compiler might allocate standalone `char[3]` objects at a stricter
    alignment, but the standard doesn't address that; compilers can
    over-align anything they like.)

    Yes, and succinctly put.

    A couple of minor quibbles: it bears mentioning that valid
    alignments are positive powers of two. Also, a point you had
    aluded to earlier: for e.g., `malloc(sizeof(char[3]))`, by the
    time the size argument gets to `malloc` the type information
    (including the alignment requirement) has been lost; malloc just
    sees `3`. For an implementation where there exists types with
    an alignment requirement of two and `sizeof(max_align_t)>=2`,
    such an alignment requirement is also fundamental. `malloc`
    cannot know that its return value won't be used to hold an
    object of such type, so it must return a pointer to an address
    that is aligned on an even boundary.

    Which might be part of the confusion about the malloc() wording in C23.

    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.

    I read the phrase "with a fundamental alignment requirement" as a
    predicate. A type has a fundamental alignment requirement or it
    doesn't.

    The alternative reading is "with a fundamental alignment requirement
    (and size) less than or equal to the size requested", which says that
    the alignment requirement (a) is fundamental *and* (b) is less than or
    equal to the size requested. I think that's a rather strained
    interpretation -- and I can't think of any cases where the distinction >between the two readings of the C23 wording could be significant, given
    that an object type can't have an alignment bigger than its size.

    Yes, and I do see what you mean: it's a distinction without a
    difference due to other constraints imposed on types generally.

    Specifically (and speaking somewhat loosely), if a type has an
    alignment requirement that is fundamental, then it is <= the
    type's size; no such type may have alignment>size. Therefore,
    whether or not the "size" condition is to applied to the
    "fundamental alignment requirement" phrase in the C23 sentence,
    the result is the same.

    This is in contrast to the C17 phrasing, which was read by many
    good judges as requiring alignment to `>=alignof(max_align_t)`
    regardless of size.

    [Snip more stuff I agree with]

    I think we're all agreed about the following: a conforming
    implementation may choose for a successful call to `malloc` et
    al to always return an "over-aligned" pointer to an address
    aligned to a boundary that is >= `alignof(max_align_t)`, but
    is not required to do so.

    If you want to steal the low bits of an address for something,
    be careful: there be dragons.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Fri Oct 16 11:27:40 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) writes:
    [...]
    A couple of minor quibbles: it bears mentioning that valid
    alignments are positive powers of two.

    And that wasn't explicitly stated until C11. C99 talks about
    alignment, but is a bit vagee about it. A hypothetical conforming
    C99 implementation might have had addresses organized in powers of
    3, for example. I doubt that any such implementation ever existed.

    Even in C23, the definition of "alignment" is vague:

    alignment

    requirement that objects of a particular type be located on
    storage boundaries with addresses that are particular multiples
    of a byte (3.7) address

    The meaning is obvious given a typical memory model, but I don't
    know how you'd define "multiples of a byte address" in standardese.
    I'm not even sure it's correctly stated.

    In practice, I don't think this vagueness has been a problem;
    everybody knows what it's *supposed* to mean.
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kaz Kylheku@[email protected] to comp.lang.c on Fri Oct 16 20:26:12 2026
    From Newsgroup: comp.lang.c

    On 2026-10-16, Keith Thompson <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:
    On 2026-10-15, Keith Thompson <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote: >>>>>[email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [snip]
    So "a fundamental alignment requirement" and "size less than or equal >>>>>>>to the size requested" are parallel requirements, both applying to >>>>>>>the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful >>>>>malloc(1) returns a value that can be assigned to a pointer of type >>>>>char*, which points to the object type "char", which does have (a >>>>>fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I suspect that this should be read as "any type of object which can
    fit into the requested size".

    I don't see any other way to interpret that.

    It can be interpreted a a defect.

    Sure. My point is that I see no ambiguity in the C17 wording. A
    non-null pointer returned by malloc() must be "suitably aligned".

    Yes, it's a defect. What it says is clear. What it says is also
    logically inconsistent.

    One problem is that "any type" if taken literally means all derived
    types, not just basic types.

    An example of "any type" is, say, struct tm. A program can declare
    much larger, more complicated struct types.

    The requirements, if interpreted literally, are saying that a successful pointer out of malloc(1) must be suitably aligned to point to a struct
    tm /and/ may be then used to access such an object (i.e a struct tm)
    in the space allocated, and the same for any conceivable struct type
    that fits into the implementation limits for conformance.

    It is not reasonable for malloc requests for a small size to be padded
    to accomodate that.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @[email protected]
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Fri Oct 16 15:27:05 2026
    From Newsgroup: comp.lang.c

    Kaz Kylheku <[email protected]> writes:
    On 2026-10-16, Keith Thompson <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:
    On 2026-10-15, Keith Thompson <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote: >>>>>>[email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [snip]
    So "a fundamental alignment requirement" and "size less than or equal >>>>>>>>to the size requested" are parallel requirements, both applying to >>>>>>>>the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is >>>>>>> that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful >>>>>>malloc(1) returns a value that can be assigned to a pointer of type >>>>>>char*, which points to the object type "char", which does have (a >>>>>>fundamental alignment requirement) and (a size less than or equal >>>>>>to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I suspect that this should be read as "any type of object which can
    fit into the requested size".

    I don't see any other way to interpret that.

    It can be interpreted a a defect.

    Sure. My point is that I see no ambiguity in the C17 wording. A
    non-null pointer returned by malloc() must be "suitably aligned".

    Yes, it's a defect. What it says is clear. What it says is also
    logically inconsistent.

    One problem is that "any type" if taken literally means all derived
    types, not just basic types.

    An example of "any type" is, say, struct tm. A program can declare
    much larger, more complicated struct types.

    The requirements, if interpreted literally, are saying that a successful pointer out of malloc(1) must be suitably aligned to point to a struct
    tm /and/ may be then used to access such an object (i.e a struct tm)
    in the space allocated, and the same for any conceivable struct type
    that fits into the implementation limits for conformance.

    It is not reasonable for malloc requests for a small size to be padded
    to accomodate that.

    I don't see that that's more serious than the defect that's already
    been acknowledged.

    Clearly a pointer returned by malloc(1) can't be used to point either
    to a long long int or to a struct HUGE. The C17 wording specifies
    only the alignment of the returned pointer. It's poorly worded,
    but I don't think anyone has interpreted it to mean that malloc(1)
    must allocate more than one byte. If alignof (max_align_t) is 16,
    for example, malloc(1) could return a 16-byte-aligned pointer to
    one byte of allocated space. (The following 15 bytes might be
    used for metadata.) C23 relaxes the alignment requirement for
    small allocations.

    C17 guarantees that
    long double *p = malloc(1);
    has defined behavior. C23 does not.

    IMHO the intent of the C17 wording is clear, even though it's fairly
    obviously wrong.
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@[email protected] to comp.lang.c on Sat Oct 17 08:21:52 2026
    From Newsgroup: comp.lang.c

    Keith Thompson <[email protected]> writes:

    [email protected] (Dan Cross) writes:

    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote:

    [email protected] (Dan Cross) writes:

    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:

    [snip]
    So "a fundamental alignment requirement" and "size less than or
    equal to the size requested" are parallel requirements, both
    applying to the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful
    malloc(1) returns a value that can be assigned to a pointer of type
    char*, which points to the object type "char", which does have (a
    fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I don't see any other way to interpret that. It requires that
    a pointer returned by a successful malloc() must *always* be
    suitably aligned. A pointer returned by malloc(1) must be aligned
    so that it can point to a long long, for example.

    It overlooks the fact that it can't point to a long long, because
    there isn't room for it. [...]

    Let's see if we can get this discussion back on the rails.

    Put yourself back in 1999. Forget about anything from 2000 onwards.
    There is just one relevant sentence in the C99 standard:

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

    The original C standard says the same thing except it says "... is
    explicitly freed or reallocated" rather than deallocated.

    The intended meaning here is straightforward. A pointer returned from
    malloc() may be assigned to any pointer no matter what type it points
    to. Non-null pointers may be used to access a suitably sized array.
    If the size requested is smaller than the pointed-to type, using the
    pointer follows the rule for zero-length flexible array members: it
    acts like an array of size 1 except that trying to access the element
    or advance to the next element is undefined behavior. In other words
    it's okay to use the pointer as a pointer but it can't be incremented
    or dereferenced.

    Now go forward to 2011. We want the same behavior for malloc() as in
    C99, only now we have to consider types with overly large alignments.
    For obvious reasons we don't want to force malloc() to make worst-case assumptions about what the alignment needs will be. A new function aligned_alloc() is added, with an extra parameter to specify what
    alignment is needed, to take care of types with overly large alignment
    needs. The general rule for what pointer types are covered is changed
    to admit the same kinds as when there were no overly large alignments;
    that change is put in place by adding the clause "with a fundamental
    alignment requirement" to the general description of memory management functions. The description of aligned_alloc() says specifically that
    it allocates an object having the given alignment value (and so the
    returned pointer value presumably may be used as such). What happens
    with requests whose size is smaller than what the assigned-to pointer
    type implies is the same as before, following the FAM rule; that rule
    applies to aligned_alloc() the same as is does for malloc().

    Note by the way that aligned_alloc() may be used to ask for a space
    whose alignment is smaller than normal as well as any overly large
    alignment. So malloc() should be used for generic alignments, and aligned_alloc() should be used any too-small or too-large alignment.

    I don't have a copy of the C17 standard handy, but I expect it says
    the same thing about malloc() and friends as the C11 standard does.

    Regarding the discussion in N2293, I'm sorry to say that either the
    author is not too bright or he just didn't have his Wheaties that
    morning. He reads the words but he doesn't understand the meaning.
    A fair guess is that he made two common mistakes: one, he didn't
    consider the historical context, and two, he looked at passages in
    isolation without taking into account a broader context. The Standard
    is not meant to be read like a legal document. All the parts are
    expected to fit together, and in deciding what a particular passage
    means one needs to consider other surrounding and related passages and
    how they might interact. As for the arguments about how various implementations behave, the reasoning is backwards. It's the tail
    wagging the dog. We don't judge what the C standard means based on
    what implementations do; rather we decide what the C standard is
    meant to say and then assess whether a proposed implementation is
    conforming. The possibility of potential performance benefits does
    not excuse changing the behavior required under previous C standards.
    If someone thinks that's important they should use aligned_alloc();
    otherwise they should just use malloc() and know that it will behave
    the way it always has.

    Bottom line: the discussion in N2293 is misguided. A technical
    corrigendum should be issued, putting back the language used in C11
    and C17, and adding a footnote explaining that aligned_alloc() can be
    used for any allocation whose alignment is other than the default
    alignment provided by malloc().
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@[email protected] to comp.lang.c on Sat Oct 17 10:48:47 2026
    From Newsgroup: comp.lang.c

    Tim Rentsch <[email protected]> writes:
    Keith Thompson <[email protected]> writes:
    [email protected] (Dan Cross) writes:
    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:

    [snip]
    So "a fundamental alignment requirement" and "size less than or
    equal to the size requested" are parallel requirements, both
    applying to the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful
    malloc(1) returns a value that can be assigned to a pointer of type
    char*, which points to the object type "char", which does have (a
    fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I don't see any other way to interpret that. It requires that
    a pointer returned by a successful malloc() must *always* be
    suitably aligned. A pointer returned by malloc(1) must be aligned
    so that it can point to a long long, for example.

    It overlooks the fact that it can't point to a long long, because
    there isn't room for it. [...]

    Let's see if we can get this discussion back on the rails.

    Put yourself back in 1999. Forget about anything from 2000 onwards.
    There is just one relevant sentence in the C99 standard:

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

    The original C standard says the same thing except it says "... is
    explicitly freed or reallocated" rather than deallocated.

    Yes. That wording is flawed. It implies that the pointer returned
    by a successful malloc(1) may be "used to access" a long double
    object in the space allocated.

    If we set aside the "used to access" clause, it's still clear that the
    returned pointer is suitably aligned for any object. So malloc(1) must
    return, for example, a 16-byte aligned pointer, even though no 16-byte
    object can be stored in the allocated space.

    The intended meaning here is straightforward. A pointer returned from malloc() may be assigned to any pointer no matter what type it points
    to. Non-null pointers may be used to access a suitably sized array.
    If the size requested is smaller than the pointed-to type, using the
    pointer follows the rule for zero-length flexible array members: it
    acts like an array of size 1 except that trying to access the element
    or advance to the next element is undefined behavior. In other words
    it's okay to use the pointer as a pointer but it can't be incremented
    or dereferenced.

    I agree that the intended meaning is straightforward, but the
    words do not correctly express that intent. To understand the
    intent, you have to set aside the incorrect assertion that a pointer
    returned by malloc() can be used to access an object bigger than
    the requested allocation.

    On the other hand, here's the C17 wording:

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

    I suppose that could be read so it applies only if such an object *can*
    exist in the space allocated. But I still find it ambiguous at best.

    Now go forward to 2011. We want the same behavior for malloc() as in
    C99, only now we have to consider types with overly large alignments.
    For obvious reasons we don't want to force malloc() to make worst-case assumptions about what the alignment needs will be. A new function aligned_alloc() is added, with an extra parameter to specify what
    alignment is needed, to take care of types with overly large alignment
    needs. The general rule for what pointer types are covered is changed
    to admit the same kinds as when there were no overly large alignments;
    that change is put in place by adding the clause "with a fundamental alignment requirement" to the general description of memory management functions. The description of aligned_alloc() says specifically that
    it allocates an object having the given alignment value (and so the
    returned pointer value presumably may be used as such). What happens
    with requests whose size is smaller than what the assigned-to pointer
    type implies is the same as before, following the FAM rule; that rule applies to aligned_alloc() the same as is does for malloc().

    "FAM"?

    Note by the way that aligned_alloc() may be used to ask for a space
    whose alignment is smaller than normal as well as any overly large
    alignment. So malloc() should be used for generic alignments, and aligned_alloc() should be used any too-small or too-large alignment.

    Suppose alignof (max_align_t) == 16. aligned_malloc(1, 1024) must still
    return a 16-byte aligned pointer. This is stated explicitly in a
    footnote in C23 (N3220).

    I don't have a copy of the C17 standard handy, but I expect it says
    the same thing about malloc() and friends as the C11 standard does.

    Regarding the discussion in N2293, I'm sorry to say that either the
    author is not too bright or he just didn't have his Wheaties that
    morning. He reads the words but he doesn't understand the meaning.

    He reads the words and recognizes the flaw.

    I personally don't think the "weak-alignment" reading described in
    N2293 is valid, but if some implementers have actually interpreted
    it that way, it's worth fixing the wording to clear it up.

    A fair guess is that he made two common mistakes: one, he didn't
    consider the historical context, and two, he looked at passages in
    isolation without taking into account a broader context. The Standard
    is not meant to be read like a legal document. All the parts are
    expected to fit together, and in deciding what a particular passage
    means one needs to consider other surrounding and related passages and
    how they might interact.

    If some wording in the standard requires historical context to
    understand it, that wording is flawed.

    As for the arguments about how various implementations behave, the reasoning is backwards. It's the tail
    wagging the dog. We don't judge what the C standard means based on
    what implementations do; rather we decide what the C standard is
    meant to say and then assess whether a proposed implementation is
    conforming. The possibility of potential performance benefits does
    not excuse changing the behavior required under previous C standards.
    If someone thinks that's important they should use aligned_alloc();
    otherwise they should just use malloc() and know that it will behave
    the way it always has.

    Bottom line: the discussion in N2293 is misguided. A technical
    corrigendum should be issued, putting back the language used in C11
    and C17, and adding a footnote explaining that aligned_alloc() can be
    used for any allocation whose alignment is other than the default
    alignment provided by malloc().

    I don't want to restore the flawed wording from C17 and earlier.

    Are you saying that malloc(1) *should* be required to return a fully
    aligned pointer? Since C17 and earlier did require that, perhaps there
    should have been more discussion about relaxing it by allowing malloc(1)
    to return an "odd" pointer. I don't think that was a bad change, but
    it could in principle break some existing code.

    I see two separate issues.

    1. The wording in C17 and earlier can be read to require the ability to
    access a large object in a small allocated space. That's a wording
    issue.

    2. C17 and earlier require small allocations to be strongly aligned.
    C23 drops that requirement.

    If there's a good reason to continue to require the result of
    malloc(1) to be strongly aligned, I wouldn't object to it - but
    C23 already dropped that requirement, so imposing it again in C2y
    could be problematic.
    --
    Keith Thompson (The_Other_Keith) [email protected]
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Sat Oct 17 18:10:00 2026
    From Newsgroup: comp.lang.c

    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:
    [snip]
    Regarding the discussion in N2293, I'm sorry to say that either the
    author is not too bright or he just didn't have his Wheaties that
    morning. He reads the words but he doesn't understand the meaning.
    A fair guess is that he made two common mistakes: one, he didn't
    consider the historical context, and two, he looked at passages in
    isolation without taking into account a broader context. The Standard
    is not meant to be read like a legal document. All the parts are
    expected to fit together, and in deciding what a particular passage
    means one needs to consider other surrounding and related passages and
    how they might interact. As for the arguments about how various >implementations behave, the reasoning is backwards. It's the tail
    wagging the dog. We don't judge what the C standard means based on
    what implementations do; rather we decide what the C standard is
    meant to say and then assess whether a proposed implementation is
    conforming. The possibility of potential performance benefits does
    not excuse changing the behavior required under previous C standards.
    If someone thinks that's important they should use aligned_alloc();
    otherwise they should just use malloc() and know that it will behave
    the way it always has.

    Did you actually _read_ N2293?

    Bottom line: the discussion in N2293 is misguided. A technical
    corrigendum should be issued, putting back the language used in C11
    and C17, and adding a footnote explaining that aligned_alloc() can be
    used for any allocation whose alignment is other than the default
    alignment provided by malloc().

    No.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@[email protected] to comp.lang.c on Sat Oct 17 20:28:52 2026
    From Newsgroup: comp.lang.c

    On 10/15/2026 2:07 PM, Chris M. Thomasson wrote:
    On 10/14/2026 10:47 PM, Keith Thompson wrote:
    Tim Rentsch <[email protected]> writes:
    Keith Thompson <[email protected]> writes:
    [email protected] (Dan Cross) writes:
    In article <11al0l0$eefv$[email protected]>,
    James Kuyper  <[email protected]> wrote:
    On 13/10/2026 10:38, chine.bleu wrote:
    As a precaution check the return

    address of malloc

      p = malloc(m);
      if ((uintptr_t)p)&1!=0) abort();

    Just to be pedantic, the standard doesn't guarantee that the
    expression you use will test what you want to test.  Since there
    is a guarantee that malloc() returns a universally aligned memory, >>>>>> I think it's actually more likely that the test will fail than
    that malloc() will fail, though both probabilities are quite
    negligible, so it's hard to be sure.

    The syntax and precedence errors aside, the test is clearly
    meant to ensure that the low bit in the return from `malloc` is
    clear, so that it can be repurposed for other things.  It should
    likely be:

        p = malloc(m);
        if (p == NULL || ((uintptr_t)p & 1) != 0) abort();

    I'm not sure the NULL test adds anything useful.  The low-order bits
    of a null pointer are very likely to be 0 on any real-world system.

    Surely we would like to protect against a null pointer being used
    to access a non-existent object.

    Also, the test only looks at the low-order bits of the result of
    the current malloc() call.  If you have an exotic system where
    an aligned pointer can have a low-order bit set to 1, it might
    *sometimes* be set to 0.

    As long as the bit in question /is/ zero it doesn't matter if it
    might have been one.  The bit will always be cleared before using
    the pointer as a pointer, so the pointer value in use will match
    the pointer value originally returned.

    Thus we might write a safe allocation function as follows:

        void *
        safe_new( size_t n ){
           void *p = 0;

           while(  !p || (uintptr_t)p %2 != 0  )  free( p ),  p = >>> malloc( n++ );

           return  p;
        }

    Of course it might not succeed in some cases, but it will be
    quite obvious if it doesn't.  :)

    I might be missing the point of "chine.bleu"'s suggestion.  I was
    assuming that the point was to check whether it's possible *in
    general* to play with the low-order bits of malloc'ed pointers.

    On most systems, you can probably make that assumption based on
    what you know about the system's memory model.  But it can't be
    checked reliably at run time.

    If I wanted my program to play with low-order bits of pointers, and
    refuse to run if the system doesn't support that, I might write a
    program that does a *lot* of malloc() calls and invoke it during
    configuration, refusing to proceed with the build if it reports that
    malloc() can return an "odd" pointer.  Aborting during execution
    feels sloppy.


    Playing the the low order bits is common in some exotic lock-free algorihtms. BUT, its much better to over allocate using say malloc, then manually align the pointer it returns. Then we can say that pointer is aligned on say a 8192 boundary. We know it size if 8192. But we had to
    ask malloc for more memory to make sure we can align and have the right
    size data block. THEN! We can play with the aligned address and store whatever data we want. Just make sure to strip it out before we use the aligned and packed pointer.

    If somebody uses packed pointers, be sure to have a strip function, say
    get back to the raw pointer. Or It might start playing the following music:

    Lisa Lisa & Cult Jam - Head To Toe https://youtu.be/51Iq8JmmfxY?list=RDMM51Iq8JmmfxY
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From antispam@[email protected] (Waldek Hebisch) to comp.lang.c on Sun Oct 18 08:42:06 2026
    From Newsgroup: comp.lang.c

    Kaz Kylheku <[email protected]> wrote:
    On 2026-10-15, Keith Thompson <[email protected]> wrote:
    [email protected] (Dan Cross) writes:
    In article <11ap0fo$1t8gq$[email protected]>,
    Keith Thompson <[email protected]> wrote: >>>>[email protected] (Dan Cross) writes:
    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    [snip]
    So "a fundamental alignment requirement" and "size less than or equal >>>>>>to the size requested" are parallel requirements, both applying to >>>>>>the type of the pointed-to object.

    That is one of the two reasonable interpretations. The other is
    that the requirements placed on size and fundamental alignment
    of the pointer type are cumulative and dependent on the size of
    the allocation. If so, then nothing in the C standard prevents
    an implementation from from returning a pointer aligned to
    `alignof(char)` for `malloc(1)`.

    I do see the parsing ambiguity now.

    I believe that's consistent with the way I parsed it. A successful >>>>malloc(1) returns a value that can be assigned to a pointer of type >>>>char*, which points to the object type "char", which does have (a >>>>fundamental alignment requirement) and (a size less than or equal
    to the size requested).

    Right, but I think it's a bit deeper than that.

    Somewhat informally, the C17 wording could be read as requiring
    that the pointer returned from `malloc` must be suitable for
    assignment to any pointer to a type with `alignof(max_align_t)`
    regardless of the requested size, which might be smaller. N2293
    calls this the, '"strong-alignment" reading.'

    The C17 wording is:

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

    I suspect that this should be read as "any type of object which can
    fit into the requested size".

    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.
    --
    Waldek Hebisch
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@[email protected] to comp.lang.c on Sun Oct 18 13:11:27 2026
    From Newsgroup: comp.lang.c

    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. [...]

    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?

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From antispam@[email protected] (Waldek Hebisch) to comp.lang.c on Sun Oct 18 15:12:01 2026
    From Newsgroup: comp.lang.c

    Janis Papanagnou <[email protected]> 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. [...]

    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. 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. 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.
    --
    Waldek Hebisch
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kaz Kylheku@[email protected] to comp.lang.c on Sun Oct 18 19:54:23 2026
    From Newsgroup: comp.lang.c

    On 2026-10-18, Janis Papanagnou <[email protected]> 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. [...]

    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.

    Sometimes you don't; you store one kind thing in the union and it stays
    that way for the life of the object. Perhaps there is a type field
    which indicaes the type of the object.

    If the smallest kind of object is 32 bytes, and the largest 512,
    you don't want to allocate 512 for the 32 variant.

    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.

    With the union type you can write the program with minimal pointer
    casts.

    If you know that "pobj" is a pointer to a string, as confirmed
    by pboj->u.base.type == OBJ_STR, you know you can access pboj->u.str.len
    (but not pboj->u.num.value).

    With transparent unions you can lose the "u." part. Can be (and has
    been) done with macros also.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @[email protected]
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@[email protected] to comp.lang.c on Wed Oct 21 02:34:39 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) writes:

    In article <11amvh2$14pou$[email protected]>,
    Keith Thompson <[email protected]> wrote:

    [email protected] (Dan Cross) writes:

    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:

    [email protected] (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.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@[email protected] to comp.lang.c on Wed Oct 21 02:41:12 2026
    From Newsgroup: comp.lang.c

    [email protected] (Dan Cross) writes:

    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:

    [email protected] (Dan Cross) writes:

    In article <11al4mu$fqtl$[email protected]>,
    Keith Thompson <[email protected]> wrote:

    David Brown <[email protected]> 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.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Wed Oct 21 11:22:16 2026
    From Newsgroup: comp.lang.c

    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:
    [email protected] (Dan Cross) writes:

    In article <[email protected]>,
    Tim Rentsch <[email protected]> wrote:

    [email protected] (Dan Cross) writes:

    In article <11al4mu$fqtl$[email protected]>,
    Keith Thompson <[email protected]> wrote:

    David Brown <[email protected]> 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.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kaz Kylheku@[email protected] to comp.lang.c on Wed Oct 21 23:55:28 2026
    From Newsgroup: comp.lang.c

    On 2026-10-13, Dan Cross <[email protected]> wrote:
    In article <11al4mu$fqtl$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    David Brown <[email protected]> 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: @[email protected]
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@[email protected] (Dan Cross) to comp.lang.c on Thu Oct 22 00:43:13 2026
    From Newsgroup: comp.lang.c

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

    In article <[email protected]>,
    Kaz Kylheku <[email protected]> wrote:
    On 2026-10-13, Dan Cross <[email protected]> wrote:
    In article <11al4mu$fqtl$[email protected]>,
    Keith Thompson <[email protected]> wrote:
    David Brown <[email protected]> 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.

    --- Synchronet 3.22a-Linux NewsLink 1.2