我正在实现一个学习 C 的玩具项目,我有一个关于无符号类型转换规则的看似简单的问题。
我特别想知道 C 标准是否期望将无符号类型转换为较小的无符号类型,从而在不使用任何位掩码的情况下简单地丢失其最高有效位。
示例:0xABC(16 位)-> 0xBC(8 位)
示例代码(共享链接):
#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
。您应该使用 PRIx8
来 inttypes.h
打印它。
如果不需要的话,编译器会简单地删除它吗?
一般来说,是的。
难道只有我没有注意到在这些情况下你实际上是在处理有符号整数吗?
没有。
如果较大的值(即 uint64_t)作为 uint8_t 参数传递或存储在 uint8_t 变量中,有什么区别?
没有区别。
编译器对这两种情况的处理是否不同?
除了明显的情况外,没有。
有人可以指出这个问题上的可信来源吗(比如 C 标准)?
将值分配给特定类型的变量时,该值将“转换”为目标类型。虽然您可能会阅读https://port70.net/~nsz/c/c11/n1570.html#6.3.1.3p2:
否则,如果新类型是无符号的,则通过比新类型可以表示的最大值重复加或减1来转换该值,直到该值在新类型的范围内https://en.cppreference.com/w/c/language/conversion现在,我们有更多可摄取的 cppreference