In the code block below what's the impicit conversion that takes place in the if statement for 7? I would have though it would end up being (0x98 <= 0x07) but that's not what happens as the condition evaluates to true and DoMyStuff gets called.
char minstogo = 0x98;
if(minstogo <= 7) {
DoMyStuff();
}
Whenever you have a binary operator (one of + - * / % << >> & | ^ == != < <= > >=) between two integral operands of different types, the two types are converted to a common type before the operation is performed. The rules for deciding the converted-to type are (from section 6.3.1.8 of the C99 standard):
If both operands have the same type, then no further conversions are required.
Otherwise, if both operands have signed integer types or both have unsigned
integer types, the operand with the type of lesser integer conversion rank is
converted to the type of the operand with greater rank.
Otherwise, if the operand that has unsigned integer type has rank greater or
equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned
integer type.
Otherwise, if the type of the operand with signed integer type can represent
all of the values of the type of the operand with unsigned integer type, then
the operand with unsigned integer type is converted to the type of the
operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type
corresponding to the type of the operand with signed integer type.
In this case, char could be either a signed or unsigned integer type -- its signedness is implementation-defined. Fortunately, though, an int can represent all possible values of a char, whether or not char is signed, assuming you're on a system where chars are 8 bits and ints are at least 16 bits.
If char is signed, then the second paragraph above applies, so both operands are converted to int (the type with higher rank; rank is defined in a somewhat complicated manner, but it's essentially equivalent to the bit size of the type). Since 0x98, as a signed char, is negative, it's converted to the integer -104, which is then less than 7.
If instead char is unsigned, then the fourth paragraph applies instead. The unsigned char would be converted to 152 as an int, which is greater than 7.
Do not ever rely on chars being signed or unsigned. If you need 8-bit integers of a certain signedness, explicitly use signed char or unsigned char, or use the C99 types int8_t and uint8_t, defined int <stdint.h>.
It's very easy to get bitten by subtle bugs caused by the integer promotion rules. I strongly advise you to always compile with -Wall with gcc, which will warn you about comparisons between signed and unsigned integers, which are frequently a cause of bugs.
What's likely happening here is that char is a signed value and 0x98 is hence registering as a negative number. Hence it is less than 7
Also in this scenario, 7 will under go no conversion. Instead the char will be widened to the same integral type as 7 and then a comparison will be done.
0x98 is 152.
Since you've declared a "char", and not an "unsigned char", you're trying to assign 152 to a type that has a range of -128 - 127.
This is going to overflow, and give you a negative number, which will be < 7 (0x07).
With chars being represented as an eight-bit byte, setting minstogo to 0x98 is a binary value of 10011000. The sign bit is set, it's a negative integer value. You probably want an unsigned char in order for the test to evaluate false.
It will evaluate the same as 0x98 <= 7 unless the platform's char type defaults to signed and CHAR_BIT is 8. In that case, the value of minstogo will be negative and minstogo <= 7 will be true.
Using your compiler with its current settings, char is a signed type: and because the high order bit (0x80) of its value is set, that value is negative. When minstogo is widened, that negative sign is preserved (via sign-extension), and so minstogo is widened to a negative integer (e.g. 0xFF98), which is less than 7.
Related
starting with a pseudo-code snippet:
char a = 0x80;
unsigned short b;
b = (unsigned short)a;
printf ("0x%04x\r\n", b); // => 0xff80
to my current understanding "char" is by definition neither a signed char nor an unsigned char but sort of a third type of signedness.
how does it come that it happens that 'a' is first sign extended from (maybe platform dependent) an 8 bits storage to (a maybe again platform specific) 16 bits of a signed short and then converted to an unsigned short?
is there a c standard that determines the order of expansion?
does this standard guide in any way on how to deal with those third type of signedness that a "pure" char (i called it once an X-char, x for undetermined signedness) so that results are at least deterministic?
PS: if inserting an "(unsigned char)" statement in front of the 'a' in the assignment line, then the result in the printing line is indeed changed to 0x0080. thus only two type casts in a row will provide what might be the intended result for certain intentions.
The type char is not a "third" signedness. It is either signed char or unsigned char, and which one it is is implementation defined.
This is dictated by section 6.2.5p15 of the C standard:
The three types char , signed char , and unsigned char are
collectively called the character types. The implementation
shall define char to have the same range, representation, and
behavior as either signed char or unsigned char.
It appears that on your implementation, char is the same as signed char, so because the value is negative and because the destination type is unsigned it must be converted.
Section 6.3.1.3 dictates how conversion between integer types occur:
1 When a value with integer type is converted to another integer type
other than
_Bool ,if the value can be represented by the new type, it is unchanged.
2 Otherwise, if the new type is unsigned, the value is
converted by repeatedly adding or subtracting one more than
the maximum value that can be represented in the new type
until the value is in the range of the new type.
3 Otherwise, the new type is signed and the value cannot be
represented in it; either the result is implementation-defined or
an implementation-defined signal is raised.
Since the value 0x80 == -128 cannot be represented in an unsigned short the conversion in paragraph 2 occurs.
char has implementation-defined signedness. It is either signed or unsigned, depending on compiler. It is true, in a way, that char is a third character type, see this. char has an indeterministic (non-portable) signedness and therefore should never be used for storing raw numbers.
But that doesn't matter in this case.
On your compiler, char is signed.
char a = 0x80; forces a conversion from the type of 0x80, which is int, to char, in a compiler-specific manner. Normally on 2's complement systems, that will mean that the char gets the value -128, as seems to be the case here.
b = (unsigned short)a; forces a conversion from char to unsigned short 1). C17 6.3.1.3 Signed and unsigned integers then says:
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or
subtracting one more than the maximum value that can be represented in the new type
until the value is in the range of the new type.
One more than the maximum value would be 65536. So you can think of this as -128 + 65536 = 65408.
The unsigned hex representation of 65408 is 0xFF80. No sign extension takes place anywhere!
1) The cast is not needed. When both operands of = are arithmetic types, as in this case, the right operand is implicitly converted to the type of the right operand (C17 6.5.16.1 ยง2).
I was reading an article on usage of size_t and ptrdiff_t data types here, when I came across this example:
The code:
int A = -2;
unsigned B = 1;
int array[5] = { 1, 2, 3, 4, 5 };
int *ptr = array + 3;
ptr = ptr + (A + B); //Error
printf("%i\n", *ptr);
I am unable to understand a couple of things. First, how can adding a signed and an unsigned number cast the enter result into unsigned type? If the result is indeed 0xFFFFFFFF of unsigned type, why in a 32 bit system, while adding it with ptr, will it be interpreted as ptr-1, given that the number is actually unsigned type and the leading 1 should not signify sign?
Second, why is the result different in 64 bit system?
Can anyone explain this please?
1. I am unable to understand a couple of things. First, how can adding a signed and an unsigned number cast the enter result into unsigned type?
This is defined by integer promotions and integer conversion rank.
6.3.1.8 p1: Otherwise, if the operand that has unsigned integer type has rank greater or
equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned
integer type.
In this case unsigned has a higher rank than int, therefore int is promoted to unsigned.
The conversion of int ( -2 ) to unsigned is performed as described:
6.3.1.3 p2: Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or
subtracting one more than the maximum value that can be represented in the new type
until the value is in the range of the new type
2. If the result is indeed 0xFFFFFFFF of unsigned type, why in a 32 bit system, while adding it with ptr, will it be interpreted as ptr-1, given that the number is actually unsigned type and the leading 1 should not signify sign?
This is undefined behavior and should not be relied on, since C doesn't define pointer arithmetic overflow.
6.5.6 p8: If both the pointer
operand and the result point to elements of the same array object, or one past the last
element of the array object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined.
3. Second, why is the result different in 64 bit system?
( This assumes( as does the picture ) that int and unsigned are 4 bytes. )
The result of A and B is the same as described in 1., then that result is added to the pointer. Since the pointer is 8 bytes and assuming the addition doesn't overflow( it still could if ptr had a large address, giving the same undefined behavior as in 2. ) the result is an address.
This is undefined behavior because the pointer points way outside of the bounds of the array.
The operands of the expression A + B are subject to usual arithmetic conversion, covered in C11 (n1570) 6.3.1.8 p1:
[...]
Otherwise, the integer promotions [which leave int and unsigned int unchanged] are performed on both operands. Then the following rules are applied to the promoted operands:
If both operands have the same type, [...]
Otherwise, if both operands have signed integer types or both have unsigned integer types, [...]
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
[...]
The types int and unsigned int have the same rank (ibid. 6.3.1.1 p1, 4th bullet); the result of the addition has type unsigned int.
On 32-bit systems, int and pointers usually have the same size (32 bit). From a hardware-centric point of view (and assuming 2's complement), subtracting 1 and adding -1u is the same (addition for signed and unsigned types is the same!), so the access to the array element appears to work.
However, this is undefined behaviour, as array doesn't contain a 0x100000003rd element.
On 64-bit, int usually has still 32 bit, but pointers have 64 bit. Thus, there is no wraparound and no equivalence to subtracting 1 (from a hardware-centric point of view, the behaviour is undefined in both cases).
To illustrate, say ptr is 0xabcd0123, adding 0xffffffff yields
abcd0123
+ ffffffff
1abcd0122
^-- The 1 is truncated for a 32-bit calculation, but not for 64-bit.
On most 64bits, int are 32bits, but on 32bits systems, pointers are also 32bits.
Recall that in 32bits arithmetic - on two's complement based hardware, adding 0xFFFFFFFF is almost the same as subtracting 1: it overflows and you get that same number minus 1 (it's the same phenomenon when you add 9 to a number between 0 and 9, you get than number minus 1 and a carry). On that type of hardware, the encoding of -1 is actually that same value 0xFFFFFFFF, only the operation is different (signed add versus unsigned add), and so a carry will be produced in the unsigned case.
On 64bits pointers are... 64bits. Adding a 32bits value to a 64bits one requires to extend that 32bit value to 64. unsigned values are zero extended (ie. the missing bits are just filled with zeros), while signed values are sign extended (ie. missing bits are filled with the sign bit value).
In this case, adding an unsigned value (which will therefore not be sign extended) will not overflow, thus yielding a very different value from the original.
signed short Temp;
Temp = 0xF2C9;
Temp2 = 0x100;
unsigned char a;
a = (unsigned char)(Temp/((unsigned short)Temp2));
What will be the expected output?
As per my understanding due to "Usual Arithmetic Conversions" first Temp should be converted into unsigned shortand result in a should be 0xF2, but I am getting the response 0xF3 which means operation is performed with signed value of Temp. Please explain the behavior.
Is endianess also relevant in this scenario?
No, first all arguments to arithmetic operators are promoted, that is narrow types, such as your shorts are converted to int. (at least on all common architectures).
Assuming short is 16 bit wide on your system, the initialization of Temp is implementation defined because the value of 0xF2C9 doesn't fit to the type. Most probably it is a negative value. Then, for the computation, that negative signed short value is promoted to int. The result of the division is a negative value, which then in turn is converted to unsigned char.
It depends if INT_MAX >= USHRT_MAX
The "Usual Arithmetic Conversions" convert Temp into (int) Temp. This will only "extend the sign" if int is wider than short.
((unsigned short)Temp2) is promoted to (int)((unsigned short)Temp2) or (unsigned)((unsigned short)Temp2).
If INT_MAX >= USHRT_MAX, then the division is done as (int)/(int).
Otherwise, like on a 16-bit system, the division is done as (int)/(unsigned), which is done as (unsigned)/(unsigned).
[Edit]
Temp, initialized with 0xF2C9 (see note), likely has the value of -3383 (or has the value of 62153 should short unlikely be wider than 16 bits.)
With (int)/(int), -3383/256 --> -13.21... --> -13. -13 converted to unsigned char --> 256 - 13 --> 243 or 0xF3.
(Assuming 16-bit int/unsigned) With (unsigned)/(unsigned), -3383 is converted to unsigned 65536 - 3383 --> 62153. 62153/256 --> 242.78... --> 242. 242 converted to unsigned char --> 242 or 0xF2.
Endian-ness in not relevant in this scenario.
Note: As pointed out by #Jens Gustedt, the value in Temp is implementation defined when Temp is 16-bit.
integer division works with "truncation toward zero", so the result of this division is F3, which is -13, instead of F2, which is -14. if you calculate this in decimal represenation the result would be -13.21 and then you would cut the .21.
"The question asked why the integers are signed"
"As per my understanding due to "Usual Arithmetic Conversions" first Temp should be converted into unsigned short..." NO. You are wrong here. The signed short Temp has its high bit set, hence is negative. The bit gets extended to the left when converted to int. The signed short Temp2 does not have its high bt set; the cast to (unsigned short) has no effect; it is converted to a positive int. The negative int is now divided by the positive int, resulting in a negtive value.
In the conversion of Temp2 to int, you don't want the sign bit extended. Use:
a = (unsigned char)(((unsigned short)Temp)/Temp2);
(I didn't test it; just theory)
Jens ,Paul and Mch,
Thanks for your clarification. But as per "ISO/IEC 9899 section 6.3.1.1
The rank of any unsigned integer type shall equal the rank of the corresponding
signed integer type, if any.
and as per 6.3.1.8 Usual arithmetic conversions" following rules should be applicable.
If both operands have the same type, then no further conversion is needed.
Otherwise, if both operands have signed integer types or both have unsigned
integer types, the operand with the type of lesser integer conversion rank is
converted to the type of the operand with greater rank.
**Otherwise, if the operand that has unsigned integer type has rank greater or
equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned
integer type.**
Otherwise, if the type of the operand with signed integer type can represent
all of the values of the type of the operand with unsigned integer type, then
the operand with unsigned integer type is converted to the type of the
operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type
corresponding to the type of the operand with signed integer type
so according to the above mentioned rule 3 1st signed short (integer type) should be converted in to unsigned short (integer type) and then arithmatic operation should be performed and result should be 0xF2.
I'm a little confused about how the C language treats different sized ints when you do basic arithmetic and bitwise operation. what would happen if in this case:
int8_t even = 0xBB;
int16_t twice = even << 8;
twice = twice + even;
What If i went the other way and attempted to add a 16 bit int to an 8 bit? is the normal int declaration dynamic? Why would i want to designate a size? What happens when I add to 8 bit ints that are both 0xFF?
As I mentioned in my comment, everything gets promoted to at least an int during arithmetic.
Here's an example program to demonstrate that:
main(){
struct {
char x : 1;
}t;
t.x = 1;
int i = t.x << (sizeof i * 8 - 1);
printf("i = %x\n",i);
}
t.x is only one bit, but in this operation it is promoted all the way to an integer to give the output:
i = 80000000
On the other hand, if we add the line
long long j = t.x << (sizeof j * 8 - 1)
gcc gave me the warning:
warning: left shift count >= width of type [enabled by default]
What If i went the other way and attempted to add a 16 bit int to an 8 bit?
All the arithmetic would be done at integer precision (probably 32 bits) and then the bottom 8 bits of the result would be stored in the 8 bit number.
Is the normal int declaration dynamic?
No. Its a fixed width on an implementation (probably 32 bits on yours).
Why would i want to designate a size?
Maybe you have space constraints. Maybe your algorithm was made to work with a specific size integer. Maybe you really want to work on some modulo field provided by uint16_t (Z_65536).
What happens when I add to 8 bit ints that are both 0xFF?
The promotion doesn't matter here, the 8 bit result will be 0xFE. If you were to store the result in a 16 bit number, then the result will be 0x1FE (Unless int's in your implementation are only 8 bits. This is highly unlikely except for some esoteric embedded applications).
Edit
I wrote about the unsigned convention here, because you representing the number as 0xFF seemed to refer to unsigned numbers (in K&R C 0xFF is actually an unsigned literal). If you were actually referring to the signed 8-bit value 0xFF that is equivalent to -1, and your problem becomes sort of trivial. No matter how big the integer, it should always be able to represent -1, as well as -1 + (-1) = -2 (you only actually need two bits to represent these numbers).
Normal "int" is not dynamic, but is not common across compilers; it's the size that is most convenient for the CPU you're compiling for. You would want to designate a size if you have some reason (network protocol, file format, etc) why you actually care about the representation.
Answers to your exercises below the answer to your question:
Your answer can be found in the C11 final draft spec (was ratified in October 2011, but the "final" document is not free): http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
The first relevant section is 6.3.1.1 Conversions : Arithmetic Operands : Boolean, characters and integers, beginning on page 50
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
The integer promotions preserve value including sign. As discussed earlier, whether a "plain" char is treated as signed is implementation-defined.
Essentially, the integer types are all promoted to at least int, and possibly to larger sizes than that if any of the operands had a rank greater than that. They generally become signed, but remain unsigned if either operand's type has greater range than int.
There's a bunch of stuff specifically defining how the conversions are performed, and codifying the rules of which types are "higher rank" (bigger) than others, and then we reach the real meat in 6.3.1.8: Usual arithmetic conversions:
Many operators that expect operands of arithmetic type cause conversions and yield result
types in a similar way. The purpose is to determine a common real type for the operands
and result. For the specified operands, each operand is converted, without change of type
domain, to a type whose corresponding real type is the common real type. Unless
explicitly stated otherwise, the common real type is also the corresponding real type of
the result, whose type domain is the type domain of the operands if they are the same,
and complex otherwise. This pattern is called the
usual arithmetic conversions:
... some stuff about floats ...
Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:
If both operands have the same type, then no further conversion is needed. Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank. Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned integer type.
Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.
int8_t even = 0xBB;
0xBB formed as a constant of type int (0x000000BB), then truncated down to int8_t
int16_t twice = even << 8;
the value of even is sign extended from int8_t to int, becoming 0xFFFFFFBB (if I assume int is 32-bits on this machine)
The value of 8 is formed as type int
These types match, so no further conversions occur
The operator << is performed (now that types match), yielding 0xFFFFBB00
This value is truncated to fit in int16_t, yielding 0xBB00
twice = twice + even;
The value of twice is sign extended from int16_t to int, yielding 0xFFFFBB00 (-17664)
The value of even is sign extended from int8_t to int, yielding 0xFFFFFFBB (-69)
These values are added, yielding 0xFFFFBABB (-17733)
This value is then truncated to fit in int16_t, yielding 0xBABB
What happens when I add to 8 bit ints that are both 0xFF?
They are both sign extended to int yielding 0xFFFFFFFF, then added yielding 0xFFFFFFFE. This may or may not be truncated back down depending on where you store it.
The compiler is of course free to notice that much of this extending/truncating stuff is worthless busywork because the output bits are not kept, and optimize it out. But the result is required to be "as if" if had done things the long way.
Suppose I have the following C code.
unsigned int u = 1234;
int i = -5678;
unsigned int result = u + i;
What implicit conversions are going on here, and is this code safe for all values of u and i? (Safe, in the sense that even though result in this example will overflow to some huge positive number, I could cast it back to an int and get the real result.)
Short Answer
Your i will be converted to an unsigned integer by adding UINT_MAX + 1, then the addition will be carried out with the unsigned values, resulting in a large result (depending on the values of u and i).
Long Answer
According to the C99 Standard:
6.3.1.8 Usual arithmetic conversions
If both operands have the same type, then no further conversion is needed.
Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.
In your case, we have one unsigned int (u) and signed int (i). Referring to (3) above, since both operands have the same rank, your i will need to be converted to an unsigned integer.
6.3.1.3 Signed and unsigned integers
When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
Now we need to refer to (2) above. Your i will be converted to an unsigned value by adding UINT_MAX + 1. So the result will depend on how UINT_MAX is defined on your implementation. It will be large, but it will not overflow, because:
6.2.5 (9)
A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
Bonus: Arithmetic Conversion Semi-WTF
#include <stdio.h>
int main(void)
{
unsigned int plus_one = 1;
int minus_one = -1;
if(plus_one < minus_one)
printf("1 < -1");
else
printf("boring");
return 0;
}
You can use this link to try this online: https://repl.it/repls/QuickWhimsicalBytes
Bonus: Arithmetic Conversion Side Effect
Arithmetic conversion rules can be used to get the value of UINT_MAX by initializing an unsigned value to -1, ie:
unsigned int umax = -1; // umax set to UINT_MAX
This is guaranteed to be portable regardless of the signed number representation of the system because of the conversion rules described above. See this SO question for more information: Is it safe to use -1 to set all bits to true?
Conversion from signed to unsigned does not necessarily just copy or reinterpret the representation of the signed value. Quoting the C standard (C99 6.3.1.3):
When a value with integer type is converted to another integer type other than _Bool, if
the value can be represented by the new type, it is unchanged.
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or
subtracting one more than the maximum value that can be represented in the new type
until the value is in the range of the new type.
Otherwise, the new type is signed and the value cannot be represented in it; either the
result is implementation-defined or an implementation-defined signal is raised.
For the two's complement representation that's nearly universal these days, the rules do correspond to reinterpreting the bits. But for other representations (sign-and-magnitude or ones' complement), the C implementation must still arrange for the same result, which means that the conversion can't just copy the bits. For example, (unsigned)-1 == UINT_MAX, regardless of the representation.
In general, conversions in C are defined to operate on values, not on representations.
To answer the original question:
unsigned int u = 1234;
int i = -5678;
unsigned int result = u + i;
The value of i is converted to unsigned int, yielding UINT_MAX + 1 - 5678. This value is then added to the unsigned value 1234, yielding UINT_MAX + 1 - 4444.
(Unlike unsigned overflow, signed overflow invokes undefined behavior. Wraparound is common, but is not guaranteed by the C standard -- and compiler optimizations can wreak havoc on code that makes unwarranted assumptions.)
Referring to The C Programming Language, Second Edition (ISBN 0131103628),
Your addition operation causes the int to be converted to an unsigned int.
Assuming two's complement representation and equally sized types, the bit pattern does not change.
Conversion from unsigned int to signed int is implementation dependent. (But it probably works the way you expect on most platforms these days.)
The rules are a little more complicated in the case of combining signed and unsigned of differing sizes.
When converting from signed to unsigned there are two possibilities. Numbers that were originally positive remain (or are interpreted as) the same value. Number that were originally negative will now be interpreted as larger positive numbers.
When one unsigned and one signed variable are added (or any binary operation) both are implicitly converted to unsigned, which would in this case result in a huge result.
So it is safe in the sense of that the result might be huge and wrong, but it will never crash.
As was previously answered, you can cast back and forth between signed and unsigned without a problem. The border case for signed integers is -1 (0xFFFFFFFF). Try adding and subtracting from that and you'll find that you can cast back and have it be correct.
However, if you are going to be casting back and forth, I would strongly advise naming your variables such that it is clear what type they are, eg:
int iValue, iResult;
unsigned int uValue, uResult;
It is far too easy to get distracted by more important issues and forget which variable is what type if they are named without a hint. You don't want to cast to an unsigned and then use that as an array index.
What implicit conversions are going on here,
i will be converted to an unsigned integer.
and is this code safe for all values of u and i?
Safe in the sense of being well-defined yes (see https://stackoverflow.com/a/50632/5083516 ).
The rules are written in typically hard to read standards-speak but essentially whatever representation was used in the signed integer the unsigned integer will contain a 2's complement representation of the number.
Addition, subtraction and multiplication will work correctly on these numbers resulting in another unsigned integer containing a twos complement number representing the "real result".
division and casting to larger unsigned integer types will have well-defined results but those results will not be 2's complement representations of the "real result".
(Safe, in the sense that even though result in this example will overflow to some huge positive number, I could cast it back to an int and get the real result.)
While conversions from signed to unsigned are defined by the standard the reverse is implementation-defined both gcc and msvc define the conversion such that you will get the "real result" when converting a 2's complement number stored in an unsigned integer back to a signed integer. I expect you will only find any other behaviour on obscure systems that don't use 2's complement for signed integers.
https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation
https://msdn.microsoft.com/en-us/library/0eex498h.aspx
Horrible Answers Galore
Ozgur Ozcitak
When you cast from signed to unsigned
(and vice versa) the internal
representation of the number does not
change. What changes is how the
compiler interprets the sign bit.
This is completely wrong.
Mats Fredriksson
When one unsigned and one signed
variable are added (or any binary
operation) both are implicitly
converted to unsigned, which would in
this case result in a huge result.
This is also wrong. Unsigned ints may be promoted to ints should they have equal precision due to padding bits in the unsigned type.
smh
Your addition operation causes the int
to be converted to an unsigned int.
Wrong. Maybe it does and maybe it doesn't.
Conversion from unsigned int to signed
int is implementation dependent. (But
it probably works the way you expect
on most platforms these days.)
Wrong. It is either undefined behavior if it causes overflow or the value is preserved.
Anonymous
The value of i is converted to
unsigned int ...
Wrong. Depends on the precision of an int relative to an unsigned int.
Taylor Price
As was previously answered, you can
cast back and forth between signed and
unsigned without a problem.
Wrong. Trying to store a value outside the range of a signed integer results in undefined behavior.
Now I can finally answer the question.
Should the precision of int be equal to unsigned int, u will be promoted to a signed int and you will get the value -4444 from the expression (u+i). Now, should u and i have other values, you may get overflow and undefined behavior but with those exact numbers you will get -4444 [1]. This value will have type int. But you are trying to store that value into an unsigned int so that will then be cast to an unsigned int and the value that result will end up having would be (UINT_MAX+1) - 4444.
Should the precision of unsigned int be greater than that of an int, the signed int will be promoted to an unsigned int yielding the value (UINT_MAX+1) - 5678 which will be added to the other unsigned int 1234. Should u and i have other values, which make the expression fall outside the range {0..UINT_MAX} the value (UINT_MAX+1) will either be added or subtracted until the result DOES fall inside the range {0..UINT_MAX) and no undefined behavior will occur.
What is precision?
Integers have padding bits, sign bits, and value bits. Unsigned integers do not have a sign bit obviously. Unsigned char is further guaranteed to not have padding bits. The number of values bits an integer has is how much precision it has.
[Gotchas]
The macro sizeof macro alone cannot be used to determine precision of an integer if padding bits are present. And the size of a byte does not have to be an octet (eight bits) as defined by C99.
[1] The overflow may occur at one of two points. Either before the addition (during promotion) - when you have an unsigned int which is too large to fit inside an int. The overflow may also occur after the addition even if the unsigned int was within the range of an int, after the addition the result may still overflow.