[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.
[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.
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.
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();
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.
[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?
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?
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.
Btw, this kind of ambiguity is the primary objection I have to
attempts at using informal English prose to precisely define a
programming language.
[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.
[...]
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?
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.
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.
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.
[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 <[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. :)
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.
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.
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).
[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.
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.
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.
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.
[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.
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.
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.
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.
[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.
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.
[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]
A couple of minor quibbles: it bears mentioning that valid
alignments are positive powers of two.
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.
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.
[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. [...]
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().
[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.
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().
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.
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. [...]
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?
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.
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", [...]
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.
[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.
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 heldMemory is cheap.
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
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)`.
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."
x[7] may be trampling on internal malloc bits.The allocator could allocate 7 byte (or smaller) objects in 8 byte
Playing games with pointer values can be useful for implementing
things like, e.g, Harris's lock-free linked list.
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 heldMemory is cheap.
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
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.
| Sysop: | DaiTengu |
|---|---|
| Location: | Appleton, WI |
| Users: | 1,142 |
| Nodes: | 10 (0 / 10) |
| Uptime: | 135:30:11 |
| Calls: | 14,707 |
| Calls today: | 1 |
| Files: | 186,498 |
| D/L today: |
1,121 files (344M bytes) |
| Messages: | 2,552,863 |