Why do C compilers not warn when assigning integer value too high for signed type?

0

(assuming 64bit machine)

e.g.

int n = 0xFFFFFFFF; //max 32bit unsigned number
printf("%u\n", n);

The maximum positive number that a regular signed integer (32bit) can store is 0x7FFFFFFF.

In the above example I'm assigning the maximum unsigned integer value to a regular signed integer, I'm receiving no warnings or error from GCC, and the result is printed without problems (with -Wall -Wextra).

Appending U or L to the hex constant changes nothing.

Why is that?

c
integer
unsigned-integer
asked on Stack Overflow Jul 3, 2018 by bool3max

3 Answers

4

0xFFFFFFFF, on a platform where unsigned has a maximum value of 232-1, will have the type unsigned according to "6.4.4.1 Integer Constants" of the Standard.

And then we get to the conversion:

6.3.1.3 Signed and unsigned integers

1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.60)
3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

So, the result is implementation-defined or raises an implementation-defined signal.

Now, you print your int with the format %u, which is just plain mismatched. And while that is strictly speaking UB, you will likely get the original constant, assuming you have 2s-complement and the original assignment used wrap-around.

answered on Stack Overflow Jul 3, 2018 by Deduplicator • edited Jul 3, 2018 by Deduplicator
4

The C standard doesn't specify the behaviour but requires that the implementation specifies it. GCC always uses 2's complement representation and converts via truncation, therefore int32_t i = 0xFFFFFFFF; will result in i being set to -1 when compiled with GCC. On other compilers YMMV.


To get the warning from GCC you need to give the -Wsign-conversion flag:

% gcc 0xfffffff.c -c -Wsign-conversion                         
0xfffffff.c:1:9: warning: conversion of unsigned constant value to negative integer
        [-Wsign-conversion]
 int i = 0xFFFFFFFF;
         ^~t ~~~~~~~~

In general C compilers by default produce warnings only about very blatant errors and of constraint violations. The -Wsign-conversion would make many compilations very noisy - even those that are well-defined, like:

unsigned char c = '\x80';

which produces

unsignedchar.c:1:19: warning: negative integer implicitly converted to unsigned type
         [-Wsign-conversion]
 unsigned char c = '\x80';
                   ^~~~~~

on implementations where char is signed.

answered on Stack Overflow Jul 4, 2018 by Antti Haapala • edited Jul 4, 2018 by Antti Haapala
3

Assume that int and unsigned int are 32 bits, which is the case on most platforms you're likely to be using (both 32-bit and 64-bit systems). Then the constant 0xFFFFFFFF is of type unsigned int, and has the value 4294967295.

This:

int n = 0xFFFFFFFF;

implicitly converts that value from unsigned int to int. The result of the conversion is implementation-defined; there is no undefined behavior. (In principle, it can also cause an implementation-defined signal to be raised, but I know of no implementations that do that).

Most likely the value stored in n will be -1.

printf("%u\n", n);

Here you use a %u format specifier, which requires an argument of type unsigned int, but you pass it an argument of type int. The standard says that values of corresponding signed and unsigned type are interchangeable as function arguments, but only for values that are within the range of both types, which is not the case here.

This call does not perform a conversion from int to unsigned int. Rather, an int value is passed to printf, which assumes that the value it received is of type unsigned int. The behavior is undefined. (Again, this would be a reasonable thing to warn about.)

The most likely result is that the int value of -1, which (assuming 2's-complement) has the same representation as 0xFFFFFFFF, will be treated as if it were an unsigned int value of 0xFFFFFFFF, which is printed in decimal as 4294967295.

You can get a warning on int n = 0xFFFFFFFF; by using the -Wconversion or -Wsign-conversion option. These option are not included in -Wextra or -Wall. (You'd have to ask the gcc maintainers why.)

I don't know of an option that will cause a warning on the printf call.

(Of course the fix is to define n as an unsigned int, which makes everything correct and consistent.)

answered on Stack Overflow Jul 4, 2018 by Keith Thompson • edited Nov 19, 2018 by Keith Thompson

User contributions licensed under CC BY-SA 3.0