How cast works?
Does it changes the memory?
For instance, from "unsigned int" to "signed char".
Is it just like discarding bytes or something else?
[...]
I also would like to understand better signed and unsigned.
There is no such think as "signed" or "unsigned" register, right?
How about floating point?
The problem is that this causes an explosion of combinations that I am
trying to avoid.
Floating point is a huge mess ...
I don't know what happens when you're changing datatype lengths, but if >they're the same length, it's just telling the compiler what the
variable should be treated as (e.g. [8-bit] int to char)
Dan Purgert <[email protected]> wrote or quoted:
I don't know what happens when you're changing datatype lengths, but if >>they're the same length, it's just telling the compiler what the
variable should be treated as (e.g. [8-bit] int to char)
Casting doesn't tweak a value right where it sits, so you don't
have to stress about resizing memory. (It hands you an rvalue,
not an lvalue.)
On 07/08/2024 17:00, Dan Purgert wrote:
On 2024-08-07, Thiago Adams wrote:
How cast works?
Does it changes the memory?
For instance, from "unsigned int" to "signed char".
Is it just like discarding bytes or something else?
[...]
I don't know what happens when you're changing datatype lengths, but if
they're the same length, it's just telling the compiler what the
variable should be treated as (e.g. [8-bit] int to char)
I also would like to understand better signed and unsigned.
There is no such think as "signed" or "unsigned" register, right?
"Signed" just means the first bit indicates negative.
So an "unsigned" 8 bit integer will have the 256 values ranging from
0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = 0
(0b00000000)
TO
128 + 64 + 32 + 16 + 8 +4 + 2 + 1 = 255
(0b11111111)
Whereas a "signed" 8 bit integer will have the 256 values ranging from
(-128) + 0 + 0 + 0 + 0 + 0 + 0 + 0 = -128
(0b10000000)
TO
0 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127
(0b01111111)
At least in two's compliment (but that's the way it's done in C)
How about floating point?
Floating point is a huge mess, and has a few variations for encoding;
though I think most C implementations use the one from the IEEE on 1985
(uh, IEEE754, I think?)
I didn't specify properly , but my question was more about floating
point registers. I think in this case they have specialized registers.
On 08/08/2024 12:14, Thiago Adams wrote:
On 07/08/2024 17:00, Dan Purgert wrote:
On 2024-08-07, Thiago Adams wrote:
How cast works?
Does it changes the memory?
For instance, from "unsigned int" to "signed char".
Is it just like discarding bytes or something else?
[...]
I don't know what happens when you're changing datatype lengths,
but if they're the same length, it's just telling the compiler
what the variable should be treated as (e.g. [8-bit] int to char)
I also would like to understand better signed and unsigned.
There is no such think as "signed" or "unsigned" register, right?
"Signed" just means the first bit indicates negative.
So an "unsigned" 8 bit integer will have the 256 values ranging
from
0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = 0
(0b00000000)
TO
128 + 64 + 32 + 16 + 8 +4 + 2 + 1 = 255
(0b11111111)
Whereas a "signed" 8 bit integer will have the 256 values ranging
from
(-128) + 0 + 0 + 0 + 0 + 0 + 0 + 0 = -128
(0b10000000)
TO
0 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127
(0b01111111)
At least in two's compliment (but that's the way it's done in C)
How about floating point?
Floating point is a huge mess, and has a few variations for
encoding; though I think most C implementations use the one from
the IEEE on 1985 (uh, IEEE754, I think?)
I didn't specify properly , but my question was more about floating
point registers. I think in this case they have specialized
registers.
Try godbolt.org. Type in a fragment of code that does different kinds
of casts (it needs to be well-formed, so inside a function), and see
what code is produced with different C compilers.
Use -O0 so that the code isn't optimised out of existence, and so
that you can more easily match it to the C source.
On Thu, 8 Aug 2024 14:23:44 +0100
Bart <[email protected]> wrote:
Try godbolt.org. Type in a fragment of code that does different kinds
of casts (it needs to be well-formed, so inside a function), and see
what code is produced with different C compilers.
Use -O0 so that the code isn't optimised out of existence, and so
that you can more easily match it to the C ource.
I'd recommend an opposite - use -O2 so the cast that does nothingThe OP is curious as to what's involved when a conversion is done.
optimized away.
int foo_i2i(int x) { return (int)x; }
int foo_u2i(unsigned x) { return (int)x; }
int foo_b2i(_Bool x) { return (int)x; }
int foo_d2i(double x) { return (int)x; }
On 08/08/2024 17:32, Michael S wrote:
On Thu, 8 Aug 2024 14:23:44 +0100
Bart <[email protected]> wrote:
Try godbolt.org. Type in a fragment of code that does different kinds
of casts (it needs to be well-formed, so inside a function), and see
what code is produced with different C compilers.
Use -O0 so that the code isn't optimised out of existence, and so
that you can more easily match it to the C ource.
I'd recommend an opposite - use -O2 so the cast that does nothing optimized away.
int foo_i2i(int x) { return (int)x; }The OP is curious as to what's involved when a conversion is done.
int foo_u2i(unsigned x) { return (int)x; }
int foo_b2i(_Bool x) { return (int)x; }
int foo_d2i(double x) { return (int)x; }
Hiding or eliminating code isn't helpful in that case; the results can
also be misleading:
Take this example:
void fred(void) {
_Bool b;
int i;
i=b;
}
Unoptimised, it generates this code:
push rbp
mov rbp, rsp
mov al, byte ptr [rbp - 1]
and al, 1
movzx eax, al
mov dword ptr [rbp - 8], eax
pop rbp
ret
You can see from this that a Bool occupies one byte; it is masked to 0/1
(so it doesn't trust it to contain only 0/1), then it is widened to an
int size.
With optimisation turned on, even at -O1, it produces this:
ret
On 08/08/2024 14:29, Bart wrote:
On 08/08/2024 17:32, Michael S wrote:
On Thu, 8 Aug 2024 14:23:44 +0100The OP is curious as to what's involved when a conversion is done.
Bart <[email protected]> wrote:
Try godbolt.org. Type in a fragment of code that does different kinds >> >> of casts (it needs to be well-formed, so inside a function), and see;
what code is produced with different C compilers.
;
Use -O0 so that the code isn't optimised out of existence, and so
that you can more easily match it to the C ource.
;
;
;
I'd recommend an opposite - use -O2 so the cast that does nothing
optimized away.
;
int foo_i2i(int x) { return (int)x; }
int foo_u2i(unsigned x) { return (int)x; }
int foo_b2i(_Bool x) { return (int)x; }
int foo_d2i(double x) { return (int)x; }
Hiding or eliminating code isn't helpful in that case; the results can
also be misleading:
Take this example:
void fred(void) {
_Bool b;
int i;
i=b;
}
Unoptimised, it generates this code:
push rbp
mov rbp, rsp
mov al, byte ptr [rbp - 1]
and al, 1
movzx eax, al
mov dword ptr [rbp - 8], eax
pop rbp
ret
You can see from this that a Bool occupies one byte; it is masked to
0/1 (so it doesn't trust it to contain only 0/1), then it is widened
to an int size.
With optimisation turned on, even at -O1, it produces this:
ret
That strikes me as rather less enlightening!
Meanwhile your foo_b2i function contains this optimised code:
mov eax, edi
ret
The masking and widening is not present. Presumably, it is taking
advantage of the fact that a _Bool argument will be converted and
widened to `int` at the callsite even though the parameter type is
also _Bool. So the conversion has already been done.
You will see this if writing also a call to foo_b2i() and looking at
the /non-elided/ code.
The unoptimised code for foo_b2i is pretty awful (like masking twice,
with a pointless write to memory between them). But sometimes with gcc
there is no sensible middle ground between terrible code, and having
most of it eliminated.
The unoptimised code from my C compiler for foo_b2i, excluding
entry/exit code, is:
movsx eax, byte [rbp + foo_b2i.x]
My compiler assumes that a _Bool type already contains 0 or 1.
If you are doing constant expression in your compiler, then you have the
same problem (casts) I am solving in cake.
For instance
static_assert((unsigned char)1234 == 210);
is already working in my cake. I had to simulate this cast.
Previously, I was doing all computations with bigger types for constant expressions. Then I realize compile time must work as the runtime.
For constexpr the compiler does not accept initialization invalid types.
for instance.
constexpr char s = 12345;
<source>:6:21: error: constexpr initializer evaluates to 12345 which is
not exactly representable in type 'const char'
6 | constexpr char s = 12345;
I am also checking all wraparound and overflow in constant expressions.
I have a warning when the computed value is different from the math value.
On 08/08/2024 19:29, Bart wrote:
On 08/08/2024 17:32, Michael S wrote:
On Thu, 8 Aug 2024 14:23:44 +0100The OP is curious as to what's involved when a conversion is done.
Bart <[email protected]> wrote:
Try godbolt.org. Type in a fragment of code that does different kinds >> >> of casts (it needs to be well-formed, so inside a function), and see;
what code is produced with different C compilers.
;
Use -O0 so that the code isn't optimised out of existence, and so
that you can more easily match it to the C ource.
;
;
;
I'd recommend an opposite - use -O2 so the cast that does nothing
optimized away.
;
int foo_i2i(int x) { return (int)x; }
int foo_u2i(unsigned x) { return (int)x; }
int foo_b2i(_Bool x) { return (int)x; }
int foo_d2i(double x) { return (int)x; }
Hiding or eliminating code isn't helpful in that case; the results can
also be misleading:
Michael is correct - the OP should enable optimisation, precisely to
avoid the issue you are concerned about. Without optimisation, the
results are misleading - they will only show things that are /not/
involved in the conversion, swamping the useful results with code that
messes about putting data on and off the stack. When optimised
compilation shows that no code is generated, it is a very clear
indication that no operations are needed for the conversions in question
- unoptimized code hides that.
Take this example:
void fred(void) {
_Bool b;
int i;
i=b;
}
Unoptimised, it generates this code:
push rbp
mov rbp, rsp
mov al, byte ptr [rbp - 1]
and al, 1
movzx eax, al
mov dword ptr [rbp - 8], eax
pop rbp
ret
You can see from this that a Bool occupies one byte; it is masked to
0/1 (so it doesn't trust it to contain only 0/1), then it is widened
to an int size.
No, you can't see that. All you can see is garbage in, garbage out. You have to start with a function that has some meaning!
With optimisation turned on, even at -O1, it produces this:
ret
Try again with:
int foo(bool x) { return x; }
bool bar(int x) { return x; }
Try it with -O0 and -O1, and then tell us which you think gives a
clearer indication of the operations needed.
On 08/08/2024 16:42, Keith Thompson wrote:
Thiago Adams <[email protected]> writes:
On 07/08/2024 17:00, Dan Purgert wrote:[...]
On 2024-08-07, Thiago Adams wrote:
How about floating point?Floating point is a huge mess, and has a few variations for
encoding;
though I think most C implementations use the one from the IEEE on 1985 >>>> (uh, IEEE754, I think?)
I didn't specify properly , but my question was more about floating
point registers. I think in this case they have specialized registers.
Who is "they"?
Some CPUs have floating-point registers, some don't. C says nothing
about registers.
What exactly is your question? Is it not already answered by reading
the "Conversions" section of the C standard?
This part is related with the previous question about the origins of
integer promotions.
We don't have "char" register or signed/unsigned register. But I believe
we may have double and float registers. So float does not need to be converted to double.
There is no specif question here, just trying to understand the
rationally behind the conversions rules.
On 08/08/2024 16:42, Keith Thompson wrote:
Thiago Adams <[email protected]> writes:
On 07/08/2024 17:00, Dan Purgert wrote:[...]
On 2024-08-07, Thiago Adams wrote:
How about floating point?Floating point is a huge mess, and has a few variations for
encoding;
though I think most C implementations use the one from the IEEE on 1985 >>>> (uh, IEEE754, I think?)
I didn't specify properly , but my question was more about floating
point registers. I think in this case they have specialized registers.
Who is "they"?
Some CPUs have floating-point registers, some don't. C says nothing
about registers.
What exactly is your question? Is it not already answered by reading
the "Conversions" section of the C standard?
This part is related with the previous question about the origins of
integer promotions.
We don't have "char" register or signed/unsigned register. But I believe
we may have double and float registers. So float does not need to be converted to double.
There is no specif question here, just trying to understand the
rationally behind the conversions rules.
I also curious about how bool works.
Values converted to bool became 0 or 1.
When this conversion happens, at read or write? Both?
On 08/08/2024 18:58, David Brown wrote:
On 08/08/2024 19:29, Bart wrote:
On 08/08/2024 17:32, Michael S wrote:
On Thu, 8 Aug 2024 14:23:44 +0100kinds
Bart <[email protected]> wrote:
Try godbolt.org. Type in a fragment of code that does different
The OP is curious as to what's involved when a conversion is done.of casts (it needs to be well-formed, so inside a function), and see >>> >> what code is produced with different C compilers.;
;
Use -O0 so that the code isn't optimised out of existence, and so
that you can more easily match it to the C ource.
;
;
;
I'd recommend an opposite - use -O2 so the cast that does nothing
optimized away.
;
int foo_i2i(int x) { return (int)x; }
int foo_u2i(unsigned x) { return (int)x; }
int foo_b2i(_Bool x) { return (int)x; }
int foo_d2i(double x) { return (int)x; }
Hiding or eliminating code isn't helpful in that case; the results
can also be misleading:
Michael is correct - the OP should enable optimisation, precisely to
avoid the issue you are concerned about. Without optimisation, the
results are misleading - they will only show things that are /not/
involved in the conversion, swamping the useful results with code that
messes about putting data on and off the stack. When optimised
compilation shows that no code is generated, it is a very clear
indication that no operations are needed for the conversions in
question - unoptimized code hides that.
Take this example:
void fred(void) {
_Bool b;
int i;
i=b;
}
Unoptimised, it generates this code:
push rbp
mov rbp, rsp
mov al, byte ptr [rbp - 1]
and al, 1
movzx eax, al
mov dword ptr [rbp - 8], eax
pop rbp
ret
You can see from this that a Bool occupies one byte; it is masked to
0/1 (so it doesn't trust it to contain only 0/1), then it is widened
to an int size.
No, you can't see that. All you can see is garbage in, garbage out.
You have to start with a function that has some meaning!
Sorry but my function is perfectly valid. It's taking a Bool value and converting it to an int.
Perhaps you don't understand x86 code? I'll tell you: it loads that /byte-sized/ value, masks it, and widens it to an int. I'm surprised you can't see that.
But I suspect a long gaslighting session coming on, where you refute the evidence that everyone else can see!
With optimisation turned on, even at -O1, it produces this:
ret
Try again with:
int foo(bool x) { return x; }
bool bar(int x) { return x; }
Try it with -O0 and -O1, and then tell us which you think gives a
clearer indication of the operations needed.
Michael is wrong and so are you.
If you want to know what casting from bool to int entails, then testing
it via a function call like this is the wrong way to do it, since half kj
of it depends on what happens when evaluating arguments at the call site.
Especially if you let the compiler do what it likes, like using its
knowledge of that call process, which is not displayed here in the
optimised code of the function body.
So I have some questions of you:
* How exactly is a _Bool value (which occupies one byte) translated to a 32-bit signed integer? What is involved?
This is machine independent other than the sizes mentioned.
Given your answer, how does it correlate with either:
mov eax, edi ; from your test; both optimised code
<nothing> ; from my test
The advantage of unoptimised code is that it will contain everything
that is normally involved; it doesn't throw anything away.
It doesn't require convincing the compiler that you're doing something
useful to avoid it eliminating most or all your code, or turning it
something that is just plain misleading.
That might be useful when compiling a huge production version of an app,
but it is useless when trying to shed light on an isolated fragment of
code.
Look, just forget it, I'm not in the mood for another marathon subthread.
So, what's involved in turning Bool to int? According to your examples
with -O1: nothing. You just copy 32 bits unchanged from one to the
other. Mildly surprising, but you are of course right, right?
However, now *I* have a problem, figuring out why on earth C compiler
does the conversion like this:
movsx eax, byte [source]
Because this must be wrong, right?
On 08/08/2024 21:09, Bart wrote:
Sorry but my function is perfectly valid. It's taking a Bool value and
converting it to an int.
No, it is not.
Attempting to use the value of a non-static local variable that has not
been initialised or assigned is undefined behaviour. Your function is garbage. No one can draw any conclusions about how meaningless code is compiled.
If you want to know what casting from bool to int entails, then
testing it via a function call like this is the wrong way to do it,
since half kj
of it depends on what happens when evaluating arguments at the call site.
Nope.
That's fine for the nonsense function you wrote.
The advantage of unoptimised code is that it will contain everything
that is normally involved; it doesn't throw anything away.
No, it does not - because normal code generated by C compilers used by C programmers who want sensible results will be the result of compiling
with optimisation, and will look very different.
So, what's involved in turning Bool to int? According to your examples
with -O1: nothing. You just copy 32 bits unchanged from one to the
other. Mildly surprising, but you are of course right, right?
Yes, I am right - and I don't see that as even mildly surprising here.
That's how you convert a _Bool in a register to an int in a register.
No. You are asking it to do something else - you are asking it to load
a _Bool from memory, not just convert it.
The value of a _Bool object is always either 0 or 1 *unless* the program
does something weird.
But I suspect a long gaslighting session coming on, where you refute the evidence that everyone else can see!
The whole casting concept hails from Algol.
On Thu, 08 Aug 2024 16:14:09 -0700, Keith Thompson wrote:
The value of a _Bool object is always either 0 or 1 *unless* the program
does something weird.
If a variable is declared to be of a particular type, does that mean that
any possible value of that variable is, by definition, interpreted as some instance of that type?
Bart <[email protected]> writes:
[...]
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it clear.
[...]
The *conversion* could be done implicitly, but you've used a cast (i.e.,
an explicit conversion) to make it clear.
There is no such thing as an "implicit cast" in C.
Em 8/8/2024 6:41 PM, Bart escreveu:
On 08/08/2024 21:34, Thiago Adams wrote:This part was always clear to me:
On 08/08/2024 16:42, Keith Thompson wrote:
Thiago Adams <[email protected]> writes:
On 07/08/2024 17:00, Dan Purgert wrote:[...]
On 2024-08-07, Thiago Adams wrote:
Who is "they"?How about floating point?Floating point is a huge mess, and has a few variations for
encoding;
though I think most C implementations use the one from the IEEE on >>>>>> 1985
(uh, IEEE754, I think?)
I didn't specify properly , but my question was more about floating
point registers. I think in this case they have specialized registers. >>>>
Some CPUs have floating-point registers, some don't. C says nothing
about registers.
What exactly is your question? Is it not already answered by reading >>>> the "Conversions" section of the C standard?
This part is related with the previous question about the origins of
integer promotions.
We don't have "char" register or signed/unsigned register. But I
believe we may have double and float registers. So float does not
need to be converted to double.
There is no specif question here, just trying to understand the
rationally behind the conversions rules.
The rules have little to do with concrete machines with registers.
Your initial post showed come confusion about how conversions work.
They are not performed 'in-place', any more than writing `a + 1`
changes the value of `a`.
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it clear. My
C compiler produces intermediate code like this before converting it
to native code:
push x r64 # r64 means float64
fix r64 -> i32
pop a i32
I could choose to interprete this code just as it is. Then, in this
execution model, there are no registers at all, only a stack that can
hold data of any type.
The 'fix' instruction pops the double value from the stack, converts
it to int (which involves changing both the bit-pattern, and the
bit-width), and pushes it back onto the stack.
Registers come into it when running it directly on a real machine. But
you seem more concerned with safety and correctness than performance,
so there's probably no real need to look at actual generated native code.
That'll just be confusing (especially if you follow the advice to
generate only optimised code).
"They are not performed 'in-place', any more than writing `a + 1`
changes the value of `a`."
Lets take double to int.
In this case the bits of double needs to be reinterpreted (copied to) int.
So the answer "how it works" can be
always/generally machine has a instruction to do this
David Brown <[email protected]> writes:
[...]
A _Bool is always either 0 or 1. The conversion is whatever the
compiler needs to give an int of value 0 or 1.
The value of a _Bool object is always either 0 or 1 *unless* the program
does something weird.
C23 is a bit clearer about the representation of bool (still also called _Bool) than previous editions. It states (draft N3220) that :
The type bool shall have one value bit and (sizeof(bool)*CHAR_BIT)-1
padding bits.
There are several ways to force a representation other than 00000000 or 00000001 into a _Bool object, including a union, memset(), or type
punning via a pointer cast.
C23 dropped the term "trap representation", replacing it with "non-value representation" -- a reasonable change, since accessing a trap
representation is not guaranteed to cause the program to "perform a
trap" (defined as "interrupt execution of the program such that no
further operations are performed").
It doesn't specify whether setting the padding bits to 1 results in a non-value representation.
If non-zero padding bits create a non-value representation, then
accessing a bool object holding such a representation has undefined
behavior. It could, among other things, yield the same value implied by
the representation as if it were an ordinary integer of the same size.
If there are no non-value representations, then only the
single value bit determines the value, which is either false or true.
As you mentioned, I expect that sizeof (bool) will normally be 1, but an implementation could make it wider, e.g. with 1 value bit and 31 padding bits.
On 09/08/2024 00:17, Keith Thompson wrote:
Bart <[email protected]> writes:
[...]
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it clear.
[...]
The *conversion* could be done implicitly, but you've used a cast (i.e.,
an explicit conversion) to make it clear.
There is no such thing as an "implicit cast" in C.
Suppose I write this code:
x = a; // implicit 'conversion'
x = (double)a; // explicit 'conversion'
My compiler produces these two bits of AST for the RHS of both expressions:
1 00009 r64---|---2 convert: sfloat_c i32 => r64
1 00009 i32---|---|---1 name: t.main.a.1
1 00010 r64---|---2 convert: sfloat_c i32 => r64
1 00010 i32---|---|---1 name: t.main.a.1
So whatever you call that `(double)` part of the second line, which is written explicitly, exactly the same thing is done internally (ie 'implicitly') to the first line. (The 09/10 are line numbers.)
Since C likes to use the term 'cast' for such conversions, I don't see a problem with talking about implicit and explicit versions.
It just seems to irk the pedantics here.
On 08/08/2024 23:32, David Brown wrote:
On 08/08/2024 21:09, Bart wrote:
Sorry but my function is perfectly valid. It's taking a Bool value
and converting it to an int.
No, it is not.
Attempting to use the value of a non-static local variable that has
not been initialised or assigned is undefined behaviour. Your
function is garbage. No one can draw any conclusions about how
meaningless code is compiled.
FFS. You really think that makes a blind bit of difference?
A variable
is not initialised, so the bool->int code shown must be just random
rubbish generated by the compiler? You think I wouldn't spot if there
was something amiss?
OK, let's initialise it and see what difference it actually makes. My
code is now this:
#include <stdbool.h>
void BC(void) {
_Bool b;
int i;
i=b;
}
void DB(void) {
_Bool b=false;
int i;
i=b;
}
The output from gcc -O1 is this:
BC:
ret
DB:
ret
There is no difference.
So, even initialised, it tells me nothing about
what might be involved in bool->int conversion. It is useless.
Now I'll try it with -O0 (line breaks added):
BC:
push rbp
mov rbp, rsp
movzx eax, BYTE PTR [rbp-1]
mov DWORD PTR [rbp-8], eax
nop
pop rbp
ret
DB:
push rbp
mov rbp, rsp
mov BYTE PTR [rbp-1], 0
movzx eax, BYTE PTR [rbp-1]
mov DWORD PTR [rbp-8], eax
nop
pop rbp
ret
Exactly the same code, except DB has an extra line to initialise that
value.
Are you surprised it is the same? I am 99% sure that you already knew
this, but were pretending that the code was meaningless, for reasons
that escape me.
On 09/08/2024 00:17, Keith Thompson wrote:...
There is no such thing as an "implicit cast" in C.
Suppose I write this code:
x = a; // implicit 'conversion'
x = (double)a; // explicit 'conversion'
My compiler produces these two bits of AST for the RHS of both expressions:
1 00009 r64---|---2 convert: sfloat_c i32 => r64
1 00009 i32---|---|---1 name: t.main.a.1
1 00010 r64---|---2 convert: sfloat_c i32 => r64
1 00010 i32---|---|---1 name: t.main.a.1
So whatever you call that `(double)` part of the second line, which is written explicitly, exactly the same thing is done internally (ie 'implicitly') to the first line. (The 09/10 are line numbers.)
Since C likes to use the term 'cast' for such conversions, ...
... I don't see a
problem with talking about implicit and explicit versions.
Em 8/8/2024 6:41 PM, Bart escreveu:
On 08/08/2024 21:34, Thiago Adams wrote:This part was always clear to me:
On 08/08/2024 16:42, Keith Thompson wrote:
Thiago Adams <[email protected]> writes:
On 07/08/2024 17:00, Dan Purgert wrote:[...]
On 2024-08-07, Thiago Adams wrote:
Who is "they"?How about floating point?Floating point is a huge mess, and has a few variations for
encoding;
though I think most C implementations use the one from the IEEE on >>>>>> 1985
(uh, IEEE754, I think?)
I didn't specify properly , but my question was more about floating
point registers. I think in this case they have specialized registers. >>>>
Some CPUs have floating-point registers, some don't. C says nothing
about registers.
What exactly is your question? Is it not already answered by reading >>>> the "Conversions" section of the C standard?
This part is related with the previous question about the origins of
integer promotions.
We don't have "char" register or signed/unsigned register. But I
believe we may have double and float registers. So float does not
need to be converted to double.
There is no specif question here, just trying to understand the
rationally behind the conversions rules.
The rules have little to do with concrete machines with registers.
Your initial post showed come confusion about how conversions work.
They are not performed 'in-place', any more than writing `a + 1`
changes the value of `a`.
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it clear. My
C compiler produces intermediate code like this before converting it
to native code:
push x r64 # r64 means float64
fix r64 -> i32
pop a i32
I could choose to interprete this code just as it is. Then, in this
execution model, there are no registers at all, only a stack that can
hold data of any type.
The 'fix' instruction pops the double value from the stack, converts
it to int (which involves changing both the bit-pattern, and the
bit-width), and pushes it back onto the stack.
Registers come into it when running it directly on a real machine. But
you seem more concerned with safety and correctness than performance,
so there's probably no real need to look at actual generated native code.
That'll just be confusing (especially if you follow the advice to
generate only optimised code).
"They are not performed 'in-place', any more than writing `a + 1`
changes the value of `a`."
Lets take double to int.
In this case the bits of double needs to be reinterpreted (copied to) int.
So the answer "how it works" can be
always/generally machine has a instruction to do this
or.. this is defined by the IIE ... standard as ...
and I am still curious if _Bool/bool makes the programs slower (more instructions) compared with
"typedef int bool" because the generated code has to convert bool->int int->bool all the time.
Everything is a bit mixed up, but I'll try to explain the part about registers that I have in mind.
In C, when you have an expression like char + char, each char is
promoted to int. The computation then occurs as int + int.
On the other hand, when you have float + float, it remains as float +
float.
My guess for this design is that computations involving char are done
using registers that are the size of an int.
But, float + float is not promoted to double, so I assume that the
computer has specific float registers or similar operation instructions
for float.
Regarding the part about signed/unsigned registers and operations, I
must admit that I'm not sure. I was planning to check on Compiler
Explorer, but I haven't done that yet.
I can frame the question like this: Does the computer make a distinction
when adding signed versus unsigned integers? Are there specific assembly instructions for signed versus unsigned operations, covering all
possible combinations?
Bart <[email protected]> writes:
[...]
Since C likes to use the term 'cast' for such conversions,
C never uses the term "cast" to refer to implicit conversions.
I don't see
a problem with talking about implicit and explicit versions.
It just seems to irk the pedantics here.
That does seem to be your goal.
x = (double)a;
The cast is implicit here but I've written it out to make it clear.
On 09/08/2024 12:04, Bart wrote:
A cast is a piece of syntax that is used to explicitly request that a conversion be performed. Conversions that are explicitly requested in C
code are referred to as casts only by people who don't understand what they're saying - the standard never refers to them as such.
... I don't see a
problem with talking about implicit and explicit versions.
There's nothing wrong with talking about implicit conversions versus
explicit conversions.
Explicit conversion are also called casts.
Bart <[email protected]> writes:
[...]
I didn't choose to muddy the waters.
Yes you did.
Dragging C standard legalese
really doesn't help here.
Yes it does.
You seem to believe, or you're pretending to believe, that using
terminology incorrectly is better than using it correctly, even when
using to correctly is just as easy.
On 09/08/2024 12:04, Bart wrote:
On 09/08/2024 00:17, Keith Thompson wrote:...
There is no such thing as an "implicit cast" in C.
Suppose I write this code:
x = a; // implicit 'conversion'
x = (double)a; // explicit 'conversion'
My compiler produces these two bits of AST for the RHS of both expressions: >>
1 00009 r64---|---2 convert: sfloat_c i32 => r64
1 00009 i32---|---|---1 name: t.main.a.1
1 00010 r64---|---2 convert: sfloat_c i32 => r64
1 00010 i32---|---|---1 name: t.main.a.1
Of course - an implicit conversion has exactly the same effect as a
explicit conversion, if the source and destination types are the same.
That doesn't make it correct to use the term "cast" to describe anything other than an explicit conversion.
James Kuyper <[email protected]> writes:
[...]
A cast is a piece of syntax that is used to explicitly request that a
conversion be performed. Conversions that are explicitly requested in C
code are referred to as casts only by people who don't understand what
they're saying - the standard never refers to them as such.
I think you omitted a "not" in the above, or meant to write "implicitly" rather than "explicitly".
On 09/08/2024 18:57, James Kuyper wrote:...
A cast is a piece of syntax that is used to explicitly request that
a
conversion be performed. Conversions that are explicitly requested in C
code are referred to as casts only by people who don't understand what
they're saying - the standard never refers to them as such.
Are you sure? What else would they be known as?
Here it uses the term 'explicit cast'. Why is that; isn't the term
'cast' unambiguous without needing to say 'explicit'?
Also, what is exactly is the difference between 'explicit conversion'
and 'explicit cast'?
Why can't there also be a similar correlation between 'implicit
conversion' and 'implicit cast'?
even if it did, surely people ought to be allowed to use alternate
terms for an informal discussion? This is a not a committee meeting.
Kaz Kylheku <[email protected]> writes:
[...]
It almost makes sense to speak of "implicit cast" (i.e. coercion) in C,
because of what happens implicitly being so unsafe.
I disagree, because that's not what "cast" means.
On 2024-08-09, James Kuyper <[email protected]> wrote:...
The implicit conversions are implicit precisely because they tend to beOf course - an implicit conversion has exactly the same effect as a
explicit conversion, if the source and destination types are the same.
That doesn't make it correct to use the term "cast" to describe anything
other than an explicit conversion.
That's all very neat and clean. However, the problem is that in C,
some of the implicit conversions are unsafe.
Implicit conversions can:
- truncate an integer value to fit a narrower type.
- convert between floating point and integer in a way that the value
is out of range, with undefined behavior ensuing.
- change the value: e.g -1 becomes UINT_MAX.
- subvert the type system, e.g. (foo *) -> (void *) -> (bar *).
In computer science, we refer to unsafe conversion as coercion.
THe cast notation is C's coercion operation.
It almost makes sense to speak of "implicit cast" (i.e. coercion) in C, because of what happens implicitly being so unsafe.
In any case, while there may be some ambiguity about whether all casts specify conversions, it is unambiguous that an implicit conversion is
not a cast.
Kaz Kylheku <[email protected]> writes:
On 2024-08-09, Keith Thompson <[email protected]> wrote:
Kaz Kylheku <[email protected]> writes:
[...]
It almost makes sense to speak of "implicit cast" (i.e. coercion) in C, >>>> because of what happens implicitly being so unsafe.
I disagree, because that's not what "cast" means.
"cast" means to (try to) project a value into another type.
*Looks around* sorry, are we still in comp.lang.c?
"Cast" has a number of meanings in contexts outside C, applicable to
dice, eyes, fishing lines, ballots, magic spells, actors, liquid metal,
and broken limbs, among other things. In C, it means what the standard
says it means, even if some people misuse it to refer to implicit conversions.
In C though, the nuance is something like "conversion that is mediated
by the presence of the cast notation", where "mediated" includes the
possibility that the cast notation has no effect at all
(e.g. 2 + (int) 3).
I hadn't noticed before that the standard does have a formal definition
of the term "cast", as well as "explicit conversion" and "implicit conversion".
Bart <[email protected]> writes:
On 09/08/2024 18:57, James Kuyper wrote:
On 09/08/2024 12:04, Bart wrote:
A cast is a piece of syntax that is used to explicitly request that
a
conversion be performed. Conversions that are explicitly requested in C
code are referred to as casts only by people who don't understand what
they're saying - the standard never refers to them as such.
Are you sure? What else would they be known as?
I believe James made a small error here, either omitting a "not" or accidentally writing "explicitly" rather than "implicitly".
With that correction, I presume what James wrote is clear enough.
I ask you not to pretend that you still don't understand it.
I ask you not to pretend that you still don't understand it.
[...]
Here it uses the term 'explicit cast'. Why is that; isn't the term
'cast' unambiguous without needing to say 'explicit'?
It's redundant.
Also, what is exactly is the difference between 'explicit conversion'
and 'explicit cast'?
The both mean the same thing in C, but "explicit cast" is redundant.
Why can't there also be a similar correlation between 'implicit
conversion' and 'implicit cast'? The only reason I can see is that out
of these four terms, only 3 of them happen to appear in the standard.
Because a cast is an explicit operator.
I don't see that as a compelling reason why that term should be
considered absolutely wrong; 'implicit cast' just never came up.
It's not as though the standard provides an official glossary,
In fact it does. Some terms are defined in section 3, and others are
defined elsewhere in the standard (definitions are denoted by italics).
I don't see a definition for the term "cast", but it's clearly described
in N3220 6.5.4 "Cast operators".
Array indexing is defined in terms of pointer arithmetic. The
evaluation of `a[i]` involves an implicit addition operation. It does
not involve an implicit "+" symbol.
You're allowed to write whatever nonsense you like, and the rest of us
are allowed to tell you that you're wrong.
Explain why using the word "cast" incorrectly is better than using it correctly. Explain why you can't just refer to explicit and implicit conversions. Do you have a motivation other than being annoying?
I remember people here getting castigated for using the term 'type
cast'. And yet, in H.2.4p1:
"The LIA−1 type conversions are the following type casts:"
That wording is no longer there in more recent editions. Annex H refers
to LIA-1 (Language Independent Arithmetic), ISO/IEC 10967–1; perhaps
that standard use the term "type casts".
On 2024-08-09, Keith Thompson <[email protected]> wrote:...
Kaz Kylheku <[email protected]> writes:
..."cast" means to (try to) project a value into another type.
I hadn't noticed before that the standard does have a formal definition
of the term "cast", ...
Based on surveying all you quoted, I basically nailed it above.
On 09/08/2024 02:56, Bart wrote:
On 08/08/2024 23:32, David Brown wrote:
On 08/08/2024 21:09, Bart wrote:
Sorry but my function is perfectly valid. It's taking a Bool value
and converting it to an int.
No, it is not.
Attempting to use the value of a non-static local variable that has
not been initialised or assigned is undefined behaviour. Your
function is garbage. No one can draw any conclusions about how
meaningless code is compiled.
FFS. You really think that makes a blind bit of difference?
Yes, I do.
A variable is not initialised, so the bool->int code shown must be
just random rubbish generated by the compiler? You think I wouldn't
spot if there was something amiss?
You wrote C code that had something amiss!
How often are you going to do this? Write some piece of meaningless
crap with undefined behaviour or - at best - no observable behaviour at
all, compile with silly choices of flags, and then make nonsensical
claims about what compilers do or how C is defined?
void DB(void) {
_Bool b=false;
int i;
i=b;
}
There is a difference in the code. One (BC) has no defined behaviour.
The other (DB) has defined behaviour with no observable behaviour.
not a surprise that a compiler generates no code for either of them.
So, even initialised, it tells me nothing about what might be involved
in bool->int conversion. It is useless.
Agreed. Nobody suggested your code above as a good idea.
Now I'll try it with -O0 (line breaks added):
BC:
push rbp
mov rbp, rsp
movzx eax, BYTE PTR [rbp-1]
mov DWORD PTR [rbp-8], eax
nop
pop rbp
ret
DB:
push rbp
mov rbp, rsp
mov BYTE PTR [rbp-1], 0
movzx eax, BYTE PTR [rbp-1]
mov DWORD PTR [rbp-8], eax
nop
pop rbp
ret
Exactly the same code, except DB has an extra line to initialise that
value.
Are you surprised it is the same? I am 99% sure that you already knew
this, but were pretending that the code was meaningless, for reasons
that escape me.
Instead of trolling with what you know, without doubt, are pointless
straw men, why not apply a little thought and write functions that make sense?
I'm giving up trying to help you - at least until you show some hint of trying to learn.
I will still make posts pointing out when you write
nonsense that might confuse or mislead others, but I'll stop trying to explain things unless you specifically ask.
On 09/08/2024 20:54, Thiago Adams wrote:
I don't know what you are referring to here. But if you are using
compiler explorer, I encourage you to look at the generated output for a
wide range of targets, including 8-bit AVR, 16-bit MSP430, 32-bit ARM,
and 64-bit x86. Use gcc -O1 or -O2 in every case.
ignorant blatherings about optimisation.)
Em 8/10/2024 7:17 AM, Bart escreveu:
On 09/08/2024 21:01, David Brown wrote:
On 09/08/2024 20:54, Thiago Adams wrote:
I don't know what you are referring to here. But if you are using
compiler explorer, I encourage you to look at the generated output
for a wide range of targets, including 8-bit AVR, 16-bit MSP430,
32-bit ARM, and 64-bit x86. Use gcc -O1 or -O2 in every case.
When would you choose -O2 over -O1 or vice versa? Could a similar
circumstance cause you to choose -O0? Why not -O3?
In fact, why is there a -O0 option at all?
(Ignore Bart's
ignorant blatherings about optimisation.)
[To TA:]
Yes do. But don't complaint to me when your test code results in
meaningless or misleading output, or no output at all.
Actually, I would recommend looking at both (eg. -O0 and -O1) so that
you can see if the compiler's optimiser has been over-zealous in
eliminating code, or has chopped out key bits, so that you might
modify your test code.
I would recommend also looking at the Tiny C option on godbolt when
comparing x86 code.
Bart, Does your compiler support the `bool` type, where the value is
always either 1 or 0?
Em 8/10/2024 9:10 PM, Keith Thompson escreveu:
Thiago Adams <[email protected]> writes:
Em 8/10/2024 1:14 PM, Bart escreveu:
There is a bool type, but it is treated like unsigned char, so is
Bart, Does your compiler support the `bool` type, where the value
is always either 1 or 0?
non-conforming.
I do the same in my compiler , when I transpile from C99 to C89.
I was thinking how to make it conforming.
For instance on each write.
bool b = 123; -> unsigned char b = !!(123);
The problem this does not fix unions, writing on int and reading from
char.
I don't think you need to fix that.
[....]
Summary:
Conversion from any scalar type to _Bool is well defined, and must yield
0 or 1.
I will fix in terns of expressions types.
- In this case cast to bool
- Assignment to bool
It's possible to force a representation other than 0 or 1 into a _Bool
object, bypassing any value conversion.
Conversion from _Bool to any scalar type is well defined if the
operand is a _Bool object holding a representation of 0 or 1.
Conversion from _Bool to any scalar type for an object holding some
representation other than 0 or 1 either yields 0 or 1 (depending
on the low-order bit) or has undefined behavior.
I did a sample now..
#include <stdio.h>
int main() {
union {
int i;
_Bool b;
} data;
data.i = 123;
printf("%d", data.b);
}
it printed 123 not 1.
So I think the assignment and cast covers all/most cases.
(From some previous tests I thought this was printing 1)
Thiago Adams <[email protected]> writes:
...
I also curious about how bool works.
Values converted to bool became 0 or 1.
When this conversion happens, at read or write? Both?
You can take a value obtained by reading an object, or a value produced
by evaluating an expression, and convert that value to a different type.
That value can later be stored in an object, or it could be used as one
of the operands for an expression. The conversion isn't associated with either the read or the write. Many conversions occur implicitly, a cast
is used to explicitly make a conversion occur.
int x = 3;
bool b = x;
In the above code, an implicit conversion from int to bool occurs after reading the value of 3 from x, and occurs before writing to bool.
b = !(bool)(x-3);
In this code, the conversion occurs after the value of 3 is retrieved
from x, and after 3 is subtracted from it. That result of 0 is then converted to bool, and then the ! operator is applied to it. Finally,
the result is written to b. So you see, it doesn't make sense to connect
the conversion with either the read or the write.
David Brown <[email protected]> writes:
On 09/08/2024 01:14, Keith Thompson wrote:
David Brown <[email protected]> writes:
[...]
A _Bool is always either 0 or 1. The conversion is whatever the
compiler needs to give an int of value 0 or 1.
The value of a _Bool object is always either 0 or 1 *unless* the
program does something weird.
True. But attempting to use a _Bool object (as a _Bool) that does not
contain either 0 or 1 is going to be undefined behaviour (at least it
was on the platform where I saw this happen as a code bug).
It depends on whether representations with non-zero padding bits are
treated as trap representations (non-value representations in C23) or
not.
It doesn't specify whether setting the padding bits to 1 results in a
non-value representation.
That's probably an implementation-defined issue, is it not?
I'm not sure whether it's implementation-defined or unspecified.
I don't see any mention of trap/non-value representations in Annex J.
[...]
On 2024-08-09, Keith Thompson <[email protected]> wrote:
Kaz Kylheku <[email protected]> writes:
[...]
It almost makes sense to speak of "implicit cast" (i.e. coercion) in C,
because of what happens implicitly being so unsafe.
I disagree, because that's not what "cast" means.
"cast" means to (try to) project a value into another type.
In computer science, we refer to unsafe conversion as coercion.
THe cast notation is C's coercion operation.
I also would like to understand better why integer promotions were created.
My guess..it is because the values are used in registers and there is
no "char" size register so the values are converted in a bigger type.
Is my guess correct?
Bart <[email protected]> writes:
[...]
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it clear.
[...]
The *conversion* could be done implicitly, but you've used a cast (i.e.,
an explicit conversion) to make it clear.
Keith Thompson <[email protected]> writes:
Bart <[email protected]> writes:
[...]
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it clear.
[...]
The *conversion* could be done implicitly, but you've used a cast (i.e.,
an explicit conversion) to make it clear.
The statement assigning to x performs two conversions: an explicit
one caused by the cast, and an implicit one caused by the assignment operation.
On 12/08/2024 01:46, Tim Rentsch wrote:
Keith Thompson <[email protected]> writes:
Bart <[email protected]> writes:
[...]
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it clear.
[...]
The *conversion* could be done implicitly, but you've used a cast (i.e., >>> an explicit conversion) to make it clear.
The statement assigning to x performs two conversions: an explicit
one caused by the cast, and an implicit one caused by the assignment
operation.
The 'x' term is the other side of the cast from the 'a' term.
So after '(double)a' has been evaluated, both sides of '=' have the
type 'double', so no further conversion is needed.
On 09/08/2024 00:17, Keith Thompson wrote:
Bart <[email protected]> writes:
[...]
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it
clear.
Since C likes to use the term 'cast' for such conversions, [...]
It just seems to irk the pedantics here.
Thiago Adams <[email protected]> writes:
I also would like to understand better why integer promotions were created. >>
My guess..it is because the values are used in registers and there is
no "char" size register so the values are converted in a bigger type.
Is my guess correct?
The motivations for integer promotion rules are purely historical.
You're looking for answers in the wrong places.
Also, it would be better for your understanding of C if you would
stop thinking about what is going on at the level of actual
hardware. Doing that serves to confuse a lot more than it helps.
Thiago Adams <[email protected]> writes:
- In this case cast to bool
- Assignment to bool
You need to cover all cases where a scalar value is converted to _Bool.
That includes (explicit) casts,
Bart <[email protected]> writes:
On 09/08/2024 00:17, Keith Thompson wrote:
Bart <[email protected]> writes:
[...]
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it
clear.
[...]
Since C likes to use the term 'cast' for such conversions, [...]
The C standard uses the term 'cast' only for explicit conversions,
and never uses the term 'cast' for implicit conversions. You
should do the same.
It just seems to irk the pedantics here.
What bothers people is not you using the wrong terminology. What
bothers people is you being a self-centered jerk, and deliberately
using incorrect terminology just to annoy people.
Incidentally, the word "pedantic" is an adjective. The noun form
is "pedant". People who complain about you using terminology
incorrectly are not being pedants.
They simply are offended by
your never-ending efforts to be a pest and an asshole.
On 09/08/2024 12:04, Bart wrote:
On 09/08/2024 00:17, Keith Thompson wrote:
Bart <[email protected]> writes:
[...]
Take:
int a; double x;
x = (double)a;
The cast is implicit here but I've written it out to make it clear.
[...]
The *conversion* could be done implicitly, but you've used a cast (i.e., >>> an explicit conversion) to make it clear.
There is no such thing as an "implicit cast" in C.
Suppose I write this code:
x = a; // implicit 'conversion' >> x = (double)a; // explicit 'conversion'
My compiler produces these two bits of AST for the RHS of both
expressions:
1 00009 r64---|---2 convert: sfloat_c i32 => r64
1 00009 i32---|---|---1 name: t.main.a.1
1 00010 r64---|---2 convert: sfloat_c i32 => r64
1 00010 i32---|---|---1 name: t.main.a.1
So whatever you call that `(double)` part of the second line, which is
written explicitly, exactly the same thing is done internally (ie
'implicitly') to the first line. (The 09/10 are line numbers.)
You've written it yourself. Both are conversions - one is implicit, the other is explicit.
Since C likes to use the term 'cast' for such conversions, I don't see
a problem with talking about implicit and explicit versions.
C does not "like" to use the term "cast" for anything other than cast operations, as defined by the C standards. Implicit conversions are not casts.
/You/ might like to call implicit conversions "casts", but you'd be
wrong to do so.
It just seems to irk the pedantics here.
You mean, people who know what they are talking about rather than those
that make up stuff as they go along?
Tim Rentsch <[email protected]> writes:
[...]
Furthermore, there are cases where having to do a conversion from
one type to the same type has semantic consequences, even though
the types are the same.
What are these cases?
Keith Thompson <[email protected]> writes:
Tim Rentsch <[email protected]> writes:
[...]
Furthermore, there are cases where having to do a conversion from
one type to the same type has semantic consequences, even though
the types are the same.
What are these cases?
The case that comes to mind is where the result of a floating point expression is represented with more precision than the type can hold
when stored.
6.3.1.8 p2:
The values of floating operands and of the results of floating
expressions may be represented in greater range and precision than
that required by the type; the types are not changed thereby.[63]
The footnote explains that the implicit conversion done on assignment
will change the value even though the types may be the same:
[63] The cast and assignment operators are still required to remove
extra range and precision.
Bart <[email protected]> writes:
Also, what is exactly is the difference between 'explicit conversion'
and 'explicit cast'?
None
On 10/08/2024 00:58, Keith Thompson wrote:
In any case, while there may be some ambiguity about whether all casts
specify conversions, it is unambiguous that an implicit conversion is
not a cast.
Here are some comments from the Tiny C source code:
/* XXX: implicit cast ? */
/* compute bigger type and do implicit casts */
This is from some gcc code:
..and some compilers cast it to int implicitly ...
Come on, everybody's at it. Unless we're trying do to a reference
document, then /it really doesn't matter/.
Bart <[email protected]> writes:
Why can't there also be a similar correlation between 'implicit
conversion' and 'implicit cast'?
The C standard defines "implicit conversion" and "explicit conversion"
in 6.3p1, and the definition it provides for "explicit conversion" is
"those [conversions] that result from a cast operation". it provides a grammar production for a cast expression, and none for a implicit cast expression.
even if it did, surely people ought to be allowed to use alternate
terms for an informal discussion? This is a not a committee meeting.
Every time you use a term with a standard-define meaning in a way that doesn't match the meaning defined for it by the standard, you create potential confusion. If that's what you want to do, go ahead, but it
seems an odd thing to do.
Bart <[email protected]> writes:...
Why can't there also be a similar correlation between 'implicit
conversion' and 'implicit cast'?
The C standard defines "implicit conversion" and "explicit conversion"
in 6.3p1, and the definition it provides for "explicit conversion" is
"those [conversions] that result from a cast operation". it provides a grammar production for a cast expression, and none for a implicit cast expression.
On 8/9/24 18:29, James Kuyper wrote:
Bart <[email protected]> writes:...
Why can't there also be a similar correlation between 'implicit
conversion' and 'implicit cast'?
The C standard defines "implicit conversion" and "explicit conversion"
in 6.3p1, and the definition it provides for "explicit conversion" is
"those [conversions] that result from a cast operation". it provides a
grammar production for a cast expression, and none for a implicit cast
expression.
Note, in particular, that if there were such a thing as an implicit
cast, then, as the name implies, an implicit cast would qualify as a
cast. Since, according to the above definition, all conversions that
result from casts would qualify as "explicit conversions", that would
include conversions that result from implicit casts. In other words, all conversions would be explicit conversions. In addition, per 6.3p1, the conversions described in 6.3 would also be implicit conversions, which
is just plain ridiculous.
Bart <[email protected]> writes:
[...]
I get that the standard mainly intends 'cast' to mean that bit of
syntax, '(T)X', by which you can force a particular conversion.
No, the standard *exclusively* uses "cast" to mean that bit of syntax
(except in Annex H, but those incorrect uses of the word are corrected
in C23). But of course you know that.
So, for all this fuss about 'cast' in the Standard, it isn't even
a reserved word in C itself.
Bart <[email protected]> writes:
So, for all this fuss about 'cast' in the Standard, it isn't even
a reserved word in C itself.
Many or most of the defined terms in the C standard are not
keywords or reserved words in the C language.
On 14/08/2024 00:46, Tim Rentsch wrote:
Bart <[email protected]> writes:
So, for all this fuss about 'cast' in the Standard, it isn't
even a reserved word in C itself.
Many or most of the defined terms in the C standard are not
keywords or reserved words in the C language.
My point was, if 'cast' /was/ a reserved word, then you'd have to
get it just right with no quibbling.
But it isn't. A bit silly to apply just as strict rules to humans
discussing topics in English.
Tim Rentsch <[email protected]> writes:
Keith Thompson <[email protected]> writes:
David Brown <[email protected]> writes:
On 09/08/2024 01:14, Keith Thompson wrote:
David Brown <[email protected]> writes:
[...]
A _Bool is always either 0 or 1. The conversion is whatever
the compiler needs to give an int of value 0 or 1.
The value of a _Bool object is always either 0 or 1 *unless* the
program does something weird.
True. But attempting to use a _Bool object (as a _Bool) that
does not contain either 0 or 1 is going to be undefined behaviour
(at least it was on the platform where I saw this happen as a
code bug).
It depends on whether representations with non-zero padding bits
are treated as trap representations (non-value representations in
C23) or not.
In C99 and C11, iirc, the width of _Bool may be any value between 1
and CHAR_BIT. If the width of _Bool is greater than 1, a _Bool may
have a well-defined value that is neither 0 or 1. My guess is most
implementations define the width of _Bool as 1, but they don't have
to (again, iirc, in C99 and C11).
C11 (N1570) isn't 100% clear, but I think you're right. The
conversion rank of _Bool is less than the rank of the char types.
I don't see an explicit statement that this implies that _Bool has
less precision than unsigned char, [...]
It doesn't specify whether setting the padding bits to 1 results
in a non-value representation.
That's probably an implementation-defined issue, is it not?
I'm not sure whether it's implementation-defined or unspecified.
I don't see any mention of trap/non-value representations in Annex
J.
[...]
6.2.6.1 p 2;
So it's implementation-defined. [...]
Quoting the standard so that everyone else doesn't have to go look
it up (and guess which edition you're referring to). You might
consider doing that yourself.
| Sysop: | Keyop |
|---|---|
| Location: | Huddersfield, West Yorkshire, UK |
| Users: | 715 |
| Nodes: | 16 (2 / 14) |
| Uptime: | 145:27:54 |
| Calls: | 12,089 |
| Calls today: | 2 |
| Files: | 15,000 |
| Messages: | 6,517,497 |