int main()
{
char c = 0xff;
bool b = 0xff == c;
// Under most C/C++ compilers' default options, b is FALSE!!!
}
Neither the C or C++ standard specify char as signed or unsigned, it is implementation-defined.
Why does the C/C++ standard not explicitly define char as signed or unsigned for avoiding dangerous misuses like the above code?
Historical reasons, mostly.
Expressions of type char are promoted to int in most contexts (because a lot of CPUs don't have 8-bit arithmetic operations). On some systems, sign extension is the most efficient way to do this, which argues for making plain char signed.
On the other hand, the EBCDIC character set has basic characters with the high-order bit set (i.e., characters with values of 128 or greater); on EBCDIC platforms, char pretty much has to be unsigned.
The ANSI C Rationale (for the 1989 standard) doesn't have a lot to say on the subject; section 3.1.2.5 says:
Three types of char are specified: signed, plain, and unsigned. A
plain char may be represented as either signed or unsigned, depending
upon the implementation, as in prior practice. The type signed char
was introduced to make available a one-byte signed integer type on
those systems which implement plain char as unsigned. For reasons of
symmetry, the keyword signed is allowed as part of the type name of
other integral types.
Going back even further, an early version of the C Reference Manual from 1975 says:
A char object may be used anywhere an int may be. In all cases the
char is converted to an int by propagating its sign through the upper
8 bits of the resultant integer. This is consistent with the two’s
complement representation used for both characters and integers.
(However, the sign-propagation feature disappears in other
implementations.)
This description is more implementation-specific than what we see in later documents, but it does acknowledge that char may be either signed or unsigned. On the "other implementations" on which "the sign-propagation disappears", the promotion of a char object to int would have zero-extended the 8-bit representation, essentially treating it as an 8-bit unsigned quantity. (The language didn't yet have the signed or unsigned keyword.)
C's immediate predecessor was a language called B. B was a typeless language, so the question of char being signed or unsigned did not apply. For more information about the early history of C, see the late Dennis Ritchie's home page, now moved here.
As for what's happening in your code (applying modern C rules):
char c = 0xff;
bool b = 0xff == c;
If plain char is unsigned, then the initialization of c sets it to (char)0xff, which compares equal to 0xff in the second line. But if plain char is signed, then 0xff (an expression of type int) is converted to char -- but since 0xff exceeds CHAR_MAX (assuming CHAR_BIT==8), the result is implementation-defined. In most implementations, the result is -1. In the comparison 0xff == c, both operands are converted to int, making it equivalent to 0xff == -1, or 255 == -1, which is of course false.
Another important thing to note is that unsigned char, signed char, and (plain) char are three distinct types. char has the same representation as either unsigned char or signed char; it's implementation-defined which one it is. (On the other hand, signed int and int are two names for the same type; unsigned int is a distinct type. (Except that, just to add to the frivolity, it's implementation-defined whether a bit field declared as plain int is signed or unsigned.))
Yes, it's all a bit of a mess, and I'm sure it would have be defined differently if C were being designed from scratch today. But each revision of the C language has had to avoid breaking (too much) existing code, and to a lesser extent existing implementations.
char at first is meant to store characters, so whether it's signed or unsigned is not important. What really matters is how to perform maths on char efficiently. So depend on the system, the compiler will choose what's most appropriate
Prior to ARMv4, ARM had no native support for loading halfwords and signed bytes. To load a signed byte you had to LDRB then sign extend the value (LSL it up then ASR it back down). This is painful so char is unsigned by default.
why unsigned types are more efficent in arm cpu?
In fact a lot of ARM compilers still use unsigned char by default, because even if you can load a byte with sign extension on modern ARM ISAs, that instruction is still less flexible than the zero extension version
is char signed or unsigned by default on iOS?
char is unsigned by default on Android NDK
And most modern compilers also allow you to change char's signness instead of using the default setting
Related
since C language using the char as integer internally(correspondent ASCII is stored). for internal calculation we can use signed and unsigned char.
other than that, any other use??
signed and unsigned char are first and foremost just small integers. Do you need to store a large quantity of small numbers (in the range [-127, +127]¹ or [0, 255])? You can use an array of signed or unsigned chars and save memory compared to pretty much any other type. That's what is done for e.g. images - a grayscale image is generally stored as an array of unsigned char (and an RGB image is generally stored as an array of 3 unsigned char components).
The second usage of char is for character strings, which you probably already saw; notice that char is a distinct type from both signed char and unsigned char, and its signedness is implementation defined. This is stupid and inconvenient in many situations - and leads to sad stuff such as the mandatory cast to unsigned char when calling functions of the toupper/isupper family.
Finally, char & co. are defined as the "underlying storage" of the C abstract machine. sizeof(char) == 1 by definition, and any type can be aliased through a (signed|unsigned)? char pointer to access its underlying bit representation.
Yes, -127; [-127, +127] is the minimum range allowed for signed char by the standard, as it still allows sign and magnitude representation; more realistic, on any real-world machine of this century it will be at least [-128, 127].
In the book "Complete Reference of C" it is mentioned that char is by default unsigned.
But I am trying to verify this with GCC as well as Visual Studio. It is taking it as signed by default.
Which one is correct?
The book is wrong. The standard does not specify if plain char is signed or unsigned.
In fact, the standard defines three distinct types: char, signed char, and unsigned char. If you #include <limits.h> and then look at CHAR_MIN, you can find out if plain char is signed or unsigned (if CHAR_MIN is less than 0 or equal to 0), but even then, the three types are distinct as far as the standard is concerned.
Do note that char is special in this way. If you declare a variable as int it is 100% equivalent to declaring it as signed int. This is always true for all compilers and architectures.
As Alok points out, the standard leaves that up to the implementation.
For gcc, the default is signed, but you can modify that with -funsigned-char. note: for gcc in Android NDK, the default is unsigned. You can also explicitly ask for signed characters with -fsigned-char.
On MSVC, the default is signed but you can modify that with /J.
C99 N1256 draft 6.2.5/15 "Types" has this to say about the signed-ness of type char:
The implementation shall define char to have the same range, representation, and behavior as either signed char or unsigned char.
and in a footnote:
CHAR_MIN, defined in <limits.h>, will have one of the values 0 or SCHAR_MIN, and this can be used to distinguish the two options. Irrespective of the choice made, char is a separate type from the other two and is not compatible with either.
According to The C Programming Language book by Dennis Ritchie which is the de-facto standard book for ANSI C, plain chars either signed or unsigned are machine dependent, but printable characters are always positive.
According to the C standard the signedness of plain char is "implementation defined".
In general implementors chose whichever was more efficient to implement on their architecture. On x86 systems char is generally signed. On arm systems it is generally unsigned (Apple iOS is an exception).
Now, we known the standard leaves that up to the implementation.
But how to check a type is signed or unsigned, such as char?
I wrote a macro to do this:
#define IS_UNSIGNED(t) ((t)~1 > 0)
and test it with gcc, clang, and cl. But I do not sure it's always safe for other cases.
What is the need for signed and unsigned characters in C?
Is there some special reason for having a signed and unsigned char in C? Or was it simply added for completeness so that the compiler does not have to check the data type before adding signed/unsigned modifier?
I am not asking about signed and unsigned variables. My doubt is about the special cases where an unsigned character variable will not be sufficient such that you have to depend on a signed character variable.
A char can be either signed or unsigned depending on what is most efficient for the underlying hardware. The keywords signed and unsigned allow you to explicitly specify that you want something else.
A quote from the C99 rationale:
Three types of char are specified: signed, plain, and unsigned. A plain char may be represented as either signed or unsigned depending upon the implementation, as in prior practice. The type signed char was introduced in C89 to make available a one-byte signed integer type on those systems which implemented plain char as unsigned char. For reasons of symmetry, the keyword signed is allowed as part of the type name of other integer types.
Information #1: char in C is just a small int, which uses 8 bits.
Information #2: Difference between signed and unsigned, is that one bit in the representation is used as the sign bit for a signed variable.
Information #3: As a result of (#2), signed variables hold different ranges (-128 to 127, in char case) compared to unsigned (0 to 255 in char case).
Q-A #1: why do we need unsigned?
In most cases (for instance representing a pointer) we do not need signed variables. By convention all locations in the memory are exposed to the program as a contiguous array of unsigned addresses.
Q-A #2: why do we need signed?
Generally, to do signed arithmetic.
I assume you are using a char to hold numbers, not characters.
So:
signed char gives you at least the -128 to 127 range.
unsigned char gives you at least the 0 to 255 range.
A char is required by standard to be AT LEAST 8 bits, so that is the reason for my saying at least. It is possible for these values to be larger.
Anyway, to answer your question, having a char as unsigned frees the requirement for the first bit to be the 'sign' bit, thus allowing you to hold near double that of a signed char.
The thing you have to understand is that datatype "char" is actually just an integer, typically 8-bits wide. You can use it like any other inter datatype, assuming you respect the reduced value limits. There is no reason to limit "char" to characters.
On a 32/64-bit processor, there is typically no need to use such small integer fields, but on an 8-bit processor such as the 8051, 8-bit integers are no only much faster to process and use less (limited) memory.
In the book "Complete Reference of C" it is mentioned that char is by default unsigned.
But I am trying to verify this with GCC as well as Visual Studio. It is taking it as signed by default.
Which one is correct?
The book is wrong. The standard does not specify if plain char is signed or unsigned.
In fact, the standard defines three distinct types: char, signed char, and unsigned char. If you #include <limits.h> and then look at CHAR_MIN, you can find out if plain char is signed or unsigned (if CHAR_MIN is less than 0 or equal to 0), but even then, the three types are distinct as far as the standard is concerned.
Do note that char is special in this way. If you declare a variable as int it is 100% equivalent to declaring it as signed int. This is always true for all compilers and architectures.
As Alok points out, the standard leaves that up to the implementation.
For gcc, the default is signed, but you can modify that with -funsigned-char. note: for gcc in Android NDK, the default is unsigned. You can also explicitly ask for signed characters with -fsigned-char.
On MSVC, the default is signed but you can modify that with /J.
C99 N1256 draft 6.2.5/15 "Types" has this to say about the signed-ness of type char:
The implementation shall define char to have the same range, representation, and behavior as either signed char or unsigned char.
and in a footnote:
CHAR_MIN, defined in <limits.h>, will have one of the values 0 or SCHAR_MIN, and this can be used to distinguish the two options. Irrespective of the choice made, char is a separate type from the other two and is not compatible with either.
According to The C Programming Language book by Dennis Ritchie which is the de-facto standard book for ANSI C, plain chars either signed or unsigned are machine dependent, but printable characters are always positive.
According to the C standard the signedness of plain char is "implementation defined".
In general implementors chose whichever was more efficient to implement on their architecture. On x86 systems char is generally signed. On arm systems it is generally unsigned (Apple iOS is an exception).
Now, we known the standard leaves that up to the implementation.
But how to check a type is signed or unsigned, such as char?
I wrote a macro to do this:
#define IS_UNSIGNED(t) ((t)~1 > 0)
and test it with gcc, clang, and cl. But I do not sure it's always safe for other cases.
From The C Programming Language (Brian W. Kernighan), 2.7 TYPE CONVERSIONS, pg 43 :
"There is one subtle point about the
conversion of characters to integers.
... On some macines a char whose
leftmost bit is 1 will be converted to
a negative integer. On others, ... is
always positive. For portability,
specify signed or unsigned if
non-character data is to be stored in
char variables."
My questions are:
Why would anyone want to store
non-char data in char? (an example
where this is necessary will be real
nice)
Why does integer value of char
change when it is converted to int?
Can you elaborate more on this
portability issue?
In regards to 1)
People often use char arrays when they really want a byte buffer for a data stream. Its not great practice, but plenty of projects do it, and if you're careful, no real harm is done. There are probably other times as well.
In regards to 2)
Signed integers are often sign extended when they are moved from a smaller data type. Thus
11111111b (-1 in base 10) becomes 11111111 11111111 11111111 11111111 when expanded to 32 bits. However, if the char was intended to be unsigned +255, then the signed integer may end up being -1.
About portability 3)
Some machines regard chars as signed integers, while others interpret them as unsigned. It could also vary based on compiler implementation. Most of the time you don't have to worry about it. Kernighan is just trying to help you understand the details.
Edit
I know this is a dead issue, but you can use the following code to check if char's on your system are signed or unsigned:
#include <limits.h> //Include implementation specific constants (MAX_INT, et c.)
#if CHAR_MAX == SCHAR_MAX
// Plain "char" is signed
#else
// Plain "char" is unsigned
#endif
1) char is the size of a single byte in C, and is therefore used for storing any sort of data. For example, when loading an image into memory, the data is represented as an array of char. In modern code, typedefs such as uint8_t are used to indicate the purpose of a buffer more usefully than just char.
2 & 3) Whether or not char is signed or unsigned is platform dependent, so if a program depends on this behavior then it's best to specify one or the other explicitly.
The char type is defined to hold one byte, i.e. sizeof(char) is defined to be 1. This is useful for serializing data, for instance.
char is implementation-defined as either unsigned char or signed char. Now imagine that char means smallint. You are simply converting a small integer to a larger integer when you go from smallint to int. The problem is, you don't know whether that smallint is signed or unsigned.
I would say it's not really a portability issue as long as you follow The Bible (K&R).
unsigned char is often used to process binary data one byte at a time. A common example is UTF-8 strings, which are not strictly made up of "chars."
If a signed char is 8 bits and the top bit is set, that indicates that it's negative. When this is converted to a larger type, the sign is kept by extending the high bit to the high bit of the new type. This is called a "sign-extended" assignment.
1) Char is implemented as one byte across all systems so it is consistent.
2) The bit mentioned in you question is the one that is used in single byte integers for their singed-ness. When a int on a system is larger than one byte the signed flat is not affected when you convert char to int, other wise it is. ( there are also singed and unsigned chars)
3) Because of the consistence of the char implementation lots of libs use them like the Intel IPP (Intel Performance Primitives) libs and their cousins OpenCV.
Usually, in C, char to int conversion and vice versa is an issue because the stanard APIs for reading character input/writing character output use int's for the character arguments and return values. See getchar(), getc() and putchar() for example.
Also, since the size of a char is 1 byte, it is a convenient way to deal with arbitrary data as a byte stream.