1. 34

  2. 14

    char can be either signed or unsigned, it’s up to the implementation

    In practice, this means “depends on CPU architecture”. Unixes on x86/amd64 keep it signed in order to not break stuff. But signed chars don’t make sense, so on newer architectures like aarch64 and riscv it’s unsigned. As a result… char is one giant portability footgun. I’ve had to send a patch to WebKit (of all things) to fix a signed char issue that broke the build on FreeBSD/aarch64.

    • so, don’t use char as anything other than an opaque type;
    • if you need to do actual computations on chars, use unsigned char or signed char;
    • if the program has already been written with signed chars in mind, put -fsigned-char into CFLAGS!
    1. 2

      This post was also featured in the 200th episode of CppCast starring Herb Sutter.

      1. 2

        that’s okay because char + char is a nonsense operation, it makes no more sense than date + date

        1. -1

          Why would everyone ever want to know? Isnt this why inttypes.h exists? Is int8_t and uint8_t not more clear?

          1. 10
            #include <stdio.h>
            #include <stdint.h>
            void f( uint8_t ) { printf( "u8\n" ); }
            void f( int ) { printf( "int\n" ); }
            int main() {
                    uint8_t a = 1;
                    uint8_t b = 2;
                    f( a + b );
                    return 0;
            > ./a.out

            The problem is that all arithmetic gets promoted to int, using explicitly sized types doesn’t make any difference.

            1. 3

              Although in that case, since int has to be at least 16-bits per the standard, uint8_t + uint8_t must always call the int overload (because it always fits), never the unsigned int overload. On the other hand, while char can do weird things like be as large as int (as described in the post), uint8_t can simply not exist if the implementation doesn’t support it.

            2. 3

              Because you have two overloaded functions, one that takes a char as an input and one that takes an integer as input.

              They have different behaviour, then you need to know which one will resolve :-)