简体   繁体   中英

Why does GCC give a warning when setting an unsigned long to 2^64-1?

The C standard states that a long int is at least 4 bytes - on my system it is 8 bytes.

This means I can store values up to 2^ 63 -1 in a long and 2 64 -1 in an unsigned long .

However, when the following code is compiled with the -Wall flag it gives the warning [Wimplicitly-unsigned-literal] :

int main (int argc, char ** argv) {

  unsigned long a;
  a = 18446744073709551615; // 2^64-1

}

If I use 2 63 -1 (9223372036854775807) instead, it compiles with no warnings (as expected - 2 63 -1 will fit in a signed long int ).

For a project I needed to have the maximum value in an unsigned long , and I discovered that (9223372036854775807 << 1) + 1 will not raise this warning. My teacher then suggested that I could use the ULONG_MAX defined in limits.h and this gave no warnings.

Why can't I do this without a warning saying it was converted implicitly - when I declared it explicitly?

Per the C standard, the type of a decimal constant without a suffix is int , long int , or long long int , specifically the first of those that is sufficient to represent the value. In your C implementation, none of those can represent 18446744073709551615, because it is too large.

In order to accommodate you, the compiler is making its type unsigned long . Technically, this does not conform to the C standard, so the compiler is warning you.

In this case, no harm is caused, because you are assigning the value to an unsigned long . But there are situations in which using the wrong type can cause problems, so generally you should append a suffix to such constants to ensure they match how they are intended to be used. In this case, a u is sufficient; as with unsuffixed types, the compiler will decide whether to use unsigned int , unsigned long int , or unsigned long long int depending on the magnitude of the number and the capabilities of the types.

You declared it explicitly, but without an U, which would make it unsigned. As there is no signed integer constant with this value, it implicitly makes it unsigned, providing you with the information that you'd better make it explicit.

Do so with a = 18446744073709551615U; .

Check out this fancy table from the standard (6.4.4.1p5) :

标准的整数常量升级表

which explains how integer literals are fitted into integer types.

Basically, because your literal is decimal and doesn't have a suffix, it tries to fit into the following types:

int
long int
long long int

Since it won't fit into long long int , you're getting the warning you're getting.

If you add a U ( u ) suffix (or UL or UL or the lowercase variants):

unsigned long a = 18446744073709551615U;

you won't have that problem, because you'll move to the promotion sequence:

unsigned int
unsigned long int
unsigned long long int

(or the appropriate subsets for UL and UL ) and the literal will fit at unsigned long int (if that's indeed the first unsigned type with no less than 64 usable bits, as it usually is).

Alternatively, you can turn the warning off by switching to an octal or hexadecimal literal:

unsigned long a = 0xFFFFFFFFFFFFFFFF;
unsigned long octal_a = 01777777777777777777777;

as for these the promotion sequence is:

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int

as per the linked table, which again, allows them to fit into the first 64-bit large unsigned type (usually unsigned long int ).

What you should absolutely not do is:

(9223372036854775807 << 1) + 1 //don't do this!

as this invokes undefined behavior by breaking 6.5.7p4 regardless of whether a warning is generated or not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM