18

Apparently there is a possibility that plain char can be either signed or unsigned by default. Stroustrup writes:

It is implementation-defined whether a plain char is considered signed or unsigned. This opens the possibility for some nasty surprises and implementation dependencies.

How do I check whether my chars are signed or unsigned? I might want to convert them to int later, and I don't want them to be negative. Should I always use unsigned char explicitly?

Oleksiy
  • 37,477
  • 22
  • 74
  • 122
  • @MitchWheat: Yes. Examples I've encountered: Cray T90, Cray SV1, Cray T3E, SGI MIPS IRIX, IBM PowerPC AIX. And any system that uses EBCDIC pretty much has to make plain `char` unsigned. – Keith Thompson Aug 14 '13 at 00:26
  • @KeithThompson: You should add your list as an answer to [this question](http://stackoverflow.com/questions/3728045/any-compiler-which-takes-char-as-unsigned) – jxh Aug 14 '13 at 00:31
  • You read the compiler's documentation. – Pete Becker Aug 14 '13 at 12:51
  • 2
    @PeteBecker: That doesn't necessarily help if you want to check at compile time or run time (though it's usually better to write code that doesn't care whether `char` is signed or unsigned, or to use `signed char` or `unsigned char` if you do care). – Keith Thompson Aug 14 '13 at 13:44
  • @KeithThompson - yes, obviously, which is why I posted a comment and not an answer. – Pete Becker Aug 14 '13 at 13:53
  • 1
    @PeteBecker: It's obvious to you and me, but might not be to everyone else reading this. – Keith Thompson Aug 14 '13 at 13:59
  • @KeithThompson - that's why I posted a comment and not an answer. – Pete Becker Aug 14 '13 at 14:02

5 Answers5

28

From header <limits>

std::numeric_limits<char>::is_signed

http://en.cppreference.com/w/cpp/types/numeric_limits/is_signed

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
21

Some alternatives:

const bool char_is_signed = (char)-1 < 0;

#include <climits>
const bool char_is_signed = CHAR_MIN < 0;

And yes, some systems do make plain char an unsigned type. Examples I've encountered: Cray T90, Cray SV1, Cray T3E, SGI MIPS IRIX, IBM PowerPC AIX. And any system that uses EBCDIC pretty much has to make plain char unsigned so that all basic characters have non-negative values. (And some compilers have an option to control the signedness of char, such as gcc's -fsigned-char and -funsigned-char.)

But std::numeric_limits<char>::is_signed, as suggested by Benjamin Lindley's answer, probably expresses the intent more clearly.

(On the other hand, the methods I suggested can also be applied to C.)

Community
  • 1
  • 1
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
0

Using unsigned char "always" could give you some interesting surprises, as the majority of C-style functions like printf, fopen, will use char, not unsigned char.

edit: Example of "fun" with C-style functions:

const unsigned char *cmd = "grep -r blah *.txt";
FILE *pf = popen(cmd, "r"); 

will give errors (in fact, I get one for the *cmd = line, and one error for the popen line). Using const char *cmd = ... will work fine. I picked popen because it's a function that isn't trivial to replace with some standard C++ functionality - obviously, printf or fopen can quite easily be replaced with some iostream or fstream type functionality, which generally has alternatives that take unsigned char as well as char.

However, if you are using > or < on characters that are beyond 127, then you will need to use unsigned char (or some other solution, such as casting to int and masking the lower 8 bits). It is probably better to try to avoid direct comparisons (in particular when it comes to non-ASCII characters - they are messy anyway, because there are often several variants depending on locale, character encodings, etc). Comparing for equality should work however.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
0

Yes, if you want to use a char type and you always want it to be unsigned, use unsigned char. Note that unlike the other fundamental integer types, unsigned char is a different type from char -- even on systems where char is unsigned. Also, conversion from char to int ought to be lossless so if your result is incorrect, your source char value may also be incorrect.

The cleanest way to test whether char is unsigned depends on whether you need it to be a preprocessor test and on which version of C++ you are targeting.

To conditionally compile code using a preprocessor test, the value of CHAR_MIN should work:

#include <climits>

#if (CHAR_MIN==0)
// code that relies on char being unsigned
#endif

In C++17, I would use std::is_signed_v and std::is_unsigned_v:

#include <type_traits>

static_assert(std::is_unsigned_v<char>);
// code that relies on char being unsigned

If you are writing against C++11 or C++14 you need the slightly more verbose std::is_signed and std::is_unsigned:

#include <type_traits>

static_assert(std::is_unsigned<char>::value, "char is signed");
// code that relies on char being unsigned

For all revisions of C++, @benjamin-lindley's solution is a good alternative.

John McFarlane
  • 5,528
  • 4
  • 34
  • 38
-1

You can use preprocessor command:

 #define is_type_signed(my_type) (((my_type)-1) < 0)
Eldar Agalarov
  • 4,849
  • 4
  • 30
  • 38