我是C语言的初学者,对它的怪癖很感兴趣.为什么这段代码还能工作呢?

#include <stdio.h>

int main() {
    unsigned char i = 126;
    i = (i << 1000000000000000000000000000000000) % 10;
    printf("%d", i);

    return 0;
}

i实际上可以处理高达6位的移位,但我能够做到这一点.我用来移位的值实际上并不重要,除非我大于6.我得到的值是6,而不是0.我相信这是正确的答案,因为我try 了宽度较大的数据类型和合理的左移量.

显然,我知道我使用的是模函数,但我的问题是,它不应该用实际上会导致0的值进行位移位,然后对它执行mod吗?

推荐答案

它不"工作",但实际上是undefined behavior,这意味着不能保证程序会做什么.事实上,未定义行为的表现方式之一是代码看起来工作正常.

如果您在编译时启用了GCC中的警告(即-Wall -Wextra),您将得到以下输出:

x1.c: In function ‘main’:
x1.c:5:15: warning: integer constant is too large for its type [enabled by default]
     i = (i << 1000000000000000000000000000000000) % 10;
               ^
x1.c:5:5: warning: left shift count >= width of type [enabled by default]
     i = (i << 1000000000000000000000000000000000) % 10;
     ^

这会对整型常量过大和移位过大发出警告.移位特别会触发未定义的行为,如第C standard中关于按位移位运算符的第6.5.7p3节所述:

对每个操作数执行整数提升.如果右操作数的值为负值或大于或 等于升级的左操作数的宽度,则行为为 未定义

所以i首先是promotedint类型.根据系统的不同,int的宽度可能是16、32或64,并且给定值比这些值都要大得多.因此,您有未定义的行为,在这种特定情况下,它似乎会做您所期望的事情,但不能保证这一点,而且实际上,如果您对代码进行看似无关的更改或使用不同的设置进行编译,未定义的行为表现会如何变化.

C++相关问答推荐

当main函数调用被重构时,C函数给出错误的结果

如何在C宏中确定Windows主目录?

va_copy的使用是未定义的行为吗?

为什么在C中二维字符数组会有这样的行为?

如何在C中从函数返回指向数组的指针?

以前版本的tty_ldisc_ops.ioctl()是否也需要文件参数?

为什么即使在强制转换时,此代码也会溢出?

GCC创建应用于移动项的单独位掩码的目的是什么?

如何识别Linux中USB集线器(根)和连接到集线器(根设备)的设备(子设备)?

在另一个函数中使用realloc和指针指向指针

不同出处的指针可以相等吗?

指向不同类型的指针是否与公共初始序列规则匹配?

为什么我的二叉树删除删除整个左部分的树?

C编译和运行

存储和访问指向 struct 的指针数组

struct 中的qsort,但排序后的 struct 很乱

无法将字符串文字分配给 C 中的字符数组

const struct 成员的 typedef 中的灵活数组大小

使用替代日历打印日期

C 预处理器中的标记分隔符列表