Why -1 is printed with this context?(Signed char vs unsigned char) - c++

From what i know it should be implementation-dependent but it returns -1.Why?
char ch = 255;
int i = ch;
cout<<i;
return 0;

char by default may be signed or unsigned. (implementation dependent)
Minimum range of signed char is -127 to 127 (typically -128 to 127).
If by default char is signed, ch would not be able to accommodate 255(0xFF) and would silently convert it to -1(0xFF). Then you are typecasting it to int and printing so the output is -1.
To avoid this use unsigned char explicitly. (Range is atleast 0 to 255)
4.7 Integral conversions [conv.integral]
[3] If the destination type is signed, the value is unchanged if it can
be represented in the destination type (and bit-field width);
otherwise, the value is implementation-defined.

char might be (and in your case apparently is) a signed type wit a range from -128 to +127.
So char ch = 255 triggers an overflow which yields an implementation defined result for signed types.

Related

Value of char c = 255 converted to unsigned int in c++ [duplicate]

I am trying to print char as positive value:
char ch = 212;
printf("%u", ch);
but I get:
4294967252
How I can get 212 in the output?
Declare your ch as
unsigned char ch = 212 ;
And your printf will work.
This is because in this case the char type is signed on your system*. When this happens, the data gets sign-extended during the default conversions while passing the data to the function with variable number of arguments. Since 212 is greater than 0x80, it's treated as negative, %u interprets the number as a large positive number:
212 = 0xD4
When it is sign-extended, FFs are pre-pended to your number, so it becomes
0xFFFFFFD4 = 4294967252
which is the number that gets printed.
Note that this behavior is specific to your implementation. According to C99 specification, all char types are promoted to (signed) int, because an int can represent all values of a char, signed or unsigned:
6.1.1.2: If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.
This results in passing an int to a format specifier %u, which expects an unsigned int.
To avoid undefined behavior in your program, add explicit type casts as follows:
unsigned char ch = (unsigned char)212;
printf("%u", (unsigned int)ch);
* In general, the standard leaves the signedness of char up to the implementation. See this question for more details.
There are two bugs in this code. First, in most C implementations with signed char, there is a problem in char ch = 212 because 212 does not fit in an 8-bit signed char, and the C standard does not fully define the behavior (it requires the implementation to define the behavior). It should instead be:
unsigned char ch = 212;
Second, in printf("%u",ch), ch will be promoted to an int in normal C implementations. However, the %u specifier expects an unsigned int, and the C standard does not define behavior when the wrong type is passed. It should instead be:
printf("%hhu", ch);
(For %hhu, printf expects an unsigned char that has, in normal C implementations, been promoted to int.)
In case you cannot change the declaration for whatever reason, you can do:
char ch = 212;
printf("%d", (unsigned char) ch);
The range of char is 127 to -128. If you assign 212, ch stores -44 (212-128-128) not 212.So if you try to print a negative number as unsigned you get (MAX value of unsigned int)-abs(number) which in this case is 4294967252
So if you want to store 212 as it is in ch the only thing you can do is declare ch as
unsigned char ch;
now the range of ch is 0 to 255.
Because char is by default signed declared that means the range of the variable is
-127 to +127>
your value is overflowed. To get the desired value you have to declared the unsigned modifier. the modifier's (unsigned) range is:
0 to 255
to get the the range of any data type follow the process 2^bit example charis 8 bit length to get its range just 2 ^(power) 8.

Reading an address containing 11111111 as an unsigned int shows 4294967295

So, I was trying some stuff and I noticed that if you read one address that contains 11111111 as an unsigned int it shows that is value is 4294967295 and not 255, why is that?
Example of the code:
unsigned int a = 255;
unsigned int* p = &a;
char *p0 = (char*)p;
cout << (void*)p0 << " " <<(unsigned int)*p0<<endl;
char *p0 = (char*)p; Here p0 points at the lowest byte of p. What value that happens to be stored there depends on endianess. Could be 0x00 (Big Endian) or 0xFF (Little Endian).
What is CPU endianness?
*p0 here you access the contents of the supposed char. Since char has implementation-defined signedness, it could either manage numbers -128 to 127 (assuming 2's complement) or 0 to 255. Is char signed or unsigned by default?
Your particular system seems to use signed char, 2's complement, Little Endian. Meaning that the raw binary value 0xFF gets interpreted as -1 decimal.
(unsigned int)*p0 forces a conversion from -1 to unsigned int type. This is done after this well-defined rule (C 6.3.1.4, same rule applies in C++):
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.
-1 + UINT_MAX + one more =
-1 + UINT_MAX + 1 =
UINT_MAX =
4294967295 on your 32 bit system.
Lesson learnt: never use char for anything but text, never use it to store values. If you need an 8 bit integer type or something to represent raw binary, use unsigned char or uint8_t.
Line 4 is reading the representation of the object a as char, which is just about the only time it's legal (not undefined) to access an object with an lvalue not matching the actual type of the object. C allows plain char to be a signed or unsigned type (implementation-defined). On the implementation you're using, char is a signed type, and unsigned int is represented in little endian (low-order byte first), so you read the bit pattern with 8 one bits, and this represents the char value -1.
Subsequently converting (via cast or implicitly) the value -1 to unsigned int reduces it modulo UINT_MAX+1, producing the value UINT_MAX.

Why signed char can hold bigger values than 127?

int main()
{
char MCU = 0b00000000;
char al_av = 0b10100000;
// Before bit operation
cout << "MCU = " << int(MCU) << endl;
MCU = MCU | al_av;
// After the bit operation
cout << "MCU = " << int(MCU) << endl; // Expected 160, got -96
char temp = 160;
cout << temp; // got the a with apostrophe
return 0;
}
I expected the output of char temp to be a negative number (or a warning / error) because 160 exceeds the [-127,127] interval, but instead, the result was the one in the ASCII table (a with apostrophe)
On cpp reference:
char - type for character representation which can be most efficiently processed on the target system (has the same representation and alignment as either signed char or unsigned char, but is always a distinct type)
I don't understand what is written in italic (also I'm not sure it helps a lot for this question). Is there any implicit conversion ?
Why signed char can hold bigger values than 127?
It cannot.
char x = 231;
here, there is an (implicit) integer conversion: 231 is a prvalue of type int and takes value -25 before it is converted to char (which is signed on your system). You can ask your compiler to warn you about it with -Wconstant-conversion.
char - type for character representation which can be most efficiently processed on the target system (has the same representation and alignment as either signed char or unsigned char, but is always a distinct type)
I don't understand what is written in italic
This isn't related to what the type can hold, it only ensures that the three types char, signed char and unsigned char have common properties.
From C++14 char, if signed, must be a 2's complement type. That means that it has the range of at least -128 to +127. It's important to know that the range could be larger than this so it's incorrect to assume that a number greater than 127 cannot be stored in a char if signed. Use
std::numeric_limits<char>::max()
to get the real upper limit on your platform.
If you do assign a value larger than this to a char and char is signed then the behaviour of your code is implementation defined. Typically that means wrap-around to a negative which is practically universal behaviour for a signed char type.
Note also that ASCII is a 7 bit encoding, so it's wrong to say that any character outside the range 0 - 127 is ASCII. Note also that ASCII is not the only encoding supported by C++. There are others.
Finally, the distinct types: Even if char is signed, it is a different type from signed char. This means that the code
int main() {
char c;
signed char d;
std::swap(c, d);
}
will always result in a compile error.
char temp = 160;
It is actually negative. The point is cout supports non-ASCII characters, so it interprets it as non-negative. cout is probably casting it to unsigned char (or any unsigned integral type) before using it.
If you use printf and tell it to interpret it as an integer you will see that it is a negative value.
printf("%d\n", temp); // prints -96

Signed and unsigned char

Why are two char like signed char and unsigned char with the same value not equal?
char a = 0xfb;
unsigned char b = 0xfb;
bool f;
f = (a == b);
cout << f;
In the above code, the value of f is 0.
Why it's so when both a and b have the same value?
There are no arithmetic operators that accept integers smaller than int. Hence, both char values get promoted to int first, see integral promotion
for full details.
char is signed on your platform, so 0xfb gets promoted to int(-5), whereas unsigned char gets promoted to int(0x000000fb). These two integers do not compare equal.
On the other hand, the standard in [basic.fundamental] requires that all char types occupy the same amount of storage and have the same alignment requirements; that is, they have the same object representation and all bits of the object representation participate in the value representation. Hence, memcmp(&a, &b, 1) == 0 is true.
The value of f and, in fact, the behaviour of the program, is implementation-defined.
In C++14 onwards1, for a signed char, and assuming that CHAR_MAX is 127, a will probably be -5. Formally speaking, if char is signed and the number does not fit into a char, then the conversion is implementation-defined or an implementation-defined signal is raised.
b is 251.
For the comparison a == b (and retaining the assumption that char is a narrower type than an int) both arguments are converted to int, with -5 and 251 therefore retained.
And that's false as the numbers are not equal.
Finally, note that on a platform where char, short, and int are all the same size, the result of your code would be true (and the == would be in unsigned types)! The moral of the story: don't mix your types.
1 C++14 dropped 1's complement and signed magnitude signed char.
Value range for (signed) char is [-128, 127]. (C++14 drops -127 as the lower range).
Value range for unsigned char is [0, 255]
What you're trying to assign to both of the variables is 251 in decimal. Since char cannot hold that value you will suffer a value overflow, as the following warning tells you.
warning: overflow in conversion from 'int' to 'char' changes value from '251' to ''\37777777773'' [-Woverflow]
As a result a will probably hold value -5 while b will be 251 and they are indeed not equal.

Char comparison with 0xFF returns false even when it is equal to it, why?

In C++ program I have some char buf[256]. The problem is here:
if (buf[pbyte] >= 0xFF)
buf[++pbyte] = 0x00;
This always returns false even when buf[pbyte] is equal to 255 AKA 0xFF as seen in immediate window and watch window. Thus the statement does not get executed. However, when I change this to below:
if (buf[pbyte] >= char(0xFF))
buf[++pbyte] = 0x00;
The program works; how come?
The literal 0xFF is treated as an int with the value 255.
When you compare a char to an int, the char is promoted to an int before the comparison.
On some platforms char is a signed value with a range like -128 to +127. On other platforms char is an unsigned value with a range like 0 to 255.
If your platform's char is signed, and its bit pattern is 0xFF, then it's probably -1. Since -1 is a valid int, the promotion stops there.
You end up comparing -1 to 255.
The solution is to eliminate the implicit conversions. You can write the comparison as:
if (buf[pbyte] == '\xFF') ...
Now both sides are chars, so they'll be promoted in the same manner and are directly comparable.
The problem is that char is signed on your system.
In the common 2s complement representation, a signed char with the "byte-value" 0xFF represents the integer -1, while 0xFF is an int with value 255. Thus, you are effectively comparing int(-1) >= int(255), which yields false. Keep in mind that they are compared as int because of arithmetic conversion rules, that is both operands are promoted ("cast implicitly") to int before comparing.
If you write char(0xFF) however, you do end up with the comparison -1 >= -1, which yields true as expected.
If you want to store numbers in the range [0,255], you should use unsigned char or std::uint8_t instead of char.
Due to the integer promotions in this condition
if (buf[pbyte] >= `0xFF`)
the two operands are converted to the type int (more precisely only the left operand is converted to an object of the type int because the right operand already has the type int). As it seems in your system the type char behaves as the type signed char then the value '\xFF' is a negative value equal to -1. Then this value is converted to an object of the type int you will get 0xFFFFFFFF (assuming that the type int occupies 4 bytes).
On the other hand the integer constant 0xFF is a positive value that has internal representation like 0x000000FF
Thus the condition in the if statement
if ( 0xFFFFFFFF >= `0x000000FF`)
yields false.
When you use the casting ( char )0xFF then the both operands have the same type and the same values.
Integer literals by default are converted to an int, assuming the value will fit into the int type, otherwise it is promoted to a long.
So in your code you are specifying 0xFF which is interpreted as an int type, i.e. 0x000000FF