Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subtracting unsigned ints in C and getting -Wconversion warnings

Tags:

c

gcc

Consider:

uint16_t x;
uint16_t y;

y = 0;
x = y - 1;

X will be some insane number. See other SO articles for the "why" and to learn about 2's compliment.

(I'm not asking why this is the case. I know why. I'm asking something else. Read on.)

If you turn on the gcc flag -Wconversion, this perfectly valid C code above will throw conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value That's the compiler trying to help us not mess up. And I tend to program with such warnings turned on because they help me avoid messing up.

However, there are realistic cases where we might use an unsigned int as a counter and want to subtract 1 and NOT get this warning. Also, we want to use it as a counter, get it down to zero and then just stop. To do this in C, we have to put in some kind of a check. However, that kind of check code will run foul of the gcc warning.

Like the code above.

I want my cake and I want to eat it too.

What's the elegant solution for this?

like image 733
101010 Avatar asked Dec 03 '22 19:12

101010


2 Answers

In this code

x = y - 1;

The argument y is subject to "default integer promotion" per the C standard.

That means y - 1 is an int value, and then that int result gets assigned back to a uint16_t via truncation. That's what GCC is complaining about.

The way to fix the problem is to explicitly cast the result to uint16_t:

x = ( uint16_t ) ( y - 1 );

The full explanation can be found in 6.3.1.1 Boolean, characters, and integers:

Every integer type has an integer conversion rank defined as follows:

  • No two signed integer types shall have the same rank, even if they have the same representation.
  • The rank of a signed integer type shall be greater than the rank of any signed integer type with less precision.
  • The rank of long long int shall be greater than the rank of long int, which shall be greater than the rank of int, which shall be greater than the rank of short int, which shall be greater than the rank of signed char.
  • The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any.
  • The rank of any standard integer type shall be greater than the rank of any extended integer type with the same width.
  • The rank of char shall equal the rank of signed char and unsigned char.
  • The rank of _Bool shall be less than the rank of all other standard integer types.
  • The rank of any enumerated type shall equal the rank of the compatible integer type (see 6.7.2.2).
  • The rank of any extended signed integer type relative to another extended signed integer type with the same precision is implementation-defined, but still subject to the other rules for determining the integer conversion rank.
  • For all integer types T1, T2, and T3, if T1 has greater rank than T2 and T2 has greater rank than T3, then T1 has greater rank than T3.

The following may be used in an expression wherever an int or unsigned int may be used:

  • An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.
  • A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.

The integer promotions preserve value including sign. As discussed earlier, whether a "plain" char is treated as signed is implementation-defined.

like image 25
Andrew Henle Avatar answered Mar 09 '23 01:03

Andrew Henle


Explicitly cast y-1 to uint16_t. GCC is complaining not about the subtraction, but that you might lose information after the result of the expression is assigned to a (now smaller) type. (Integral operands are silently promoted to int when used in expressions, when the types are smaller and could fit in an signed int without losing precision.)

like image 159
Kitten Avatar answered Mar 09 '23 00:03

Kitten