Printing declared char value in C - c

I understand that character variable holds from (signed)-128 to 127 and (unsigned)0 to 255
char x;
x = 128;
printf("%d\n", x);
But how does it work? Why do I get -128 for x?

printf is a variadic function, only providing an exact type for the first argument.
That means the default promotions are applied to the following arguments, so all integers of rank less than int are promoted to int or unsigned int, and all floating values of rank smaller double are promoted to double.
If your implementation has CHAR_BIT of 8, and simple char is signed and you have an obliging 2s-complement implementation, you thus get
128 (literal) to -128 (char/signed char) to -128 (int) printed as int => -128
If all the listed condition but obliging 2s complement implementation are fulfilled, you get a signal or some implementation-defined value.
Otherwise you get output of 128, because 128 fits in char / unsigned char.
Standard quote for case 2 (Thanks to Matt for unearthing the right reference):
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.60)
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.

This all has nothing to do with variadic functions, default argument promotions etc.
Assuming your system has signed chars, then x = 128; is performing an out-of-range assignment. The behaviour of this is implementation-defined ; meaning that the compiler may choose an action but it must document what it does (and therefore, do it reliably). This action is allowed to include raising a signal.
The usual behaviour that modern compilers do for out-of-range assignment is to truncate the representation of the value to fit in the destination type.
In binary representation, 128 is 000....00010000000.
Truncating this into a signed char gives the signed char of binary representation 10000000. In two's complement representation, which is used by all modern C systems for negative numbers, this is the representation of the value -128. (For historical curiousity: in one's complement this is -127, and in sign-magnitude, this is -0 which may be a trap representation and thus raise a signal).
Finally, printf accurately prints out this char's value of -128. The %d modifier works for char because of the default argument promotions and the facts that INT_MIN <= CHAR_MIN and INT_MAX >= CHAR_MAX.; this behaviour is guaranteed except on systems which have plain char as unsigned, and sizeof(int)==1 (which do exist but you'd know about it if you were on one).

Lets look at the binary representation of 128 when stored into 8 bits:
1000 0000
And now let's look at the binary representation of -128 when stored into 8 bits:
1000 0000
The standard for char with your current setup looks to be a signed char (note this isn't in the c standard, look here if you don't believe me) and thus when you're assigning the value of 128 to x you're assigning it the value 1000 0000 and thus when you compile and print it out it's printing out the signed value of that binary representation (meaning -128).
It turns out my environment is the same in assuming char is actually signed char. As expected if I cast x to be an unsigned char then I get the expected output of 128:
#include <stdio.h>
#include <stdlib.h>
int main() {
char x;
x = 128;
printf("%d %d\n", x, (unsigned char)x);
return 0;
}
gives me the output of -128 128
Hope this helps!

Related

Range of character is positive then if i store a integer in it still it should print a value between(0-255).why Output is -24

'''
void main()
{
int i=1000;
char c='A';
c=i;
printf("%d",c);
}
'''
Output is -24
why this output when range of character is (0-255)
When explaining that behaviour, the following things need to be considered:
First, in an assignment like c=i in
int i=1000;
char c='A';
c=i;
we need to consider that i is converted to the type of c before assignment. Integral conversion is defined here in an online C99 standard draft:
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.60)
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.
So it is necessary to know if char is a singed integral type or not. This can differ from compiler to compiler, but it seems that your compiler considers type char as signed char by the default.
In case of signed char, the "result is implementation-defined", and we would need to have a look at the compiler's specification.
A common implementation is that the integral value 1000, which in binary is 00000011 11101000 is truncated to 8 bits and stored in the char value.
What 11101000 then means for a signed char is defined in the representation of types:
6-2-6-2 Integer types
2 For signed integer types, the bits of the object representation
shall be divided into three groups: value bits, padding bits, and the
sign bit. There need not be any padding bits; signed char shall not
have any padding bits. There shall be exactly one sign bit. Each bit
that is a value bit shall have the same value as the same bit in the
object representation of the corresponding unsigned type (if there are
M value bits in the signed type and N in the unsigned type, then M <=
N ). If the sign bit is zero, it shall not affect the resulting value.
If the sign bit is one, the value shall be modified in one of the
following ways:
the corresponding value with sign bit 0 is negated (sign and
magnitude);
the sign bit has the value -(2M) (two's complement);
the
sign bit has the value -(2M- 1) (ones' complement).
Which of these
applies is implementation-defined, as ...
Again, the result is defined in by the implementation of the compiler; but a common interpretation is two's complement, interpreting the most significant bit as sign bit:
In your case, the 8 bits of 11101000 is one sign bit (set) and 7 bits of the remainder; the remainder is 1101000 which is 104; the actual value in two's complement is then -(127-104+1), which is -24.
Note that it is not clear that the result is -24; other compilers might yield different results.
There is even one more step to consider, as you print the signed character value using format specifier "%d":
printf("%d",c)
This means, that negative signed char value gets promoted to int type; but this will yield the "same" negative value then. I omit the explanation of "promotion" and why arguments in printf are promoted at all.
As #JohnBollinger said, the typical range of type char, which is usually signed, is -128 - 127. When you assign 1000(1111101000 in binary) to the char, only the 8 least significant bits(assuming your chars are 8 bits) are kept leading to 11101000 in binary. This translates to -24 when it's printed as a signed integer.
Your compiler by default considers the type char as a signed integer type similarly to the type signed char.
The range of values for the type signed char is
— minimum value for an object of type signed char
SCHAR_MIN -127 // −(27 − 1)
— maximum value for an object of type signed char
SCHAR_MAX +127 // 27 − 1
From the C Standard (5.2.4.2.1 Sizes of integer types <limits.h>)
2 If the value of an object of type char is treated as a signed
integer when used in an expression, the value of CHAR_MIN shall be the
same as that of SCHAR_MIN and the value of CHAR_MAX shall be the same
as that of SCHAR_MAX. Otherwise, the value of CHAR_MIN shall be 0 and
the value of CHAR_MAX shall be the same as that of UCHAR_MAX. 20) The
value UCHAR_MAX shall equal 2CHAR_BIT − 1.
Here is a demonstrative program where there is used the type unsigned char instead of the type char.
#include <stdio.h>
int main(void)
{
int i = 1000;
unsigned char c;
printf( "i = %#x\n", i );
c = i;
printf( "c = %d\n", c );
return 0;
}
The program output is
i = 0x3e8
c = 232
As you can see an object of the type signed char can not hold such a big value as 232. If you will subtract 232 from 256 you will get 24. So this value and the value 232 used as an internal representation of signed char will get 0. So this internal representation of 232 interpreted as a signed value should be equal to -24 that is 24 + -24 = 0.

Type conversion of objects involving unsigned type in C language

I have been reading KnR lately and have come across a statement:
"Type conversions of Expressions involving unsigned type values are
complicated"
So to understand this i have written a very small code:
#include<stdio.h>
int main()
{
signed char x = -1;
printf("Signed : %d\n",x);
printf("Unsigned : %d\n",(unsigned)x);
}
signed x bit form will be : 1000 0001 = -1
when converted to unsigned its value has to be 1000 0001 = 129.
But even after type conversion it prints the -1.
Note: I'm using gcc compiler.
C11 6.3.1.3, paragraph 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.
−1 cannot be represented as an unsigned int value, the −1 is converted to UINT_MAX. Thus -1 becomes a very large positive integer.
Also, use %u for unsigned int, otherwise result invoked undefined behaviour.
C semantics regarding conversion of integers is defined in terms of the mathematical values, not in terms of the bits. It is good to understand how values are represented as bits, but, when analyzing expressions, you should think about values.
In this statement:
printf("Signed : %d\n",x);
the signed char x is automatically promoted to int. Since x is −1, the new int also has the value −1, and printf prints “-1”.
In this statement:
printf("Unsigned : %d\n",(unsigned)x);
the signed char x is automatically promoted to int. The value is still −1. Then the cast converts this to unsigned. The rule for conversion to unsigned is that UINT_MAX+1 is added to or subtracted from the value as needed to bring it into the range of unsigned. In this case, adding UINT_MAX+1 to −1 once brings the value to UINT_MAX, which is within range. So the result of the conversion is UINT_MAX.
However, this unsigned value is then passed to printf to be printed with the %d conversion. That violates C 2018 7.21.6.1 9, which says the behavior is undefined if the types do not match.
This means a C implementation is allowed to do anything. In your case, it seems what happened is:
The unsigned value UINT_MAX is represented with all one bits.
The printf interpreted the all-one-bits value as an int.
Your implementation uses two’s complement for int types.
In two’s complement, an object with all one bits represents −1.
So printf printed “-1”.
If you had used this correct code instead:
printf("Unsigned : %u\n",(unsigned)x);
then printf would print the value of UINT_MAX, which is likely 4,294,967,295, so printf would print “4294967295”.

C typecasting from a signed char to int type

In the below snippet, shouldn't the output be 1? Why am I getting output as -1 and 4294967295?
What I understand is, the variable, c, here is of signed type, so shouldn't its value be 1?
char c=0xff;
printf("%d %u",c,c);
c is of signed type. a char is 8 bits. So you have an 8 bit signed quantity, with all bits 1. On a twos complement machine, that evaluates to -1.
Some compilers will warn you when you do that sort of thing. If you're using gcc/clang, switch on all the warnings.
Pedant note: On some machines it could have the value 255, should the compiler treat 'char' as unsigned.
You're getting the correct answer.
The %u format specifier indicates that the value will be an unsigned int. The compiler automatically promotes your 8-bit char to a 32-bit int. However you have to remember that char is a signed type. So a value of 0xff is in fact -1.
When the casting from char to int occurs, the value is still -1, but the it's the 32-bit representation which in binary is 11111111 11111111 11111111 11111111 or in hex 0xffffffff
When that is interpreted as an unsigned integer, all of the bits are obviously preserved because the length is the same, but now it's handled as an unsigned quantity.
0xffffffff = 4294967295 (unsigned)
0xffffffff = -1 (signed)
There are three character types in C, char, signed char, and unsigned char. Plain char has the same representation as either signed char or unsigned char; the choice is implementation-defined. It appears that plain char is signed in your implementation.
All three types have the same size, which is probably 8 bits (CHAR_BIT, defined in <limits.h>, specifies the number of bits in a byte). I'll assume 8 bits.
char c=0xff;
Assuming plain char is signed, the value 0xff (255) is outside the range of type char. Since you can't store the value 255 in a char object, the value is implicitly converted. The result of this conversion is implementation-defined, but is very likely to be -1.
Keep this carefully in mind: 0xff is simply another way to write 255, and 0xff and -1 are two distinct values. You cannot store the value 255 in a char object; its value is -1. Integer constants, whether they're decimal, hexadecimal, or octal, specify values, not representations.
If you really want a one-byte object with the value 0xff, define it as an unsigned char, not as a char.
printf("%d %u",c,c);
When a value of an integer type narrower than int is passed to printf (or to any variadic function), it's promoted to int if that type can hold the type's entire range of values, or to unsigned int if it can't. For type char, it's almost certainly promoted to int. So this call is equivalent to:
printf("%d %u", -1, -1);
The output for the "%d" format is obvious. The output for "%u" is less obvious. "%u" tells printf that the corresponding argument is of type unsigned int, but you've passed it a value of type int. What probably happens is that the representation of the int value is treated as if it were of type unsigned int, most likely yielding UINT_MAX, which happens to be 4294967295 on your system. If you really want to do that, you should convert the value to type unsigned int. This:
printf("%d %u", -1, (unsigned int)-1);
is well defined.
Your two lines of code are playing a lot of games with various types, treating values of one type as if they were of another type, and doing implicit conversions that might yield results that are implementation-defined and/or depend on the choices your compiler happens to make.
Whatever you're trying to do, there's undoubtedly a cleaner way to do it (unless you're just trying to see what your implementation does with this particular code).
Let us start with the assumption using OP's "c, here is of signed type"
char c=0xff; // Implementation defined behavior.
0xff is a hexadecimal constant with the value of 255 and type of int.
... 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. §6.3.1.4 3
So right off, the value of c is implementation defined (ID). Let us assume the common ID behavior of 8-bit wrap-around, so c --> -1.
A signed char will be promoted to int as part of a variadic argument to printf("%d %u",c,c); is the same as printf("%d %u",-1, -1);. Printing the -1 with "%d" is not an issue and "-1" is printed.
Printing an int -1 with "%x" is undefined behavior (UB) as it is a mis-matched specifier/type and does not fall under the exception of being representable in both types. The common UB is to print the value as if it was converted to unsigned before being passed. When UINT_MAX == 4294967295 (4-bytes) that prints the value as -1 + (UINT_MAX + 1) or "4294967295"`.
So with ID and UB, you get a result, but robust code would be re-written to depend on neither.

Char multiplication in C

I have a code like this:
#include <stdio.h>
int main()
{
char a=20,b=30;
char c=a*b;
printf("%c\n",c);
return 0;
}
The output of this program is X .
How is this output possible if a*b=600 which overflows as char values lies between -128 and 127 ?
Whether char is signed or unsigned is implementation defined. Either way, it is an integer type.
Anyway, the multiplication is done as int due to integer promotions and the result is converted to char.
If the value does not fit into the "smaller" type, it is implementation defined for a signed char how this is done. Far by most (if not all) implementations simply cut off the upper bits.
For an unsigned char, the standard actually requires (briefly) cutting of the upper bits.
So:
(int)20 * (int)20 -> (int)600 -> (char)(600 % 256) -> 88 == 'X'
(Assuming 8 bit char).
See the link and its surrounding paragraphs for more details.
Note: If you enable compiler warnings (as always recommended), you should get a truncation warning for the assignment. This can be avoided by an explicit cast (only if you are really sure about all implications). The gcc option is -Wconversion.
First off, the behavior is implementation-defined here. A char may be either unsigned char or signed char, so it may be able to hold 0 to 255 or -128 to 127, assuming CHAR_BIT == 8.
600 in decimal is 0x258. What happens is the least significant eight bits are stored, the value is 0x58 a.k.a. X in ASCII.
This code will cause undefined behavior if char is signed.
I thought overflow of signed integer is undefined behavior, but conversion to smaller type is implementation-defined.
quote from N1256 6.3.1.3 Signed and unsigned integers:
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.
If the value is simply truncated to 8 bits, (20 * 30) & 0xff == 0x58 and 0x58 is ASCII code for X. So, if your system do this and use ASCII code, the output will be X.
First, looks like you have unsigned char with a range from 0 to 255.
You're right about the overflow.
600 - 256 - 256 = 88
This is just an ASCII code of 'X'.

Converting unsigned char to signed int

Consider the following program
void main(){
char t = 179;
printf("%d ",t);
}
Output is -77.
But binary representation of 179 is
10110011
So, shouldn't output be -51, considering 1st bit is singed bit.
Binary representation of -77 is
11001101
It seems bit order is reversed. What's going on? Please somebody advice.
You claimed that the binary representation of -77 is 11001101, but the reversal is yours. The binary representation of -77 is 10110011.
Binary 10110011 unsigned is decimal 179.
Binary 10110011 signed is decimal -77.
You assigned the out-of-range value 179 to a signed char. It might theoretically be Undefined Behaviour, but apart from throwing an error, it would be a very poor compiler that placed anything but that 8-bit value in the signed char.
But when printed, it's interpreted as a negative number because b7 is set.
Looks like char is a signed type on your system. The valid range for a char would be [-128, 127]
By using
char t = 179;
the compiler uses the 2's complement of 179 (which is most likely -77) and assigns that value to t.
To convert between a positive and a negative number in 2's complement you invert all the bits and then you add 1.
10110011
01001100 (invert all bits)
01001101 (add one)
That is 77 decimal
Char could be signed or unsigned: that's up to your compiler.
If signed, it could be 2s or 1 complement.
Furthermore, it could be larger than 8 bits, although sizeof char is defined to be 1.
So it's inadvisable to rely on a specific representation.
Please note, that on many systems char is signed. Hence, when you assign 179 (which is of type int) to a char this value is outside of char range, hence it is unspecified behaviour.
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 unsigned integer than the maximum value that can be represented in the new type until the value is in the range of the new conversion to 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.
If you change the type to unsigned char your program will perform correctly.
Also note that char, signed char and unsigned char are 3 distinct types, unlike int and signed int. The signedness of char is implementation defined.

Resources