C语言输出变量和表达式结果不一致问题

书里看到这样一个题目,让读者看看下面代码两次输出结果是否一致,为什么

int i = 0xcffffff3;
printf("%x\n", 0xcffffff3>>2);
printf("%x\n", i>>2);

输出结果

33fffffc
f3fffffc

下面列出分析思路。

  1. 移位结果计算
    0xcffffff3: 1100 1111 1111 1111 1111 1111 1111 0011
    右移两位后: 11 0011 1111 1111 1111 1111 1111 1100
    对应十六进制结果:
    33fffffc
  2. 那么f3fffffc是怎么来的呢?
    0xcffffff3:1100 1111 1111 1111 1111 1111 1111 0011
    0xf3fffffc:1111 0011 1111 1111 1111 1111 1111 1100
    对比0xcffffff3的二进制明显是右移时补位填1了
  3. 打印二者类型

将代码改为如下,执行

    int i = 0xcffffff3;
    printf("%x\n", 0xcffffff3>>2);
    printf("%x\n", i>>2);

结果

root@lssd:~/lumut/test_all# gcc xm.c -o xm -g -Wall
xm.c:15:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]
   15 | void main()
      |      ^~~~
xm.c: In function ‘main’:
xm.c:24:14: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘unsigned int’ [-Wformat=]
   24 |     printf("%s\n", 0xcffffff3>>2);//33fffffc
      |             ~^     ~~~~~~~~~~~~~
      |              |     |
      |              |     unsigned int
      |              char *
      |             %d
xm.c:25:14: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
   25 |     printf("%s\n", i>>2);//f3fffffc
      |             ~^     ~~~~
      |              |      |
      |              char * int
      |             %d

显然移位表达式是无符号型,变量i是int型。
C语言定义int类型时,默认是有符号型,所以这里变量i首位是1,被处理为负数,负数右移补位有可能补0也有可能补1,x86平台的gcc编译器,最高位是移入1的。
而表达式0xcffffff3>>2中0xcffffff3首位是1,编译器处理成无符号整型,因为c的二进制首位是1,如果是有符号数,则会处理成负数。
5.验证上述思路,修改代码增加二进制首位不为1的十六进制数进行移位,如下:

    int i = 0xcffffff3;
    printf("%x\n", 0xcffffff3>>2);
    printf("%x\n", i>>2);
    printf("%s",i);
    printf("%s",0x0ffffff3);

结果

root@lssd:~/lumut/test_all# gcc xm.c -o xm -g
xm.c: In function ‘main’:
xm.c:26:14: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
   26 |     printf("%s",i);
      |             ~^  ~
      |              |  |
      |              |  int
      |              char *
      |             %d
xm.c:27:14: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
   27 |     printf("%s",0x0ffffff3);
      |             ~^  ~~~~~~~~~~
      |              |  |
      |              |  int
      |              char *
      |             %d

二者都是int型了。

5.总结
1.0x3ffffff3和0xcffffff3这两个常量虽然看起来差不多,但前者是int型,而后者是unsigned int型;
2.负数的移位运算应注意编译器运算的结果。

书籍:《Linux C编程一站式学习》 作者:宋劲杉

上一篇:今天开始Linux的学习


下一篇:封装继承多态