c - C 中类型转换和按位运算的结果取决于顺序
问题描述
我试图在int, char, short, long
不使用头文件的情况下打印最小值<limit.h>
。所以按位运算将是一个不错的选择。但是奇怪的事情发生了。
该声明
printf("The minimum of short: %d\n", ~(((unsigned short)~0) >> 1));
给我
The minimum of short: -32768
但声明
printf("The minimum of short: %d\n", ~((~(unsigned short)0) >> 1));
给我
The minimum of short: 0
这种现象也发生在char
。但它不会发生在long, int
. 为什么会这样?
值得一提的是,我使用 VS Code 作为我的编辑器。当我unsigned char
在语句中移动光标时
printf("The minimum of char: %d\n", (short)~((~(unsigned char)0) >> 1));
它给了我一个提示,(int) 0
而不是(unsigned char)0
我所期望的。为什么会这样?
解决方案
首先,您的任何代码都不是真正可靠的,并且不会按照您的期望进行。
printf
并且所有其他可变参数长度函数都有一个功能失调的“功能”,称为默认参数提升。这意味着传递的参数的实际类型会进行静默升级。小整数类型(例如char
和short
)被提升为int
有符号的。(并且 float 被提升为 double。) Tl;dr:printf
是一个疯狂的函数。
所以你可以在各种小整数类型之间随意转换,最后还是会有提升int
的。如果您为预期类型使用正确的格式说明符,这没有问题,但您不使用,您使用%d
which is for int
。
此外,与~
C 中的大多数运算符一样,运算符对其操作数执行隐式整数提升。请参阅隐式类型提升规则。
话虽这么说,这条线~((~(unsigned short)0) >> 1)
做了以下事情:
0
取type的文字int
并转换为unsigned short
.通过隐式整数提升将其隐式提升
unsigned short
回。int
计算
int
值的按位补码0
。这是0xFF...FF
十六进制,-1
十进制,假设 2 的补码。将其右移
int
1。在这里,您在移动负整数时调用实现定义的行为。C允许这导致逻辑移位=移位零,或算术移位=符号位移位。从编译器到编译器的结果不同且不可移植。您会得到
0x7F...FF
逻辑移位或0xFF...FF
算术移位的情况。在这种情况下,它似乎是后者,这意味着您-1
在班次后仍然有小数。您对
0xFF...FF
=进行按位补码-1
并得到0
。你把它投射到
short
. 还是0
。默认参数提升将其转换为
int
. 还是0
。%d
期望 aint
并因此相应地打印。unsigned short
印有%hu
和。使用正确的格式说明符应该取消默认参数提升的效果。short
%hd
建议:研究隐式类型提升,避免在有符号类型的操作数上使用位运算符。
为了简单地显示各种有符号类型的最低 2 的补码值,您必须对无符号类型进行一些技巧,因为对其有符号版本的按位运算是不可靠的。例子:
int shift = sizeof(short)*8 - 1; // 15 bits on sane systems
short s = (short) (1u << shift);
printf("%hd\n", s);
这会将 unsigned int 移位1u
15 位,然后以某种“实现定义的方式”将结果转换为 short,这意味着在二进制补码系统上,您最终会将 0x8000 转换为 -32768。
然后给出printf
正确的格式说明符,您将从那里得到预期的结果。
推荐阅读
- javascript - 从 JSON 中的文件名组装 JSON 文件的 Grunt 任务
- sql - SQL选择省略倍数
- actions-on-google - Google 助理 - 操作未获批准
- c++ - 如何为给定的 boost::spirit 语法向用户提供高级自动完成建议?
- avr - 如何使用 UART 在 avr(atmega16/32) 上连接多个设备?
- react-native - 反应导航如何为每个选项卡动态更改标题导航标题?
- php - 在 PHP 中获取参数和查询
- image-processing - 如何使用带有 FFMPEG 的图像创建图像?
- android - Android 将数据读入内存
- wordpress - k6模拟登录wordpress用户