简体   繁体   中英

C signed arithmetic: difference in representation between literals and variables

The following program:

#include <limits.h>
#include <stdio.h>

int main() {
  long int max = LONG_MAX;
  long int max_plus_one = max + 1;

  printf(" max \t\t %lx\n LONG_MAX \t %lx\n", max, LONG_MAX);
  printf(" max_plus_one \t %lx\n max + 1 \t %lx\n LONG_MIN \t %lx\n\n",\
      max_plus_one, max + 1, LONG_MIN);

  printf(" max == LONG_MAX? \t\t %s\n", max == LONG_MAX ? "true" : "false");
  printf(" max_plus_one == LONG_MIN ? \t %s\n",\
      max_plus_one == LONG_MIN ? "true" : "false");
  printf(" max + 1 == max_plus_one? \t %s\n",\
      max + 1 == max_plus_one ? "true" : "false");
  printf(" max + 1 == LONG_MIN? \t\t %s\n", max + 1 == LONG_MIN ? "true" : "false");
}

Outputs the following:

max            7fffffffffffffff
LONG_MAX       7fffffffffffffff
max_plus_one   8000000000000000
max + 1        8000000000000000
LONG_MIN       8000000000000000

max == LONG_MAX?               true
max_plus_one == LONG_MIN ?     true
max + 1 == max_plus_one?       true
max + 1 == LONG_MIN?           false

Why is the expression (max + 1) not equal to LONG_MIN?
What is it equal to then?
If I try to printf max + 1 as an expression I do get 8000000000000000

Really baffled about this!

--EDIT

As discussed in the comments and answers below signed overflow is undefined and the compiler can do as it pleases, so we have no business questioning its oddities.

Inspired by the comments below I checked the assembly code generated by my platform (gcc on a linode running ubuntu) and what is happening is that the compiler is simply deciding the result of the last equality to be false without performing the actual equality check (which would otherwise result in a true value)

The behaviour on overflowing a signed integral type is undefined in C.

Your outputs that are contingent on the evaluation of max + 1 are manifestations of that undefined behaviour. (Formally this means that your entire program is undefined.)

Note that LONG_MIN will probably be defined as -<value> - 1 where <value> is the same number as LONG_MAX . This obviates any overflow or argument promotion effects in a 2's complement system.

As Bathsheba's answer correctly points out, signed integer overflow has undefined behavior.

Having said that, the result of evaluating LONG_MAX + 1 is likely to be LONG_MIN on most implementations. The reason you're not seeing a negative value when you print it is that you're printing it incorrectly.

long int max = LONG_MAX;
long int max_plus_one = max + 1;

printf(" max \t\t %lx\n LONG_MAX \t %lx\n", max, LONG_MAX);
printf(" max_plus_one \t %lx\n max + 1 \t %lx\n LONG_MIN \t %lx\n\n",\
       max_plus_one, max + 1, LONG_MIN);

The %lx specifier requires an argument of type unsigned long int . You're giving it an argument of type (signed) long int . In particular, you're giving it a negative value.

Usually if you pass a numeric argument to a function expecting a different numeric type, the value will be converted. That's possible because the compiler knows what type the function expects. For printf , however, the expected type is determined by the format string, which isn't (necessarily) processed until run time. It's likely to interpret the contents of the long int object as if it were an object of type unsigned long int , but strictly speaking the behavior is undefined.

Here's a simpler example where the number being printed was set to LONG_MIN without any overflow:

#include <stdio.h>
#include <limits.h>
int main(void) {
    unsigned long num = LONG_MIN;
    printf("num = %ld\n", num);
    printf("num = 0x%lx (undefined behavior)\n", num);
}

The output on my system is:

num = -9223372036854775808
num = 0x8000000000000000 (undefined behavior)

The positive value printed by the second printf does not indicate that num has a positive value, just that it's being printed incorrectly.

You can use %lx to print a signed value if it's within the range of both long and unsigned long . There is no format specifier that will print an arbitrary signed value in hexadecimal.

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