我正在实现一个学习C的玩具项目,我有一个关于无符号类型转换规则的看似简单的问题.

特别是,我想知道C标准是否希望转换为较小的无符号类型的无符号类型在不使用任何位屏蔽的情况下简单地丢失其最重要的位.

示例:0xABC

示例代码(Shared link):

#include <stdint.h>
#include <stdio.h>

void print_small_hex_value(uint8_t value) {
    printf("Small hex value from function: %llx\n", value);
}

int main()
{
    uint64_t large_value = 0xABCDEFABCDEFABCD;
    printf("Large hex value: %llx\n", large_value);
    uint8_t small_value = large_value; /* without bit mask */
    printf("Small hex value: %llx\n", small_value);
    uint8_t small_value_masked = large_value & 0xFF; /* with bit mask */
    printf("Small hex value masked: %llx\n", small_value);
    printf("\n");
    print_small_hex_value(large_value); /* print from function */
    print_small_hex_value(large_value & 0xFF);
    print_small_hex_value(small_value);
}

输出:

Large hex value: abcdefabcdefabcd
Small hex value: cd
Small hex value masked: cd

Small hex value from function: cd
Small hex value from function: cd
Small hex value from function: cd

在我看来,即使没有位面具,这种"神奇的"转换也有效.

那么,为什么有这么多代码库(即,CPython)强制位通过位屏蔽(又名value & 0xFF)? 如果没有必要,是否只是稍后由编译器省略?难道只是我没有注意到,在这些情况下,您实际上正在处理有符号的整除吗?

如果值越大(即,uint64_t)作为uint8_t参数传递还是存储在uint8_t变量中?编辑者对这两种情况的处理是否有所不同?

有人能就此事指出值得信赖的来源(例如C标准)吗?

推荐答案

C标准期望转换为较小的无符号类型的无符号类型会简单地丢失其最重要的位,而不使用任何位屏蔽.

是的

线路:

%llx\n", small_value

而类似的其他无效.见https://godbolt.org/z/b7xa794x1.%llx期望unsigned long long参数.small_value具有类型uint8_t.您应该使用PRIx8inttypes.h打印它.

如果没有必要,是否只是稍后由编译器省略?

一般来说,是的.

难道只是我没有注意到,在这些情况下,您实际上是在处理有符号的整点吗?

如果较大的值(即uint64_t)作为uint8_t参数传递或存储在uint8_t变量中,有什么区别?

没有区别.

编辑者对这两种情况的处理是否有所不同?

除了显而易见的,不.

有人能就此事指出值得信赖的来源(例如C标准)吗?

当值被分配给特定类型的变量时,该值对于目标类型来说是converted.虽然您可以阅读https://port70.net/~nsz/c/c11/n1570.html#6.3.1.3p2:

否则,如果新类型是无符号的,则通过重复添加或减go 比新类型中可以表示的最大值多一个来转换该值,直到该值处于新类型的范围内

0xABCDEFABCDEFABCD是12379814471884843981.我们从这个数字48358650280800171中重复减go 256.该操作之后,我们只剩下205,即十六进制的0xCD.这基本上是描述& 0xff的一种奇特方式.

如今,我们有了更多可摄入的cppreference https://en.cppreference.com/w/c/language/conversion.

为什么许多代码库(即CPython)强制位通过位屏蔽(又名值0xFF)?

出于可读性或可维护性的考虑,这可能是程序员的偏好.C中还有安全标准,例如MISRA 2012规则10.3要求您写uint8_t small_value = (uint8_t)large_value;,但我认为我不知道需要掩蔽的规则.

C++相关问答推荐

为什么海湾合作委员会在共享对象中的. init_data的虚拟内存地址之前留出一个空白

在Windows上构建无聊的SSL x64

从STdin读写超过4096个字节

C指针算法在函数参数中的应用

两个连续的语句是否按顺序排列?

如何调试LD_PRELOAD库中的构造函数?

如何在C中通过套接字自定义数据类型读取原始变量?

致命:ThreadSaniizer:在Linux内核6.6+上运行时意外的内存映射

将数据移动到寄存器时出现分段故障

是什么让numpy.sum比优化的(自动矢量化的)C循环更快?

平均程序编译,但结果不好

如何将另一个数组添加到集合中,特别是字符串?

在libwget中启用Cookie会导致分段故障

为什么电路板被循环删除?

为什么Linux无法映射这个PT_LOAD ELF段?

C编译和运行

macos/arm64 上地址空间不使用第一位吗?

如何正确探测平台设备?

在 C/C++ 中原子按位与字节的最佳方法?

C simd _m128 晶圆厂