This question already has answers here:
unsigned int and signed char comparison
(4 answers)
Closed 6 years ago.
I came across this question.
#include <stdio.h>
int main(void) {
// your code goes here
unsigned int i = 23;
signed char c = -23;
if (i > c)
printf("yes");
else
printf("no");
return 0;
}
I am unable to understand why the output of this code is no.
Can someone help me in understanding how the comparison operator works when comparison is done between int and char in C ?
You are comparing an unsigned int to a signed char. The semantics of this kind of comparison are counter-intuitive: most binary operations involving signed and unsigned operands are performed on unsigned operands, after converting the signed value to unsigned (if both operands have the same size after promotion). Here are the steps:
The signed char value is promoted to an int with the same value -23.
comparison is to be performed on int and unsigned int, the common type is unsigned int as defined in the C Standard.
The int is converted to an unsigned int with value UINT_MAX - 23, a very large number.
The comparison is performed on the unsigned int values: 23 is the smaller value, the comparison evaluates to false.
The else branch is evaluated, no is printed.
To make matters worse, if c had been defined as a long, the result would have depended on whether long and int have the same size or not. On Windows, it would print no, whereas on 64 bit Linux, it would print yes.
Never mix signed and unsigned values in comparisons. Enable compiler warnings to prevent this kind of mistake (-Wall or -Weverything). You might as well make all these warnings fatal with -Werror to avoid this kind of ill-fated code entirely.
For a complete reference, read the following sections of the C Standard (C11) under 6.3 Conversions:
integer promotions are explained in 6.3.1.1 Boolean, characters, and integers.
operand conversions are detailed in 6.3.1.8 Usual arithmetic conversions.
You can download the latest draft of the C11 Standard from the working group website: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
Related
This question already has answers here:
Comparison operation on unsigned and signed integers
(7 answers)
Closed 1 year ago.
#include <stdio.h>
int main() {
unsigned int i = 23;
signed char c = -23;
if (i<c)
puts("TRUE");
return 0;
}
Why the output of the following program is TRUE, even though I have used signed char which can store from -128 to 127.
Your i < c comparison has operands of two different types, so the operand with the smaller type (lower rank) is converted to the type of the other. In this case, as -23 cannot be represented as an unsigned int, and following the "usual arithmetic conversions," it will end up with a value considerably greater than +23 (actually, UINT_MAX + 1 - 23), according to Paragraph #2 in the following excerpt from this (Draft) C11 Standard:
6.3.1.3 Signed and unsigned integers
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.
You can see this conversion in action by assigning c to a separate unsigned int and printing its value, as in the following code. Assuming a 32-bit unsigned int, you will likely see a value of 4294967273 (which is, indeed, greater than 23).
#include <stdio.h>
int main()
{
unsigned int i = 23;
signed char c = -23;
unsigned int uc = c; // Add a diagnostic line ...
printf("%u\n", uc); // ... and show the value being used in the comparison
if (i < c) {
printf("TRUE");
}
return 0;
}
Note: The relative size of the two variables (i and c) here is something of a red herring; even if you declare c as signed int c = -23;, you will still get the same result, due to the Usual Arithmetic Conversions (link courtesy of Jonathan Leffler's comment) – note that a signed int cannot represent all possible values of an unsigned int, so the very last of the 'hollow' bullets will come into play.
In order to make the comparison, the smallest variable (char, 8 bit) must be promoted to the size and signedness of the larger one (integer, 32 bit). But in so doing, -23 becomes 4294967273. Which is much more than 23.
If you compare c with a signed integer, the sign promotion causes no change and the test behaves as expected.
This question already has answers here:
Comparison operation on unsigned and signed integers
(7 answers)
In a C expression where unsigned int and signed int are present, which type will be promoted to what type?
(2 answers)
Closed 7 years ago.
This is the code,
#include <stdio.h>
int main()
{
unsigned int i = 0xFFFFFFFF;
if (i == -1)
printf("signed variable\n");
else
printf("unsigned variable\n");
return 0;
}
This is the output,
signed variable
Why is i's value -1 even it is declared as unsigned?
Is it something related to implicit type conversations?
This is the build environment,
Ubuntu 14.04, GCC 4.8.2
The == operator causes its operands to be promoted to a common type according to C's promotion rules. Converting -1 to unsigned yields UINT_MAX.
i's value is 0xFFFFFFFF, which is exactly the same as -1, at least when the later is converted to an unsigned integer. And this is exactly what is happening with the comparison operators:
If both of the operands have arithmetic type, the usual arithmetic conversions are performed. [...]
[N1570 $6.5.9/4]
-1 in two's complement is "all bits set", which is also what 0xFFFFFFFF for an unsigned int (of size 4) is.
It's been a while since I start programming in C, however, I still feel confused about unsigned. If we compiled this code:
#include <stdio.h>
int main(int argc, char **argv)
{
unsigned int x = -1;
return 0;
}
both gcc and VC++ don't raise any error or even a warning regarding using negative number with unsigned.
My question is that does unsigned do any internal job or it just a hint to the programer that this value shouldn't be negative?
It is NOT just a hint. The following two snippets should behave differently:
Signed int:
int x = -1;
printf("%d\n", x > 0); // prints 0
Unsigned int:
unsigned int x = -1;
printf("%d\n", x > 0); // prints 1
And you could probably come up with 5 more examples where the signedness matters. For example, shifting right with the >> operator.
Use -Wsign-conversion to get a warning with gcc.
With gcc 4.7.1, -Wsign-conversion is neither a part of -Wall nor -Wextra.
Also note that the C Standard does NOT require a warning for this initialization.
unsigned is not a qualifier like static, extern, const or inline. It is part of the type.
int and unsigned int are two completely different types. You will never find an unsigned int that can hold a negative number. Note also that int and signed int are exactly the same type. It's a slightly different story for char, but I'll leave that for another time.
Assigning -1 to an unsigned integer is a common trick to set it to the largest value it can hold.
unsigned can affect integer overflow, comparison, and bitwise-shift behavior. It is not just a "hint" that it should not be negative.
Try to enable warnings in gcc by: gcc -Wall -o out input.c
Another difference between unsigned int and normal int: int range using 4-bytes: –2,147,483,648 to 2,147,483,647, unsigned range using 4-bytes: 0 to 4,294,967,295 . You don't have to store information about the sign (the highest bit) so you can assign larger values and the application will work properly.
((unsigned int) -1) is equivalent to (UINT_MAX) (<limits.h>), because a conversion from a negative value to an unsigned value is guaranteed to be done by a modulo operation.
C11 § 6.3.1.3 Signed and unsigned integers
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.
There are both signed and unsigned versions of the internal(machine-level) instructions for calculations such as multiplication mul(for unsigned) and imul(signed). Compiler chooses them as you declare variables as unsigned or signed.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Confused about C macro expansion and integer arithmetic
A riddle (in C)
The expected output of the following C program is to print the elements in the array. But when actually run, it doesn't do so.
#include<stdio.h>
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = {23,34,12,17,204,99,16};
int main()
{
int d;
for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
printf("%d\n",array[d+1]);
return 0;
}
Because sizeof gives you an unsigned value, which you probably would have noticed had you turned up the warning level, such as using -Wall -Wextra with gcc (a):
xyzzy.c: In function 'main':
xyzzy.c:8: warning: comparison between signed and unsigned
If you force it to signed, it works fine:
#define TOTAL_ELEMENTS (int)((sizeof(array) / sizeof(array[0])))
What happens in detail can be gleaned from the ISO standard. In comparisons between different types, promotions are performed to make the types compatible. The compatible type chosen depends on several factors such as sign compatibility, precision and rank but, in this case, it was deemed that the unsigned type size_t was the compatible type so d was upgraded to that type.
Unfortunately, casting -1 to an unsigned type (at least for two's complement which is almost certainly what you're using) results in a rather large positive number.
One that's certainly larger the the 5 you get from (TOTAL_ELEMENTS-2). In other words, your for statement effectively becomes:
for (d = some big honking number way greater than five;
d <= 5;
d++
) {
// fat chance of getting in here !!
}
(a) This requirement to use extra remains a point of contention between the gcc developers and myself. They're obviously using some new definition of the word "all" of which I was previously unaware (with apologies to Douglas Adams).
TOTAL_ELEMENTS is of type size_t, subtracting 2 is done at compile time and so it is 5UL (emphasis on the unsigned suffix). The comparison with the signed integer d is then always false. Try
for(d=-1;d <= (ssize_t)(TOTAL_ELEMENTS-2);d++)
FTW, the intel compiler warns about exactly this when you try and compile the code.
To clarify what went wrong: sizeof() translates to a result type of size_t, which is nothing but an unsigned integer, larger than or equal to unsigned int.
So the result of (sizeof(array) / sizeof(array[0])) is a result in two operands of size_t type. The division is performed on these operands:size_t / size_t. Both operands are of the same type so it works fine. The result of the division is of type size_t, which is the type that TOTAL_ELEMENTS results in.
The expression (TOTAL_ELEMENTS-2) therefore have the types size_t - int, since the integer literal 2 is of type int.
Here we have two different types. What happens then is something called balancing (formally "the usual arithmetic conversions"), which happens when the compiler spots two different types. The balancing rules state that if one operand is signed and the other unsigned, then the signed one is silently, implicitly converted to an unsigned type.
This is what happens in this code. size_t - int is converted to size_t - size_t, then the subtraction is executed, the result is size_t. Then int <= size_t is converted to size_t <= size_t. The variable d turns unsigned, and if it had a negative value, the code goes haywire.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
A riddle (in C)
1.
main()
{
if(-1<(unsigned char)1)
printf("-1 is less than (unsigned char)1:ANSI semantics");
else
printf("-1 NOT less than (unsigned char)1:K&R semantics");
}
2.
int array[] = {23,41,12,24,52,11};
#define TOTAL_ELEMENTS (sizeof(array)/sizeof(array[0]))
main()
{
int d = -1,x;
if(d<=TOTAL_ELEMENTS -2)
x = array[d+1];
}
The first convert unsigned char 1 to a signed variable in ANSI C,
while the second program convert d to an unsigned int that makes the
condition expression return false in ANSI C.
Why did they behave differently?
For the first one the right-hand side is an unsigned char, and all unsigned char values fit into a signed int, so it is converted to signed int.
For the second one the right-hand side is an unsigned int, so the left-hand side is converted from signed int to unsigned int.
See also this CERT document on integer conversions.
starblue explained the first part of your question. I'll take the second part. Because TOTAL_ELEMENTS is a size_t, which is unsigned, the int is converted to that unsigned type. Your size_t is so that int cannot represent all values of it, so the conversion of the int to size_t happens, instead of the size_t to the int.
Conversion of negative numbers to unsigned is perfectly defined: The value wraps around. If you convert -1 to an unsigned int, it ends up at UINT_MAX. That is true whether or not you use twos' complement to represent negative numbers.
The rationale for C document has more information about that value preserving conversion.
Here's the way I remember how automatic conversions are applied:
if the operand sizes are different, the conversion is applied to the smaller operand to make it the same type as the larger operand (with sign extension if the smaller operand is signed)
if the operands are the same size, but one is signed and the other unsigned, then the signed operand is converted to unsigned
While the above may not be true for all implementations, I believe it is correct for all twos-complement implementations.