It’s a misconception that casts are always free in C.
Yes, many particular casts are free on common architectures: 65 has the same representation as 'A' and x86 doesn’t have a type system; casting is free.
That does not generalise: for example, casting from float or double to int has the same semantics as flooring the value, because floating-point numbers are represented entirely differently from integers – with the added bonus of UB around the extremes.
Honestly, casts are a wart on every C-like language I know. They should just be functions.
float is a special kind of fun, because it’s also UB if a float to int cast overflows. Rust had to add a range check to float casts to close this loophole in LLVM.
Most of the time, C isn’t casting to bool (actually, _Bool, if bool exists in C then it’s a typedef provided by stdbool.h). Prior to C99 the language didn’t have an explicit Boolean type and let you use any integer for logical expressions (I think you can also use floats but that causes some special excitement). In C, zero is false and non-zero is true. This is a simple choice for code generation because most architectures have a cheap branch if [not] zero sequence. Then a Boolean type was introduced, you needed to be able to compare it against true and so a _Bool is an integer type that must hold either false (which, I think, must be zero) or true (which must be some implementation-defined non-zero value and is 1 in every implementation that I’ve seen), it is undefined behaviour for it to hold any other value.
C++ had a bool type from the start and so if means something slightly different in C++. if(x) in C means ‘do this if x is not zero’, but in C++ means ‘convert x to bool and do the following statement if the result is true’. For all of the types that C and C++ share, these are equivalent but C++ user-defined types can also implement their own convert to bool operator. For example, std::optional uses it to return whether it holds the value. I typically dislike this style because implicit casts are a great way to hide subtle details from people reviewing your code.
It’s a misconception that casts are always free in C.
Yes, many particular casts are free on common architectures:
65
has the same representation as'A'
and x86 doesn’t have a type system; casting is free. That does not generalise: for example, casting fromfloat
ordouble
toint
has the same semantics asfloor
ing the value, because floating-point numbers are represented entirely differently from integers – with the added bonus of UB around the extremes.Honestly, casts are a wart on every C-like language I know. They should just be functions.
float is a special kind of fun, because it’s also UB if a float to int cast overflows. Rust had to add a range check to float casts to close this loophole in LLVM.
And in one of the examples, it performs sign extension, so the bit pattern can actually change.
Most of the time, C isn’t casting to bool (actually, _Bool, if bool exists in C then it’s a typedef provided by stdbool.h). Prior to C99 the language didn’t have an explicit Boolean type and let you use any integer for logical expressions (I think you can also use floats but that causes some special excitement). In C, zero is false and non-zero is true. This is a simple choice for code generation because most architectures have a cheap branch if [not] zero sequence. Then a Boolean type was introduced, you needed to be able to compare it against true and so a _Bool is an integer type that must hold either false (which, I think, must be zero) or true (which must be some implementation-defined non-zero value and is 1 in every implementation that I’ve seen), it is undefined behaviour for it to hold any other value.
C++ had a bool type from the start and so if means something slightly different in C++. if(x) in C means ‘do this if x is not zero’, but in C++ means ‘convert x to bool and do the following statement if the result is true’. For all of the types that C and C++ share, these are equivalent but C++ user-defined types can also implement their own convert to bool operator. For example, std::optional uses it to return whether it holds the value. I typically dislike this style because implicit casts are a great way to hide subtle details from people reviewing your code.
bool is no longer a typedef in C23. _Bool’s true and false values were always required to be 1 and 0 (not implementation-defined).
Huh, I missed that, though that does better align with c++. Did it introduce rules related to implicit conversions?