Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enable compiler warning when comparing char and unsigned char?

take for example the following C code :

int main(int argc, char *argv[])
{
    signed char i;
    unsigned char count = 0xFF;

    for (i=0; i<count;i++)
    {
        printf("%x\n", i);
    }
    return 0;
}

This code runs in an infinite loop, even if I compile it as follows :

# gcc -Wall -Wpedantic -Wconversion -Wsign-compare -Wtype-limits -Wsign-conversion test.c -o test

Does someone know for a compiler flag that should warn about those kind of issues ?

Just to be clear, I'm not asking 'why is get an infinite loop', but to know if there's a way to prevent it using a compiler or static analysis ?

like image 889
sagi Avatar asked Oct 31 '16 11:10

sagi


People also ask

Is char the same as unsigned char?

A signed char is a signed value which is typically smaller than, and is guaranteed not to be bigger than, a short . An unsigned char is an unsigned value which is typically smaller than, and is guaranteed not to be bigger than, a short .

Is char default signed or unsigned?

In the book "Complete Reference of C" it is mentioned that char is by default unsigned.

Why do we need signed and unsigned char in C?

Signed char and unsigned char both are used to store single character. The variable stores the ASCII value of the characters. For an example if 'A' is stored, actually it will hold 65. For signed char we need not to write the signed keyword.

What is meant by signed and unsigned char in C?

The XDR standard defines signed integers as integer. A signed integer is a 32-bit datum that encodes an integer in the range [-2147483648 to 2147483647]. An unsigned integer is a 32-bit datum that encodes a nonnegative integer in the range [0 to 4294967295].


3 Answers

The flag -Wconversion won't catch the error, because both operands in the comparison: i<count, get promoted to int using integer promotions.

There is no flag in gcc that would catch this.

That aside, the behavior of your code is undefined, because variable i overflows, when it has the value 0x7F and is incremented: i++.

If you want to iterate up to some value, make sure the type you're using can represent that value.

like image 57
2501 Avatar answered Oct 20 '22 04:10

2501


i is a signed char, incrementing it beyond SCHAR_MAX has an implementation defined effect. The computation i + 1 is performed after promotion of i to int and it does not overflow (unless sizeof(int) == 1 and SCHAR_MAX == INT_MAX). Yet this value is beyond the range of i and since i has a signed type, either the result is implementation-defined or an implementation-defined signal is raised. (C11 6.3.1.3p3 Signed and unsigned integers).

By definition, the compiler is the implementation, so the behavior is defined for each specific system and on x86 architectures where storing the value results in masking the low-order bits, gcc should be aware that the loop test is definitely constant, making it an infinite loop.

Note that clang does not detect the constant test either, but clang 3.9.0 will if count is declared as const, and it does issue a warning if i < count is replaced with i < 0xff, unlike gcc.

Neither compiler complains about the signed / unsigned comparison issue because both operands are actually promoted to int before the comparison.

You found a meaningful issue here, especially significant because some coding conventions insist on using the smallest possible type for all variables, resulting in such oddities as int8_t or uint8_t loop index variables. Such choices are indeed error-prone and I have not yet found a way to get the compiler to warn the programmer about silly errors such as the one you posted.

like image 42
chqrlie Avatar answered Oct 20 '22 04:10

chqrlie


Since i is a signed char, its value ranges from -128 to 127 typically. Whereas count being an unsigned char is assigned the value 255 (0xFF).

Inside the loop, when i value gets to 127 and is incremented again, it never reaches 128 and gets to -128 and then gets again to 127 and then again rolls over to -128, and so on. The value of i will be forever less than the value of count inside the loop and so the loop can never terminate!

This is happening because of the overflow of the data type and care must be taken to critically examine the expressions where automatic type coercions may take place as they will not issue any warning.

EDIT: From the GCC documentation,

-Wconversion: Warn for implicit conversions that may alter a value.

Here, we are getting inconsistency due to comparison and not assignment.

like image 39
skrtbhtngr Avatar answered Oct 20 '22 03:10

skrtbhtngr