The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
On 8/20/2026 4:14 PM, Lawrence D?Oliveiro wrote:
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
static int g_a = 0;
int main()
{
˙˙˙ int a = g_a;
˙˙˙ // oh my... a can be anything wrt the std?
˙˙˙ return 0;
}
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that's different) instead?
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
On 21/08/2026 00:14, Lawrence D?Oliveiro wrote:
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
The language would need to ensure that whatever bit-pattern is needed
for NULL, would be written.
This means for example that:
static void* A[100000000];
can't occupy the special .bss segment in an executable that takes
almost no space; it would need 0.4 to 0.8GB of init data.
In general it would be a massive PITA, if you have for example arrays
of nested structs which have mixed pointer/non-pointer members.
Imagine allocating such an array like this:
p = malloc(N*sizeof(T));
and you wanted to set all elements to zeros and NULLs. You couldn't
for example do:
memset(p, 0, N*sizeof(T));
as the pointers would not be NULL.
On 21/08/2026 00:14, Lawrence D?Oliveiro wrote:
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero....> This means for example that:
static void* A[100000000];
can't occupy the special .bss segment in an executable that takes
almost no space; it would need 0.4 to 0.8GB of init data.
In general it would be a massive PITA, if you have for example arrays
of nested structs which have mixed pointer/non-pointer members.
Imagine allocating such an array like this:
p = malloc(N*sizeof(T));
and you wanted to set all elements to zeros and NULLs. You couldn't
for example do:
memset(p, 0, N*sizeof(T));
as the pointers would not be NULL.
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
On 21/08/2026 00:14, Lawrence D?Oliveiro wrote:
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
The language would need to ensure that whatever bit-pattern is needed
for NULL, would be written. This means for example that:
static void* A[100000000];
can't occupy the special .bss segment in an executable that takes almost
no space; it would need 0.4 to 0.8GB of init data.
In general it would be a massive PITA, if you have for example arrays of nested structs which have mixed pointer/non-pointer members.
Imagine allocating such an array like this:
˙˙˙ p = malloc(N*sizeof(T));
and you wanted to set all elements to zeros and NULLs. You couldn't for example do:
˙˙˙ memset(p, 0, N*sizeof(T));
as the pointers would not be NULL.
For those rare implementations which choose such a bit pattern, that
pattern is forced on them by the design of the hardware the code
will be running on.
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
On 21/08/2026 01:14, Lawrence D?Oliveiro wrote:
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
The standard does not say that static variables are initialised to zero.
˙It gives the rules quite clearly.
The standard says :
"""
If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static or thread storage duration is not initialized explicitly, then:
? if it has pointer type, it is initialized to a null pointer;
? if it has arithmetic type, it is initialized to (positive or unsigned) zero;
? if it is an aggregate, every member is initialized (recursively)
according to these rules, and any padding is initialized to zero bits;
? if it is a union, the first named member is initialize
"""
Initialisation is as though the objects had been assigned "0".˙ If you
have a target where the bit pattern for a null pointer is something
other than zero, that's what you get when pointer is default
initialised.˙ (Similarly, if your target's floating point format uses something other than zero bits for 0 value, you get the 0 value for
default initialised floating point objects.)
Of course this initialisation is vastly more efficient when zero bits
works for everything.
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
On Thu, 20 Aug 2026 23:01:06 -0400, James Kuyper wrote:
For those rare implementations which choose such a bit pattern, that
pattern is forced on them by the design of the hardware the code
will be running on.
There might be some leeway.
For example, on the original VAX architecture, only the bottom 3GiB of
the total 32-bit address space were defined by the architecture to be >mappable through page tables; the top 1GiB was ?reserved? and
unusable.
As part of this, address 0 was also mappable by the hardware. But what
they did with the VAX/VMS ABI, was declare that the bottom page
(addresses 0..511) would always remain unmapped, so they could use
address 0 as an invalid address.
The VAX Unix (BSD) implementation reversed that and mapped a read-only
page of zeros at address 0.
In article <11681np$3s8jk$4@dont-email.me>,
Lawrence D;Oliveiro <ldo@nz.invalid> wrote:
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that's different) instead?
ISO C 1999, 6.7.8:
If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static
storage duration is not initialized explicitly, then:
? if it has pointer type, it is initialized to a null pointer;
? if it has arithmetic type, it is initialized to (positive or
unsigned) zero;
? if it is an aggregate, every member is initialized (recursively)
according to these rules;
? if it is a union, the first named member is initialized
(recursively) according to these rules
On 8/20/2026 4:42 PM, Richard Tobin wrote:
In article <11681np$3s8jk$4@dont-email.me>,
Lawrence D;Oliveiro˙ <ldo@nz.invalid> wrote:
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that's different) instead?
ISO C 1999, 6.7.8:
If an object that has automatic storage duration is not initialized
explicitly, its value is indeterminate. If an object that has static
storage duration is not initialized explicitly, then:
? if it has pointer type, it is initialized to a null pointer;
Oh. I must have missed that one.
so:
static void* g_ptr;
is guaranteed to be nullptr? Just to clarify...
On 21/08/2026 21:12, Chris M. Thomasson wrote:
On 8/20/2026 4:42 PM, Richard Tobin wrote:Yes.
In article <11681np$3s8jk$4@dont-email.me>,Oh. I must have missed that one.
Lawrence D;Oliveiro˙ <ldo@nz.invalid> wrote:
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that's different) instead?
ISO C 1999, 6.7.8:
If an object that has automatic storage duration is not initialized
explicitly, its value is indeterminate. If an object that has static
storage duration is not initialized explicitly, then:
? if it has pointer type, it is initialized to a null pointer;
so:
static void* g_ptr;
is guaranteed to be nullptr? Just to clarify...
I would still recomend memset or bzero. You never know what is
complient or not - better to be safe then sorry.
David Brown <david.brown@hesbynett.no> writes:[...]
On 21/08/2026 21:12, Chris M. Thomasson wrote:
On 8/20/2026 4:42 PM, Richard Tobin wrote:
Oh. I must have missed that one.Yes.
so:
static void* g_ptr;
is guaranteed to be nullptr? Just to clarify...
I would still recomend memset or bzero. You never know what is complient
or not - better to be safe then sorry.
is guaranteed to be nullptr? Just to clarify...
Yes.
I would still recomend memset or bzero. You never know what is complient
or not - better to be safe then sorry.
steve g <Sgonedes1977@gmail.com> writes:
David Brown <david.brown@hesbynett.no> writes:[...]
On 21/08/2026 21:12, Chris M. Thomasson wrote:
On 8/20/2026 4:42 PM, Richard Tobin wrote:
Oh. I must have missed that one.Yes.
so:
static void* g_ptr;
is guaranteed to be nullptr? Just to clarify...
I would still recomend memset or bzero. You never know what is complient
or not - better to be safe then sorry.
Bad idea.
bzero is non-standard. It was never in ISO C, and has been removed
from POSIX.
Calling memset() to set a pointer object to the null pointer fails
if a null pointer is not represented as all-bits-zero. An explicit initialization to 0, NULL, or nullptr (C23 or later) is clearer and
more portable. Implementations that don't use all-bits-zero for null pointers are rare, but so are implementations that don't zero-init uninitialized static objects -- and the former can be conforming.
There are apparently a handful of C implementations for very
small embedded systems that do not zero static objects. If you're
using such an implementation, you're very probably aware of it,
and you certainly should be. Your "You never know" statement above
is incorrect. I suspect that most code written for such systems
doesn't need to be portable.
On 24/08/2026 00:29, Keith Thompson wrote:
steve g <Sgonedes1977@gmail.com> writes:
David Brown <david.brown@hesbynett.no> writes:[...]
On 21/08/2026 21:12, Chris M. Thomasson wrote:
On 8/20/2026 4:42 PM, Richard Tobin wrote:
Oh. I must have missed that one.Yes.
so:
static void* g_ptr;
is guaranteed to be nullptr? Just to clarify...
I would still recomend memset or bzero. You never know what is complient >>> or not - better to be safe then sorry.
Bad idea.
bzero is non-standard.˙ It was never in ISO C, and has been removed
from POSIX.
Calling memset() to set a pointer object to the null pointer fails
if a null pointer is not represented as all-bits-zero.˙ An explicit
initialization to 0, NULL, or nullptr (C23 or later) is clearer and
more portable.˙ Implementations that don't use all-bits-zero for null
pointers are rare, but so are implementations that don't zero-init
uninitialized static objects -- and the former can be conforming.
There are apparently a handful of C implementations for very
small embedded systems that do not zero static objects.˙ If you're
using such an implementation, you're very probably aware of it,
and you certainly should be.˙ Your "You never know" statement above
is incorrect.˙ I suspect that most code written for such systems
doesn't need to be portable.
You probably also know if you are coding for one of the few systems that
do not use all zero bits for null pointers - AFAIK they are basically
museum relics.˙ (Of course there are still a few museum relics in
current use, but you would know it if you were coding for them.)
Still, using memset or bzero to write zeros to data that is initialised
by pre-main startup is worse than useless.˙ It does nothing useful,
takes a bit of extra time, may spoil some optimisations (compilers can sometimes eliminate or simplify file-static variables, but probably not
if they have been the target of memset or bzero), makes a maintenance a
pain (you have to add new calls for each new variable), risks someone
making smart-arse and unwarranted assumptions (such as trying to combine variables in a single memset/bzero call), and can easily confuse readers
who can't see why the code is doing this.
It is true that pretty much every C compiler has its non-conformances,
with some having more than others.˙ But pretty much all (baring the aforementioned embedded toolchains) are going to get something this
basic entirely correct.˙ If you can't be entirely confident of a
toolchain getting this right, you should - if possible - switch toolchains.
(Fortunately in the embedded world, most development is moving to gcc or occasionally clang, with some industries sticking to the "big"
commercial toolchain vendors.˙ The weird and highly non-conforming are
much rarer, and becoming horror stories to scare the new kids.)
Can you safely say something like this ... ?
struct foo
{
char *s;
int n;
char c;
void *data;
};
static const struct foo FOO_INIT;
int main(void)
{
struct foo foo;
foo = FOO_INIT;
...
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Can you safely say something like this ... ?
struct foo
{
char *s;
int n;
char c;
void *data;
};
static const struct foo FOO_INIT;
int main(void)
{
struct foo foo;
foo = FOO_INIT;
...
That's safe for conforming implementations. You can also
use an initializer:
struct foo foo = FOO_INIT;
It solves the problem of zero-initializing a non-static object
(such objects contain garbage if they're not initialized) -- but
it's better done by writing
struct foo foo = { 0 };
(In C23 and later you can omit the 0.)
If you need a zero-initialized struct foo value as an expression,
e.g. for use in an assigment or as an argument, you can use a
compound literal in C99 or later:
foo = (struct foo){ 0 };
Again, you can omit the 0 in C23 or later.
Here you're relying on the implementation to zero unspecified
members, which is required by the standard.
If you're stuck using a non-conforming implementation, you need
to know exactly *how* it's non-conforming, and write whatever
ugly code you need to work around it. Different non-conforming implementations are likely to be non-conforming in surprising ways.
(Ideally your ugly code will also work on conforming implementations,
but that might not be a priority.)
David Brown <david.brown@hesbynett.no> writes:
On 21/08/2026 21:12, Chris M. Thomasson wrote:
On 8/20/2026 4:42 PM, Richard Tobin wrote:Yes.
In article <11681np$3s8jk$4@dont-email.me>,Oh. I must have missed that one.
Lawrence D;Oliveiro˙ <ldo@nz.invalid> wrote:
The spec also says that all static variables are initialized to zero >>>>> at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that's different) instead?
ISO C 1999, 6.7.8:
If an object that has automatic storage duration is not initialized
explicitly, its value is indeterminate. If an object that has static
storage duration is not initialized explicitly, then:
? if it has pointer type, it is initialized to a null pointer;
so:
static void* g_ptr;
is guaranteed to be nullptr? Just to clarify...
I would still recomend memset or bzero. You never know what is complient
or not - better to be safe then sorry.
On 24/08/2026 12:04, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Can you safely say something like this ... ?That's safe for conforming implementations. You can also
struct foo
{
char *s;
int n;
char c;
void *data;
};
static const struct foo FOO_INIT;
int main(void)
{
struct foo foo;
foo = FOO_INIT;
...
use an initializer:
struct foo foo = FOO_INIT;
It solves the problem of zero-initializing a non-static object
(such objects contain garbage if they're not initialized) -- but
it's better done by writing
struct foo foo = { 0 };
(In C23 and later you can omit the 0.)
In what sense do you consider that "better" ? I think it is arguably
neater and less cognitive effort to understand, but did you have
anything else in mind?
As I understand it, there is a distinction between :
struct foo foo;
foo = FOO_INIT; // or foo = (struct foo) { 0 };
and
struct foo foo = FOO_INIT; // or foo = { 0 };
in that the former just copies the value of FOO_INIT while the later guarantees zero bit initialisation for padding bits and bytes.
It's unlikely that an optimising compiler would generate different
code in practice.
David Brown <david.brown@hesbynett.no> writes:
On 24/08/2026 12:04, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Can you safely say something like this ... ?That's safe for conforming implementations. You can also
struct foo
{
char *s;
int n;
char c;
void *data;
};
static const struct foo FOO_INIT;
int main(void)
{
struct foo foo;
foo = FOO_INIT;
...
use an initializer:
struct foo foo = FOO_INIT;
It solves the problem of zero-initializing a non-static object
(such objects contain garbage if they're not initialized) -- but
it's better done by writing
struct foo foo = { 0 };
(In C23 and later you can omit the 0.)
In what sense do you consider that "better" ? I think it is arguably
neater and less cognitive effort to understand, but did you have
anything else in mind?
Not really. With { 0 }, you can see at a glance what it means.
With FOO_INIT, you have to know how FOO_INIT is defined, possibly in
a different source file.
Of course if the initial value is something other than all-zeros, you
can change the way FOO_INIT is defined, and { 0 } wouldn't work.
As I understand it, there is a distinction between :
struct foo foo;
foo = FOO_INIT; // or foo = (struct foo) { 0 };
and
struct foo foo = FOO_INIT; // or foo = { 0 };
in that the former just copies the value of FOO_INIT while the later
guarantees zero bit initialisation for padding bits and bytes.
It's unlikely that an optimising compiler would generate different
code in practice.
I hadn't thought about that (and it's not likely to matter). I don't remember the rules for initializing padding off the top of my head.
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Can you safely say something like this ... ?
struct foo
{
char *s;
int n;
char c;
void *data;
};
static const struct foo FOO_INIT;
int main(void)
{
struct foo foo;
foo = FOO_INIT;
...
That's safe for conforming implementations. You can also
use an initializer:
struct foo foo = FOO_INIT;
It solves the problem of zero-initializing a non-static object
(such objects contain garbage if they're not initialized) -- but
it's better done by writing
struct foo foo = { 0 };
(In C23 and later you can omit the 0.)
If you need a zero-initialized struct foo value as an expression,
e.g. for use in an assigment or as an argument, you can use a
compound literal in C99 or later:
foo = (struct foo){ 0 };
Again, you can omit the 0 in C23 or later.
Here you're relying on the implementation to zero unspecified
members, which is required by the standard.
If you're stuck using a non-conforming implementation, you need
to know exactly *how* it's non-conforming, and write whatever
ugly code you need to work around it. Different non-conforming implementations are likely to be non-conforming in surprising ways.
(Ideally your ugly code will also work on conforming implementations,
but that might not be a priority.)
On 24/08/2026 11:04, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Can you safely say something like this ... ?
struct foo
{
˙˙˙˙ char *s;
˙˙˙˙ int n;
˙˙˙˙ char c;
˙˙˙˙ void *data;
};
static const struct foo FOO_INIT;
int main(void)
{
˙˙˙˙ struct foo foo;
˙˙˙˙ foo = FOO_INIT;
˙˙˙˙ ...
That's safe for conforming implementations.˙ You can also
use an initializer:
˙˙˙˙ struct foo foo = FOO_INIT;
It solves the problem of zero-initializing a non-static object
(such objects contain garbage if they're not initialized) -- but
it's better done by writing
˙˙˙˙ struct foo foo = { 0 };
(In C23 and later you can omit the 0.)
If you need a zero-initialized struct foo value as an expression,
e.g. for use in an assigment or as an argument, you can use a
compound literal in C99 or later:
˙˙˙˙ foo = (struct foo){ 0 };
Again, you can omit the 0 in C23 or later.
Here you're relying on the implementation to zero unspecified
members, which is required by the standard.
If you're stuck using a non-conforming implementation, you need
to know exactly *how* it's non-conforming, and write whatever
ugly code you need to work around it.˙ Different non-conforming
implementations are likely to be non-conforming in surprising ways.
(Ideally your ugly code will also work on conforming implementations,
but that might not be a priority.)
Thanks.
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie: correct
for pointers and floats that are not all-bits-zero?
Oh, and don't mix variable and struct names - no "struct foo foo".
On 25/08/2026 13:15, David Brown wrote:
Oh, and don't mix variable and struct names - no "struct foo foo".
Any reason why?˙ Just a style thing?
It is not a good idea to give two different things the same
identifier.
In article <116k96l$3oj0e$1@dont-email.me>,
David Brown <david.brown@hesbynett.no> wrote:
It is not a good idea to give two different things the same
identifier.
On the other hand it is a bad idea to not use the obvious name for a variable. If I'm dealing with a single variable of type "struct dog",
I'd prefer not to have to come up with another name just to avoid
calling it "dog".
On 25/08/2026 13:15, David Brown wrote:
Oh, and don't mix variable and struct names - no "struct foo foo".
Any reason why? Just a style thing?
On the other hand it is a bad idea to not use the obvious name for a variable. If I'm dealing with a single variable of type "struct
dog", I'd prefer not to have to come up with another name just to
avoid calling it "dog".
In article <116k96l$3oj0e$1@dont-email.me>,
David Brown <david.brown@hesbynett.no> wrote:
It is not a good idea to give two different things the same
identifier.
On the other hand it is a bad idea to not use the obvious name for a variable. If I'm dealing with a single variable of type "struct dog",
I'd prefer not to have to come up with another name just to avoid
calling it "dog".
On the other hand it is a bad idea to not use the obvious name for a
variable. If I'm dealing with a single variable of type "struct dog",
I'd prefer not to have to come up with another name just to avoid
calling it "dog".
That's much more likely to happen in sample code of the kind
commonly posted here than in real-world programs. Realistically,
if you have a type "struct dog", a variable of that type is likely
to have some more specific meaning.
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
On Tue, 25 Aug 2026 15:11:44 +0100, Richard Harnden wrote:
On 25/08/2026 13:15, David Brown wrote:
Oh, and don't mix variable and struct names - no "struct foo foo".
Any reason why? Just a style thing?
This is one area where C++ is (by design?) backward-incompatible with
C.
In C, struct names and enum names occupy namespaces separate from the
names of variables and typedefs. In C++, they are all in the same
namespace.
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes. That's unlikely to matter, and I still
haven't looked up the relevant rules in the standard.
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes. That's unlikely to matter, and I still
haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of embedded C compilers that did not support struct assignment (or passing
or returning structs by value in functions). Fortunately I have not
seen such blatant non-conformity for a couple of decades.
David Brown <david.brown@hesbynett.no> wrote:[...]
Digging in the toolchain horror stories again, I have used a couple of
embedded C compilers that did not support struct assignment (or passing
or returning structs by value in functions). Fortunately I have not
seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value. At that time they claimed C2011 support.
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes. That's unlikely to matter, and I still
haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of
embedded C compilers that did not support struct assignment (or passing
or returning structs by value in functions). Fortunately I have not
seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value. At that time they claimed C2011 support.
On 26/08/2026 11:50, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes. That's unlikely to matter, and I still
haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of
embedded C compilers that did not support struct assignment (or passing
or returning structs by value in functions). Fortunately I have not
seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value. At that time they claimed C2011 support.
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers) for some aspects of C. I don't know the specifics of what it does and does
not support, but there are many non-conformities or missing features
that are sometimes seen for toolchains for such targets. These are all taken from real toolchains, some of which sold for significant prices :
* Failing to zero program-lifetime data (that's a nasty one, because it
does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
* Inability to pass and/or return structs by value
* Lack of support for 64-bit long long
* Functions not being re-entrant or recursive unless specifically marked
by an extension
* const data and non-const being in separate address spaces, and their pointers being incompatible (that's another painful one)
* Lack of support for multi-dimensional arrays
* Not allowing arrays of structs, and/or structs with array fields
* Small limits on the number of parameters in functions, or the even the number of local variables
* Failing to promote small integer types to "int" in arithmetic
expressions (another cause of subtle problems)
Embedded development got a lot simpler - but perhaps less interesting -
when ARM and gcc became dominant.
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 11:50, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes. That's unlikely to matter, and I still
haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of >>>> embedded C compilers that did not support struct assignment (or passing >>>> or returning structs by value in functions). Fortunately I have not
seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value. At that time they claimed C2011 support.
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers) for
some aspects of C. I don't know the specifics of what it does and does
not support, but there are many non-conformities or missing features
that are sometimes seen for toolchains for such targets. These are all
taken from real toolchains, some of which sold for significant prices :
* Failing to zero program-lifetime data (that's a nasty one, because it
does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
That does not bother me too much. If there is no hardware floating
point, then compiler support means library + syntactic sugar.
I would use compiler provided library only if it gives me exactly
what I need, which is unlikely to be the case.
* Inability to pass and/or return structs by value
That bothers me quite a lot, especially if that includes structures
that fit into machine register. AFAICS structures and unions are
the only way to get resonable supply of types which contain the same information as a base type, but are incompatible with it (typedef
is considerd different name for the type so would not do).
* Lack of support for 64-bit long long
That bothers me more than lack of double, but like floating point
this is library + syntactic sugar. I can provide my own library
if I need it. But replacement type normally would be a struct
so I would like ability to pass and return them from functions.
* Functions not being re-entrant or recursive unless specifically marked
by an extension
Not nice, but practical impact likely is minimal: for small embedded
target recursive functions are likely to be quite rare.
* const data and non-const being in separate address spaces, and their
pointers being incompatible (that's another painful one)
AFAICS this is essentially forced by the hardware + desire to keep
const data in flash. And offending hardware is well known, so one
knows which hardware should be avoided.
* Lack of support for multi-dimensional arrays
* Not allowing arrays of structs, and/or structs with array fields
* Small limits on the number of parameters in functions, or the even the
number of local variables
Smallest MCU that I have has 256 bytes RAM, that obviously limits
number of local variables. And there are smaller MCU-s. OTOH
on some machines, like Cortex M0, supporting decent number of
local variables is painful. I can understand compiler writer
which decides to support only what works in natural way on given
hardware. AFAIK gcc always tried to give smooth support in face
of hardware limitations, but in the past it was rich source of bugs.
* Failing to promote small integer types to "int" in arithmetic
expressions (another cause of subtle problems)
Embedded development got a lot simpler - but perhaps less interesting -
when ARM and gcc became dominant.
richard@cogsci.ed.ac.uk (Richard Tobin) writes:
In article <116k96l$3oj0e$1@dont-email.me>,
David Brown <david.brown@hesbynett.no> wrote:
It is not a good idea to give two different things the same
identifier.
On the other hand it is a bad idea to not use the obvious name for a
variable. If I'm dealing with a single variable of type "struct dog",
I'd prefer not to have to come up with another name just to avoid
calling it "dog".
That's much more likely to happen in sample code of the kind
commonly posted here than in real-world programs. Realistically,
if you have a type "struct dog", a variable of that type is likely
to have some more specific meaning.
On 26/08/2026 11:50, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
˙˙˙˙˙ foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes.˙ That's unlikely to matter, and I still
haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of
embedded C compilers that did not support struct assignment (or passing
or returning structs by value in functions).˙ Fortunately I have not
seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value.˙ At that time they claimed C2011 support.
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers) for some aspects of C.˙ I don't know the specifics of what it does and does
not support, but there are many non-conformities or missing features
that are sometimes seen for toolchains for such targets.˙ These are all taken from real toolchains, some of which sold for significant prices :
* Failing to zero program-lifetime data (that's a nasty one, because it
does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
* Inability to pass and/or return structs by value
* Lack of support for 64-bit long long
* Functions not being re-entrant or recursive unless specifically marked
by an extension
* const data and non-const being in separate address spaces, and their pointers being incompatible (that's another painful one)
* Lack of support for multi-dimensional arrays
* Not allowing arrays of structs, and/or structs with array fields
* Small limits on the number of parameters in functions, or the even the number of local variables
* Failing to promote small integer types to "int" in arithmetic
expressions (another cause of subtle problems)
For passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed
by reference. At least that's what compilers for 64-bit machines seem to do.
bart <bc@freeuk.com> writes:
For passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed
by reference. At least that's what compilers for 64-bit machines seem to do.
Which ABI?
This is from the SPARC ABI:
On 26/08/2026 12:47, David Brown wrote:
On 26/08/2026 11:50, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
˙˙˙˙˙ foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes.˙ That's unlikely to matter, and I still
haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of >>>> embedded C compilers that did not support struct assignment (or passing >>>> or returning structs by value in functions).˙ Fortunately I have not
seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value.˙ At that time they claimed C2011 support.
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers)
for some aspects of C.˙ I don't know the specifics of what it does and
does not support, but there are many non-conformities or missing
features that are sometimes seen for toolchains for such targets.
These are all taken from real toolchains, some of which sold for
significant prices :
* Failing to zero program-lifetime data (that's a nasty one, because
it does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
* Inability to pass and/or return structs by value
* Lack of support for 64-bit long long
I suppose you could have 64-bit floats and ints, but if the device has,
for example, only 64KB code + data, there wouldn't be much memory left
after those libraries are included.
They wouldn't run that fast either if it is a 8-bit device with a clock speed in MHz.
For passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed
by reference. At least that's what compilers for 64-bit machines seem to
do.
* Functions not being re-entrant or recursive unless specifically
marked by an extension
That sounds like a feature actually. Although if there are lots of such functions that keep their locals in static memory (off the stack), they
will occupy memory even when not called.
* const data and non-const being in separate address spaces, and their
pointers being incompatible (that's another painful one)
* Lack of support for multi-dimensional arrays
I don't think C technically has multi-dimension arrays, it just allows
1D arrays whose elements can be other fixed-size arrays.
Do you mean elements can only be non-aggregate types, or that pointer dereference levels are limited?
That would be an unreasonable limitation, assuming a cross-compiler is
being used. (If the compiler has to run /on/ the device, as mine did,
then it's more understandable!)
* Not allowing arrays of structs, and/or structs with array fields
See above.
* Small limits on the number of parameters in functions, or the even
the number of local variables
* Failing to promote small integer types to "int" in arithmetic
expressions (another cause of subtle problems)
That's another useful feature, even if it makes it non-conforming. But perhaps it should be an option.
On 25/08/2026 23:47, Lawrence D?Oliveiro wrote:
On Tue, 25 Aug 2026 15:11:44 +0100, Richard Harnden wrote:
On 25/08/2026 13:15, David Brown wrote:
Oh, and don't mix variable and struct names - no "struct foo foo".
Any reason why?˙ Just a style thing?
This is one area where C++ is (by design?) backward-incompatible with
C.
You make it sound like it might have been designed specifically to be backwards incompatible with C, which is not the case.
The point of having C++
˙˙˙˙struct X { ... };
behave as though it were C
˙˙˙˙typedef struct X { ... } X;
is simply that struct (and therefore also class) types are heavily used
in C++, and the language does not want to distinguish them in normal usage.
In C, you can choose to keep the distinction (by using the full name
"struct foo") or remove it (by using typedefs).˙ Some prefer one style,
some prefer the other.
In C, struct names and enum names occupy namespaces separate from the
names of variables and typedefs. In C++, they are all in the same
namespace.
To be pedantic, the term you are looking for is "name space" - two
words.˙ That's the term used in both the C and C++ standards, while "namespace" (one word) is a specific feature of the C++ language.
Obviously there is no confusion in what you wrote here, but it's always possible that someone will want to look at what the C standard says
about the different name spaces - and then they should search the pdf
files with the correct spelling.
On 26/08/2026 12:47, David Brown wrote:
On 26/08/2026 11:50, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie:
correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
˙˙˙˙˙ foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes.˙ That's unlikely to matter, and I still
haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of >>>> embedded C compilers that did not support struct assignment (or passing >>>> or returning structs by value in functions).˙ Fortunately I have not
seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value.˙ At that time they claimed C2011 support.
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers) for
some aspects of C.˙ I don't know the specifics of what it does and does
not support, but there are many non-conformities or missing features
that are sometimes seen for toolchains for such targets.˙ These are all
taken from real toolchains, some of which sold for significant prices :
* Failing to zero program-lifetime data (that's a nasty one, because it
does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
* Inability to pass and/or return structs by value
* Lack of support for 64-bit long long
I suppose you could have 64-bit floats and ints, but if the device has,
for example, only 64KB code + data, there wouldn't be much memory left
after those libraries are included.
They wouldn't run that fast either if it is a 8-bit device with a clock speed in MHz.
For passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed
by reference. At least that's what compilers for 64-bit machines seem to do.
* Functions not being re-entrant or recursive unless specifically marked
by an extension
That sounds like a feature actually. Although if there are lots of such functions that keep their locals in static memory (off the stack), they
will occupy memory even when not called.
* const data and non-const being in separate address spaces, and their
pointers being incompatible (that's another painful one)
* Lack of support for multi-dimensional arrays
I don't think C technically has multi-dimension arrays, it just allows
1D arrays whose elements can be other fixed-size arrays.
Do you mean elements can only be non-aggregate types, or that pointer dereference levels are limited?
That would be an unreasonable limitation, assuming a cross-compiler is
being used. (If the compiler has to run /on/ the device, as mine did,
then it's more understandable!)
* Not allowing arrays of structs, and/or structs with array fields
See above.
* Small limits on the number of parameters in functions, or the even the
number of local variables
* Failing to promote small integer types to "int" in arithmetic
expressions (another cause of subtle problems)
That's another useful feature, even if it makes it non-conforming. But perhaps it should be an option.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
richard@cogsci.ed.ac.uk (Richard Tobin) writes:
In article <116k96l$3oj0e$1@dont-email.me>,
David Brown <david.brown@hesbynett.no> wrote:
It is not a good idea to give two different things the same
identifier.
On the other hand it is a bad idea to not use the obvious name for a
variable. If I'm dealing with a single variable of type "struct dog",
I'd prefer not to have to come up with another name just to avoid
calling it "dog".
That's much more likely to happen in sample code of the kind
commonly posted here than in real-world programs. Realistically,
if you have a type "struct dog", a variable of that type is likely
to have some more specific meaning.
Or one might use 'struct canine' and then call it a dog...
On 26/08/2026 18:33, bart wrote:[...]
On 26/08/2026 12:47, David Brown wrote:
* Lack of support for multi-dimensional arraysI don't think C technically has multi-dimension arrays, it just
allows 1D arrays whose elements can be other fixed-size arrays.
Do you mean elements can only be non-aggregate types, or that
pointer dereference levels are limited?
Without trying to be pedantic about terminology, I mean the later
here. Thus such compilers can't handle "int xss[10][20];".
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers)
for some aspects of C. I don't know the specifics of what it does and
does not support, but there are many non-conformities or missing
features that are sometimes seen for toolchains for such targets.
These are all taken from real toolchains, some of which sold for
significant prices :
* Failing to zero program-lifetime data (that's a nasty one, because
it does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
* Lack of support for 64-bit long long
bart <bc@freeuk.com> wrote:[...]
For passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed
by reference. At least that's what compilers for 64-bit machines seem to do.
C says that arguments are passed by value, which needs a copy. C 'const' seem to give too weak warranty to prevent it.
bart <bc@freeuk.com> wrote:
On 26/08/2026 12:47, David Brown wrote:
On 26/08/2026 11:50, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie: >>>>>>> correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time.
There's nothing wrong with
˙˙˙˙˙ foo = FOO_INIT;
There might be some subtle semantic differences involving padding
bits and/or padding bytes.˙ That's unlikely to matter, and I still >>>>>> haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of >>>>> embedded C compilers that did not support struct assignment (or passing >>>>> or returning structs by value in functions).˙ Fortunately I have not >>>>> seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value.˙ At that time they claimed C2011 support.
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers) for >>> some aspects of C.˙ I don't know the specifics of what it does and does
not support, but there are many non-conformities or missing features
that are sometimes seen for toolchains for such targets.˙ These are all
taken from real toolchains, some of which sold for significant prices :
* Failing to zero program-lifetime data (that's a nasty one, because it
does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
* Inability to pass and/or return structs by value
* Lack of support for 64-bit long long
I suppose you could have 64-bit floats and ints, but if the device has,
for example, only 64KB code + data, there wouldn't be much memory left
after those libraries are included.
They wouldn't run that fast either if it is a 8-bit device with a clock
speed in MHz.
I am not sure if you are familiar with typical modern MCU-s. First,
in a sense you get a complete computer in a single chip. Just connect
power, one or two capacitors and possibly connect appropriate voltage
to configuration pins. Inside chip there is CPU, RAM and flash
memory and a bunch of peripherials, like pin drivers, serial ports,
ADC, etc. Flash is writable, but slow to write and number of writes
may be limited.
64-bit addition and subtraction is rather small routine even
on 8-bitters. On CH32V003 there is complication as the CPU
has no carry flag,
On 8/26/2026 12:28 AM, David Brown wrote:
On 25/08/2026 23:47, Lawrence D?Oliveiro wrote:
On Tue, 25 Aug 2026 15:11:44 +0100, Richard Harnden wrote:
On 25/08/2026 13:15, David Brown wrote:
Oh, and don't mix variable and struct names - no "struct foo foo".
Any reason why?˙ Just a style thing?
This is one area where C++ is (by design?) backward-incompatible with
C.
You make it sound like it might have been designed specifically to be
backwards incompatible with C, which is not the case.
The point of having C++
˙˙˙˙˙struct X { ... };
behave as though it were C
˙˙˙˙˙typedef struct X { ... } X;
is simply that struct (and therefore also class) types are heavily
used in C++, and the language does not want to distinguish them in
normal usage.
In C, you can choose to keep the distinction (by using the full name
"struct foo") or remove it (by using typedefs).˙ Some prefer one
style, some prefer the other.
In C, struct names and enum names occupy namespaces separate from the
names of variables and typedefs. In C++, they are all in the same
namespace.
To be pedantic, the term you are looking for is "name space" - two
words.˙ That's the term used in both the C and C++ standards, while
"namespace" (one word) is a specific feature of the C++ language.
Obviously there is no confusion in what you wrote here, but it's
always possible that someone will want to look at what the C standard
says about the different name spaces - and then they should search the
pdf files with the correct spelling.
name space in C, not that bad.
ct_*
There. A name space.
antispam@fricas.org (Waldek Hebisch) writes:
bart <bc@freeuk.com> wrote:[...]
For passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed
by reference. At least that's what compilers for 64-bit machines seem to do.
C says that arguments are passed by value, which needs a copy. C 'const'
seem to give too weak warranty to prevent it.
Pretty much. Attempting to modify an object via a const-qualified
lvalue (say, by casting away the "const") has undefined behavior, and a compiler can assume that such modifications never happen, but that's not enough to allow eliminating the copy in all cases.
For example:
struct big { blah; blah; };
void func(const struct big param) {
// ...
}
In the abstract machine, func() receives a copy of the argument.
Depending on the ABI, the caller might create a copy and pass the
address of the copy.
You might think that the "const" would allow generating code
that passes the address instead, but suppose the argument is a
"global variable" and func() calls something that modifies it.
That modification must not affect the value of the parameter.
A compiler can certainly avoid the copy if it can prove that doing so
doesn't change the behavior. This is easier if the call is inlined.
The argument object and the parameter object must have distinct
addresses, but that's not necessarily enforced if the address is
never taken.
On 27/08/2026 00:29, Waldek Hebisch wrote:
I am not sure if you are familiar with typical modern MCU-s.˙ First,
in a sense you get a complete computer in a single chip.˙ Just connect
power, one or two capacitors and possibly connect appropriate voltage
to configuration pins.˙ Inside chip there is CPU, RAM and flash
memory and a bunch of peripherials, like pin drivers, serial ports,
ADC, etc.˙ Flash is writable, but slow to write and number of writes
may be limited.
I used only a couple of devices with on-chip peripherals, although since
it was over 40 years ago, my know-how will be dated.
However my comment was about emulating 64-bit int and float ops on small devices.
On modern 64-bit processors, people still like to keep to a 32-bit int because it is more efficient,
yet people are also saying it is fine to
do /software emulation/ of 64 bit ops on magnitudes-slower 8-bit devices.
(My application area was low-end CAD which needed lots of 32-bit
floating point. That needed emulation on Z80, and also later
8086/286/386 when there was no FPU. I didn't attempt 64-bit floats until
I knew they were supported by hardware.
However I accept that some applications may only need to do FP ops at a
slow enough rate that the emulation can keep up.)
David Brown <david.brown@hesbynett.no> writes:
[...]
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers)
for some aspects of C. I don't know the specifics of what it does and
does not support, but there are many non-conformities or missing
features that are sometimes seen for toolchains for such targets.
These are all taken from real toolchains, some of which sold for
significant prices :
* Failing to zero program-lifetime data (that's a nasty one, because
it does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
I would be very annoyed by a C-like compiler that defines a type
"double" that doesn't meet the standard's requirements for that type.
I'd prefer it to allow "float" and reject "double".
[...]
* Lack of support for 64-bit long long
Lack of support for long long is fine, once you accept that the
compiler is non-conforming. Defining a type "long long" that's
narrower than 64 bits is tantamount to lying to the user.
Disclaimer: I haven't used such compilers.
On 27/08/2026 03:10, bart wrote:
On 27/08/2026 00:29, Waldek Hebisch wrote:
I am not sure if you are familiar with typical modern MCU-s.˙ First,
in a sense you get a complete computer in a single chip.˙ Just connect
power, one or two capacitors and possibly connect appropriate voltage
to configuration pins.˙ Inside chip there is CPU, RAM and flash
memory and a bunch of peripherials, like pin drivers, serial ports,
ADC, etc.˙ Flash is writable, but slow to write and number of writes
may be limited.
I used only a couple of devices with on-chip peripherals, although
since it was over 40 years ago, my know-how will be dated.
However my comment was about emulating 64-bit int and float ops on
small devices.
On modern 64-bit processors, people still like to keep to a 32-bit int
because it is more efficient,
I would be careful about that - I think there are many reasons why "int"
is usually 32-bit on 64-bit systems.˙ They are more efficient in some
ways, and less efficient in others.˙ I would guess, but not assume, that
the prime reason is to minimise issues with existing 32-bit code that assumes "int" is exactly 32-bit.
yet people are also saying it is fine to do /software emulation/ of 64
bit ops on magnitudes-slower 8-bit devices.
Can you quote anyone saying that, or are you inventing things again?
People have pointed out that C conformance /requires/ support for 64-bit
(or bigger, but let's agree to simplify a little) integer types.
Sometimes, but not often, they can even be useful in code for small microcontrollers.
I think everyone realises that the implementation of 64-bit types on any processor that does not support them directly in hardware, needs to
build them from smaller elements.
On 27/08/2026 02:21, Keith Thompson wrote:
antispam@fricas.org (Waldek Hebisch) writes:
bart <bc@freeuk.com> wrote:[...]
Pretty much. Attempting to modify an object via a const-qualifiedFor passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed >>>> by reference. At least that's what compilers for 64-bit machines seem to do.
C says that arguments are passed by value, which needs a copy. C 'const' >>> seem to give too weak warranty to prevent it.
lvalue (say, by casting away the "const") has undefined behavior, and a
compiler can assume that such modifications never happen, but that's not
enough to allow eliminating the copy in all cases.
To be clear here - attempting to modify an object that is defined
const, such as by casts and pointers, is UB. But if you have a const-qualified lvalue identifying an object that was not defined
const, then you can "cast away the const" and change it.
On 27/08/2026 08:53, David Brown wrote:
On 27/08/2026 03:10, bart wrote:
On 27/08/2026 00:29, Waldek Hebisch wrote:I would be careful about that - I think there are many reasons why
I am not sure if you are familiar with typical modern MCU-s.˙ First,
in a sense you get a complete computer in a single chip.˙ Just connect >>>> power, one or two capacitors and possibly connect appropriate voltage
to configuration pins.˙ Inside chip there is CPU, RAM and flash
memory and a bunch of peripherials, like pin drivers, serial ports,
ADC, etc.˙ Flash is writable, but slow to write and number of writes
may be limited.
I used only a couple of devices with on-chip peripherals, although
since it was over 40 years ago, my know-how will be dated.
However my comment was about emulating 64-bit int and float ops on
small devices.
On modern 64-bit processors, people still like to keep to a 32-bit
int because it is more efficient,
"int" is usually 32-bit on 64-bit systems.˙ They are more efficient
in some ways, and less efficient in others.˙ I would guess, but not
assume, that the prime reason is to minimise issues with existing
32-bit code that assumes "int" is exactly 32-bit.
I'm not going to trawl through old threads, but I distinctly remember
people saying that because most integer values that come up will fit
into i32, then there was no need for 'int' to be i64.
You were bemoaning the lack of support for 64-bit ints and floats, and sometimes no floats at all, on small devices. Which would imply that
you expected them to be emulated no matter what.
And WH gave extensive examples of such emulation being done.
People have pointed out that C conformance /requires/ support for
64-bit (or bigger, but let's agree to simplify a little) integer
types. Sometimes, but not often, they can even be useful in code for
small microcontrollers.
I guess then that _BitInt support will soon be required?
I'm saying it is OK for a compiler vendor to choose to omit some
features that are a poor match for the intended targets.
I think you mentioned that gcc did not limit things like the number of
a local variables in a function, yet still worked for a target with no memory?! I suppose /some/ limitations have to be in place?
On 27/08/2026 02:21, Keith Thompson wrote:
antispam@fricas.org (Waldek Hebisch) writes:
bart <bc@freeuk.com> wrote:[...]
For passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed >>>> by reference. At least that's what compilers for 64-bit machines
seem to do.
C says that arguments are passed by value, which needs a copy.˙ C
'const'
seem to give too weak warranty to prevent it.
Pretty much.˙ Attempting to modify an object via a const-qualified
lvalue (say, by casting away the "const") has undefined behavior, and a
compiler can assume that such modifications never happen, but that's not
enough to allow eliminating the copy in all cases.
To be clear here - attempting to modify an object that is defined const, such as by casts and pointers, is UB.˙ But if you have a const-qualified lvalue identifying an object that was not defined const, then you can
"cast away the const" and change it.
Thus :
˙˙˙˙void foo(const int * p) {
˙˙˙˙˙˙˙ int * q = (int *) p;
˙˙˙˙˙˙˙ *q = 1;
˙˙˙˙}
˙˙˙˙int okay() {
˙˙˙˙˙˙˙ int x = 2;
˙˙˙˙˙˙˙ foo(&x);
˙˙˙˙˙˙˙ return x;˙˙˙ // Returns 1
˙˙˙˙}
˙˙˙˙int bad() {
˙˙˙˙˙˙˙ const int x = 2;
˙˙˙˙˙˙˙ foo(&x);
˙˙˙˙˙˙˙ return x;˙˙˙ // Could return 1, 2, or a nasal daemon
˙˙˙˙}
This also means that using "const T *" pointers for passing structs (or anything else) does not guarantee that the callee does not change
anything, and the compiler, when compiling the calling code, can't
assume that the passed-by-const-pointer data stays constant unless the
data was defined as "const" and it assumes no UB occurs.˙ (The same
applies to pass by const reference in C++.)
For example:
˙˙˙˙ struct big { blah; blah; };
˙˙˙˙ void func(const struct big param) {
˙˙˙˙˙˙˙˙ // ...
˙˙˙˙ }
In the abstract machine, func() receives a copy of the argument.
Depending on the ABI, the caller might create a copy and pass the
address of the copy.
You might think that the "const" would allow generating code
that passes the address instead, but suppose the argument is a
"global variable" and func() calls something that modifies it.
That modification must not affect the value of the parameter.
A compiler can certainly avoid the copy if it can prove that doing so
doesn't change the behavior.˙ This is easier if the call is inlined.
The argument object and the parameter object must have distinct
addresses, but that's not necessarily enforced if the address is
never taken.
I'd have preferred "const" to give stronger guarantees, but it's a bit
late for that now!
On 27/08/2026 08:53, David Brown wrote:
On 27/08/2026 03:10, bart wrote:
On 27/08/2026 00:29, Waldek Hebisch wrote:
I am not sure if you are familiar with typical modern MCU-s.˙ First,
in a sense you get a complete computer in a single chip.˙ Just connect >>>> power, one or two capacitors and possibly connect appropriate voltage
to configuration pins.˙ Inside chip there is CPU, RAM and flash
memory and a bunch of peripherials, like pin drivers, serial ports,
ADC, etc.˙ Flash is writable, but slow to write and number of writes
may be limited.
I used only a couple of devices with on-chip peripherals, although
since it was over 40 years ago, my know-how will be dated.
However my comment was about emulating 64-bit int and float ops on
small devices.
On modern 64-bit processors, people still like to keep to a 32-bit
int because it is more efficient,
I would be careful about that - I think there are many reasons why
"int" is usually 32-bit on 64-bit systems.˙ They are more efficient in
some ways, and less efficient in others.˙ I would guess, but not
assume, that the prime reason is to minimise issues with existing 32-
bit code that assumes "int" is exactly 32-bit.
I'm not going to trawl through old threads, but I distinctly remember
people saying that because most integer values that come up will fit
into i32, then there was no need for 'int' to be i64.
(I think I came up with some figures to show that 50% of integer values would fit into a byte value!)
yet people are also saying it is fine to do /software emulation/ of
64 bit ops on magnitudes-slower 8-bit devices.
Can you quote anyone saying that, or are you inventing things again?
You were bemoaning the lack of support for 64-bit ints and floats, and sometimes no floats at all, on small devices. Which would imply that you expected them to be emulated no matter what.
And WH gave extensive examples of such emulation being done.
People have pointed out that C conformance /requires/ support for 64-
bit (or bigger, but let's agree to simplify a little) integer types.
Sometimes, but not often, they can even be useful in code for small
microcontrollers.
I guess then that _BitInt support will soon be required?
I gave an example of my own where a 1000! calculation (1000 multi-
precision multiplies) would take many minutes on an 8-bit processor
running at 1980s speeds.
In the 1980s, these restrictions would not have been remarkable. Nobody
then would be complaining that their C compiler didn't support 64-bit integers, or 128- or 256-bit for that matter.
But because now, decades later, C has those features since modern
machines are so much more capable, it seems unreasonable to expect / everything/ to be supported on those old systems.
I think everyone realises that the implementation of 64-bit types on
any processor that does not support them directly in hardware, needs
to build them from smaller elements.
You're saying it again!
i64 support on a modern machine is more critical
because things like file and disk sizes, memory sizes and so on may need
it. Those quantities were much smaller on smaller systems.
I'm saying it is OK for a compiler vendor to choose to omit some
features that are a poor match for the intended targets.
I think you mentioned that gcc did not limit things like the number of a local variables in a function, yet still worked for a target with no memory?! I suppose /some/ limitations have to be in place?
On 27/08/2026 00:29, Waldek Hebisch wrote:
bart <bc@freeuk.com> wrote:
On 26/08/2026 12:47, David Brown wrote:
On 26/08/2026 11:50, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 26/08/2026 03:01, Keith Thompson wrote:
Richard Harnden <richard.nospam@gmail.invalid> writes:
[...]
Would "memcpy(&foo, &FOO_INIT, sizeof FOO_INIT);" be better, ie: >>>>>>>> correct for pointers and floats that are not all-bits-zero?
Given that FOO_INIT is defined correctly, that should work.
Calling memcpy() would be a great workaround for a language
that doesn't support struct assignment as a language feature.
Fortunately, C has not been such a language for a very long time. >>>>>>>
There's nothing wrong with
˙˙˙˙˙ foo = FOO_INIT;
There might be some subtle semantic differences involving padding >>>>>>> bits and/or padding bytes.˙ That's unlikely to matter, and I still >>>>>>> haven't looked up the relevant rules in the standard.
Digging in the toolchain horror stories again, I have used a couple of >>>>>> embedded C compilers that did not support struct assignment (or passing >>>>>> or returning structs by value in functions).˙ Fortunately I have not >>>>>> seen such blatant non-conformity for a couple of decades.
Last time that I checked sdcc did not support passing or returning
structs by value.˙ At that time they claimed C2011 support.
SDCC does its best, but it's very difficult to generate decent object
code for its targets (mainly brain-dead 8-bit CISC microcontrollers) for >>>> some aspects of C.˙ I don't know the specifics of what it does and does >>>> not support, but there are many non-conformities or missing features
that are sometimes seen for toolchains for such targets.˙ These are all >>>> taken from real toolchains, some of which sold for significant prices : >>>>
* Failing to zero program-lifetime data (that's a nasty one, because it >>>> does not give compile-time errors)
* 32-bit "double", or maybe no floating point support at all
* Inability to pass and/or return structs by value
* Lack of support for 64-bit long long
I suppose you could have 64-bit floats and ints, but if the device has,
for example, only 64KB code + data, there wouldn't be much memory left
after those libraries are included.
They wouldn't run that fast either if it is a 8-bit device with a clock
speed in MHz.
I am not sure if you are familiar with typical modern MCU-s. First,
in a sense you get a complete computer in a single chip. Just connect
power, one or two capacitors and possibly connect appropriate voltage
to configuration pins. Inside chip there is CPU, RAM and flash
memory and a bunch of peripherials, like pin drivers, serial ports,
ADC, etc. Flash is writable, but slow to write and number of writes
may be limited.
I used only a couple of devices with on-chip peripherals, although since
it was over 40 years ago, my know-how will be dated.
However my comment was about emulating 64-bit int and float ops on small devices.
On modern 64-bit processors, people still like to keep to a 32-bit int because it is more efficient, yet people are also saying it is fine to
do /software emulation/ of 64 bit ops on magnitudes-slower 8-bit devices.
(My application area was low-end CAD which needed lots of 32-bit
floating point. That needed emulation on Z80, and also later
8086/286/386 when there was no FPU. I didn't attempt 64-bit floats until
I knew they were supported by hardware.
However I accept that some applications may only need to do FP ops at a
slow enough rate that the emulation can keep up.)
64-bit addition and subtraction is rather small routine even
on 8-bitters. On CH32V003 there is complication as the CPU
has no carry flag,
To get back to this point, previous discussions about a 64-bit C 'int'
have suggested there weren't enough use-cases to make worthwhile as a default. So what sorts of things need a 64-bit range?
(BTW I recently tested big-number multiplication on my Z80 emulator. Calculating 1000!, which has a 2600-digit result, would have taken about
45 mins on a 4MHz device. Just because it can do it (it just has to fit
into memory) doesn't mean it is viable! However a decent compiler will
have improved on that time.)
On 27/08/2026 03:10, bart wrote:
On 27/08/2026 00:29, Waldek Hebisch wrote:
I am not sure if you are familiar with typical modern MCU-s.˙ First,
in a sense you get a complete computer in a single chip.˙ Just connect
power, one or two capacitors and possibly connect appropriate voltage
to configuration pins.˙ Inside chip there is CPU, RAM and flash
memory and a bunch of peripherials, like pin drivers, serial ports,
ADC, etc.˙ Flash is writable, but slow to write and number of writes
may be limited.
I used only a couple of devices with on-chip peripherals, although since
it was over 40 years ago, my know-how will be dated.
However my comment was about emulating 64-bit int and float ops on small
devices.
On modern 64-bit processors, people still like to keep to a 32-bit int
because it is more efficient,
I would be careful about that - I think there are many reasons why "int"
is usually 32-bit on 64-bit systems. They are more efficient in some
ways, and less efficient in others. I would guess, but not assume, that
the prime reason is to minimise issues with existing 32-bit code that >assumes "int" is exactly 32-bit.
scott@slp53.sl.home (Scott Lurndal) writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes: >>>richard@cogsci.ed.ac.uk (Richard Tobin) writes:
In article <116k96l$3oj0e$1@dont-email.me>,
David Brown <david.brown@hesbynett.no> wrote:
It is not a good idea to give two different things the same >>>>>identifier.
On the other hand it is a bad idea to not use the obvious name for a
variable. If I'm dealing with a single variable of type "struct dog", >>>> I'd prefer not to have to come up with another name just to avoid
calling it "dog".
That's much more likely to happen in sample code of the kind
commonly posted here than in real-world programs. Realistically,
if you have a type "struct dog", a variable of that type is likely
to have some more specific meaning.
Or one might use 'struct canine' and then call it a dog...
Yes, but "canine" and "dog" mean pretty much the same thing (unless
"canine" also covers coyotes, wolves, et al, but I don't think
we're talking about inheritance).
David Brown <david.brown@hesbynett.no> writes:
On 27/08/2026 03:10, bart wrote:
On 27/08/2026 00:29, Waldek Hebisch wrote:
I am not sure if you are familiar with typical modern MCU-s.˙ First,
in a sense you get a complete computer in a single chip.˙ Just connect >>>> power, one or two capacitors and possibly connect appropriate voltage
to configuration pins.˙ Inside chip there is CPU, RAM and flash
memory and a bunch of peripherials, like pin drivers, serial ports,
ADC, etc.˙ Flash is writable, but slow to write and number of writes
may be limited.
I used only a couple of devices with on-chip peripherals, although since >>> it was over 40 years ago, my know-how will be dated.
However my comment was about emulating 64-bit int and float ops on small >>> devices.
On modern 64-bit processors, people still like to keep to a 32-bit int
because it is more efficient,
I would be careful about that - I think there are many reasons why "int"
is usually 32-bit on 64-bit systems. They are more efficient in some
ways, and less efficient in others. I would guess, but not assume, that
the prime reason is to minimise issues with existing 32-bit code that
assumes "int" is exactly 32-bit.
I would also argue that "more efficient" is not accurate. It would certainly be more space efficient but not more time efficient.
David Brown <david.brown@hesbynett.no> writes:[...]
I would be careful about that - I think there are many reasons why
"int" is usually 32-bit on 64-bit systems. They are more efficient
in some ways, and less efficient in others. I would guess, but not
assume, that the prime reason is to minimise issues with existing
32-bit code that assumes "int" is exactly 32-bit.
I would also argue that "more efficient" is not accurate. It would certainly be more space efficient but not more time efficient.
On 26/08/2026 21:37, Chris M. Thomasson wrote:
On 8/26/2026 12:28 AM, David Brown wrote:
On 25/08/2026 23:47, Lawrence D?Oliveiro wrote:
On Tue, 25 Aug 2026 15:11:44 +0100, Richard Harnden wrote:
On 25/08/2026 13:15, David Brown wrote:
Oh, and don't mix variable and struct names - no "struct foo foo".
Any reason why?˙ Just a style thing?
This is one area where C++ is (by design?) backward-incompatible with
C.
You make it sound like it might have been designed specifically to be
backwards incompatible with C, which is not the case.
The point of having C++
˙˙˙˙˙struct X { ... };
behave as though it were C
˙˙˙˙˙typedef struct X { ... } X;
is simply that struct (and therefore also class) types are heavily
used in C++, and the language does not want to distinguish them in
normal usage.
In C, you can choose to keep the distinction (by using the full name
"struct foo") or remove it (by using typedefs).˙ Some prefer one
style, some prefer the other.
In C, struct names and enum names occupy namespaces separate from the
names of variables and typedefs. In C++, they are all in the same
namespace.
To be pedantic, the term you are looking for is "name space" - two
words.˙ That's the term used in both the C and C++ standards, while
"namespace" (one word) is a specific feature of the C++ language.
Obviously there is no confusion in what you wrote here, but it's
always possible that someone will want to look at what the C standard
says about the different name spaces - and then they should search
the pdf files with the correct spelling.
name space in C, not that bad.
ct_*
There. A name space.
The distinction I was making seems to have whizzed far above your head.
For the convenience of people reading and using the standard, I was explaining the terms and their spelling, as used in the C (and C++) standards.˙ No one, I think, is in any doubt about what Lawrence meant
in his post - nor what you and others colloquially call "name spaces" or "namespaces" in C by using prefixes on externally visible identifiers.
No, "ct_*" is /not/ a "name space" in C.˙ It's an identifier prefix.˙ If
you want a new name space in C, declare a struct - within the struct declaration, you have a new name space so that the identifiers of the
fields are independent of any other identifiers in the code.
On 27/08/2026 02:21, Keith Thompson wrote:[...]
antispam@fricas.org (Waldek Hebisch) writes:
bart <bc@freeuk.com> wrote:[...]
For passing structs by value, C seems to require that copies are made
first, even if marked 'const', and even if the ABI says they are passed >>>> by reference. At least that's what compilers for 64-bit machines
seem to do.
C says that arguments are passed by value, which needs a copy.˙ C
'const'
seem to give too weak warranty to prevent it.
Pretty much.˙ Attempting to modify an object via a const-qualified
lvalue (say, by casting away the "const") has undefined behavior, and a
compiler can assume that such modifications never happen, but that's not
enough to allow eliminating the copy in all cases.
To be clear here - attempting to modify an object that is defined const, such as by casts and pointers, is UB.˙ But if you have a const-qualified lvalue identifying an object that was not defined const, then you can
"cast away the const" and change it.
Thus :
˙˙˙˙void foo(const int * p) {
˙˙˙˙˙˙˙ int * q = (int *) p;
˙˙˙˙˙˙˙ *q = 1;
˙˙˙˙}
Last time that I checked sdcc did not support passing or returning
structs by value. At that time they claimed C2011 support.
On 8/26/2026 11:28 PM, David Brown wrote:
On 26/08/2026 21:37, Chris M. Thomasson wrote:
On 8/26/2026 12:28 AM, David Brown wrote:
On 25/08/2026 23:47, Lawrence D?Oliveiro wrote:
On Tue, 25 Aug 2026 15:11:44 +0100, Richard Harnden wrote:
On 25/08/2026 13:15, David Brown wrote:
Any reason why?˙ Just a style thing?
Oh, and don't mix variable and struct names - no "struct foo foo". >>>>>>
This is one area where C++ is (by design?) backward-incompatible with >>>>> C.
You make it sound like it might have been designed specifically to
be backwards incompatible with C, which is not the case.
The point of having C++
˙˙˙˙˙struct X { ... };
behave as though it were C
˙˙˙˙˙typedef struct X { ... } X;
is simply that struct (and therefore also class) types are heavily
used in C++, and the language does not want to distinguish them in
normal usage.
In C, you can choose to keep the distinction (by using the full name
"struct foo") or remove it (by using typedefs).˙ Some prefer one
style, some prefer the other.
In C, struct names and enum names occupy namespaces separate from the >>>>> names of variables and typedefs. In C++, they are all in the same
namespace.
To be pedantic, the term you are looking for is "name space" - two
words.˙ That's the term used in both the C and C++ standards, while
"namespace" (one word) is a specific feature of the C++ language.
Obviously there is no confusion in what you wrote here, but it's
always possible that someone will want to look at what the C
standard says about the different name spaces - and then they should
search the pdf files with the correct spelling.
name space in C, not that bad.
ct_*
There. A name space.
The distinction I was making seems to have whizzed far above your head.
For the convenience of people reading and using the standard, I was
explaining the terms and their spelling, as used in the C (and C++)
standards.˙ No one, I think, is in any doubt about what Lawrence meant
in his post - nor what you and others colloquially call "name spaces"
or "namespaces" in C by using prefixes on externally visible identifiers.
stdio vs standard_input_output?
No, "ct_*" is /not/ a "name space" in C.˙ It's an identifier prefix.
If you want a new name space in C, declare a struct - within the
struct declaration, you have a new name space so that the identifiers
of the fields are independent of any other identifiers in the code.
Are you missing the forest for the trees in a sense?
On 8/27/2026 12:03 AM, David Brown wrote:
On 27/08/2026 02:21, Keith Thompson wrote:[...]
antispam@fricas.org (Waldek Hebisch) writes:
bart <bc@freeuk.com> wrote:[...]
For passing structs by value, C seems to require that copies are made >>>>> first, even if marked 'const', and even if the ABI says they are
passed
by reference. At least that's what compilers for 64-bit machines
seem to do.
C says that arguments are passed by value, which needs a copy.˙ C
'const'
seem to give too weak warranty to prevent it.
Pretty much.˙ Attempting to modify an object via a const-qualified
lvalue (say, by casting away the "const") has undefined behavior, and a
compiler can assume that such modifications never happen, but that's not >>> enough to allow eliminating the copy in all cases.
To be clear here - attempting to modify an object that is defined
const, such as by casts and pointers, is UB.˙ But if you have a const-
qualified lvalue identifying an object that was not defined const,
then you can "cast away the const" and change it.
Thus :
˙˙˙˙˙void foo(const int * p) {
˙˙˙˙˙˙˙˙ int * q = (int *) p;
˙˙˙˙˙˙˙˙ *q = 1;
˙˙˙˙˙}
Mutable?>
On 27/08/2026 23:07, Chris M. Thomasson wrote:
On 8/26/2026 11:28 PM, David Brown wrote:
On 26/08/2026 21:37, Chris M. Thomasson wrote:
On 8/26/2026 12:28 AM, David Brown wrote:
On 25/08/2026 23:47, Lawrence D?Oliveiro wrote:
On Tue, 25 Aug 2026 15:11:44 +0100, Richard Harnden wrote:
On 25/08/2026 13:15, David Brown wrote:
Any reason why?˙ Just a style thing?
Oh, and don't mix variable and struct names - no "struct foo foo". >>>>>>>
This is one area where C++ is (by design?) backward-incompatible with >>>>>> C.
You make it sound like it might have been designed specifically to
be backwards incompatible with C, which is not the case.
The point of having C++
˙˙˙˙˙struct X { ... };
behave as though it were C
˙˙˙˙˙typedef struct X { ... } X;
is simply that struct (and therefore also class) types are heavily
used in C++, and the language does not want to distinguish them in
normal usage.
In C, you can choose to keep the distinction (by using the full
name "struct foo") or remove it (by using typedefs).˙ Some prefer
one style, some prefer the other.
In C, struct names and enum names occupy namespaces separate from the >>>>>> names of variables and typedefs. In C++, they are all in the same
namespace.
To be pedantic, the term you are looking for is "name space" - two
words.˙ That's the term used in both the C and C++ standards, while >>>>> "namespace" (one word) is a specific feature of the C++ language.
Obviously there is no confusion in what you wrote here, but it's
always possible that someone will want to look at what the C
standard says about the different name spaces - and then they
should search the pdf files with the correct spelling.
name space in C, not that bad.
ct_*
There. A name space.
The distinction I was making seems to have whizzed far above your head.
For the convenience of people reading and using the standard, I was
explaining the terms and their spelling, as used in the C (and C++)
standards.˙ No one, I think, is in any doubt about what Lawrence
meant in his post - nor what you and others colloquially call "name
spaces" or "namespaces" in C by using prefixes on externally visible
identifiers.
stdio vs standard_input_output?
No, "ct_*" is /not/ a "name space" in C.˙ It's an identifier prefix.
If you want a new name space in C, declare a struct - within the
struct declaration, you have a new name space so that the identifiers
of the fields are independent of any other identifiers in the code.
Are you missing the forest for the trees in a sense?
I will try once more - a bit more slowly this time.˙ Please try to read
my post, and think about what I am saying.
/Everyone/ knows how to use prefixes with identifiers in C, and this is
used to group functions and other objects together.˙ It is especially popular for reusable libraries.˙ Anyone who has worked with C beyond a "Hello, world" program is aware of this.
I was pointing out, to Lawrence and anyone else, the exact terminology
used by the C standards.˙ That was not to be pedantic, or nit-picking -
it is useful to know such distinctions if you are looking things up in
the C standards.˙ If someone wants to know if the identifiers for union declarations and struct declarations are in separate name spaces in C,
or if they are combined (like in C++), then the fastest way to do is
likely to search a pdf of the C standards for "name space".˙ If that
person searches for "namespace", they will find nothing.˙ Exact terms,
exact spelling, /matters/.
I don't think I can explain more clearly than that.˙ Either you
understand why exact terms are important, or you do not.
C++ has "mutable", but C does not.
But the existence of "mutable" in C++ does illustrate the issue -
"const" in C and C++, at least in cases like this, does not mean the pointed-to data and its representation cannot ever change.˙ It is more
of a pinky-promise by the programmer that they will not change the
meaning of the data.
C2011 support is not the same as C2011 conformance.
On Thu, 27 Aug 2026 17:50:38 -0400, James Kuyper wrote:
C2011 support is not the same as C2011 conformance.Which is which? Can you ?support? without ?conforming?? ?Conform?
without ?supporting?? Can one be ?partial? but not the other? If so,
which?
And which one is equivalent to ?implementing??
Lawrence D?Oliveiro <ldo@nz.invalid> writes:
On Thu, 27 Aug 2026 17:50:38 -0400, James Kuyper wrote:
C2011 support is not the same as C2011 conformance.Which is which? Can you ?support? without ?conforming?? ?Conform?
without ?supporting?? Can one be ?partial? but not the other? If so,
which?
And which one is equivalent to ?implementing??
I'm not sure what you found difficult to understand. It seemed clear
enough to me (if you don't snip some of what James wrote):
C2011 support is not the same as C2011 conformance. I know nothing of
sdcc, but that wording allows for it to support particular features of
C2011, without necessarily fully conforming to any version of the C
standard.
Waldek Hebisch has previously written that sdcc "claimed C2011
support". It's plausible that that could have meant that it supports
some C2011-specific features (i.e., features that are in C2011 but
not in C1999), but does not fully conform to the C2011 standard.
For example, an implementation might support _Generic, but not
passing structs by value (which appears to be the case for sdcc
4.2.0). Depending on how it's worded, the documentation that
Waldek referred to could be accurate.
"C2011 conformance" would of course mean full conformance to the
C2011 standard.
On Thu, 27 Aug 2026 17:50:38 -0400, James Kuyper wrote:
On 2026-08-26 05:50, Waldek Hebisch wrote:
Last time that I checked sdcc did not support passing or returning
structs by value. At that time they claimed C2011 support.
C2011 support is not the same as C2011 conformance.
Which is which? Can you ?support? without ?conforming?? ?Conform?
without ?supporting?? Can one be ?partial? but not the other? If so,
which?
And which one is equivalent to ?implementing??
The C spec goes out of its way to make clear that a NULL pointer does
not necessarily have the same bit pattern as the integer value zero.
This in spite of the fact that it is allowed to test a pointer value
for NULL by comparing it to the integer value zero.
The spec also says that all static variables are initialized to zero
at program start time. Are pointer variables allowed to be given an
all-zero bit pattern at this time? Or are they supposed to be
initialized to the NULL value (if that?s different) instead?
| Sysop: | Tetrazocine |
|---|---|
| Location: | Melbourne, VIC, Australia |
| Users: | 9 |
| Nodes: | 8 (0 / 8) |
| Uptime: | 245:23:08 |
| Calls: | 220 |
| Files: | 21,513 |
| Messages: | 83,782 |