C学习:移位幻影之左移一个负数,会发生什么?

C学习:移位幻影之左移一个负数,会发生什么?

问题背景


C语言中,移位是个简单的问题,但又是个高风险的问题。

简单在于,大部分场景都可以理解为乘或除2的幂。风险在于,C语言中对有符号数的移位与无符号数的移位异常场景,未定义:

  • 不同位宽转换时的移位过程如何处理?
  • 有符号数的左右移动,是逻辑移位,还是带符号移位?
  • 无符号数的移动中,移动数为负或超过了符号位宽如何处理?

前两问题中,在《C学习:无符号有符号数的移位问题分析总结》和《C学习:隐藏在负数移位过程中的bug》有过一些探讨,这里就第最后一个问题做一些小结。

无符号数移位问题


之前在《C学习:一个百思不得其解的无符号数移位问题》,也初步聊过,但无法解释下面这个问题。

1<<-1,对无符号数如1左移-1位会发生什么?

照例,先问下AI大模型看它如何应对。Prompt提问是:C语言中,执行这段语句:int b = 1 << -1, 会发生什么?

Copilot回答如下:

在C语言中,执行语句 int b = 1 << -1; 会导致未定义行为。原因如下:

  1. 位移操作符<< 是左移操作符,用于将一个整数的二进制位左移指定的位数。
  2. 负数位移:标准C语言不允许负数位移。位移操作的位数必须是非负整数。

因此,1 << -1 是非法的,会导致未定义行为。这意味着程序的行为是不可预测的,可能会导致程序崩溃或产生错误的结果。

我理解,不可预测指的是该操作的结果有多义性,具体行为取决于编译器处理,比如可能结果:

  • 为0,编译器翻译为右移1位,1>>1
  • 直接报错返回,编译器直接返回运行错误码

我的环境,跑的结果是0,会告警,但不会报错。错的是将其作为除数,导致浮点除零。但编译器也没中断,而是将结果存为一个极小值,继续运行。

有符号数移位操作使低位置零问题


t = (t << 8) >> 8,此代码中,t为有符号数int类型,移位结果是否固定?

显然,前文说过,有符号数移位是未定义行为,可能是算术,也可能是逻辑移位。但多数编译器,是使用的算术移位,即带符号移位。

所以,一般场景而言,该行代码能实现有符号数的低8位比特置零的效果。

但是,假如我们不允许对有符号数进行移位,该如何实现有符号数低8位置零操作呢?

容易想到的是,C语言中可通过除法这样实现:t = t / 256 * 256

扩展思考的是,可否不用除法,将有符号数移位过程等价转换到无符号数的位操作上去,从而更高效的实现?

c 复制代码
   /*  Write C code in this online editor and run it. */

    printf("Hello, World! \n");


    int num = -10;
    
    unsigned int numVer = num;
    
    printf("0x%x\n", numVer);


    num = -33554432; // -2^25()
    
    printf("0x%x\n", num);
    
    int num_shift = (num << 8) >> 8;
    
    printf("0x%x\n", num_shift);

仍存疑中。

相关推荐
锐策3 分钟前
『 C 』 `##` 在 C 语言宏定义中的作用解析
c语言
字节全栈_kYu10 分钟前
微信小程序视频点播在线视频学习系统 毕业设计 课程设计(1)首页_微信小程序视频功能设计流程图
学习·微信小程序·课程设计
lsx20240612 分钟前
ECharts 样式设置
开发语言
想拿高薪的韭菜20 分钟前
人工智能第2章-知识点与学习笔记
人工智能·笔记·学习
Future_yzx1 小时前
Java Web的发展史与SpringMVC入门学习(SpringMVC框架入门案例)
java·前端·学习
Future_yzx1 小时前
Spring MVC学习——发送请求(@RequestMapping注解及请求参数绑定)
学习·spring·mvc
沈韶珺1 小时前
Elixir语言的安全开发
开发语言·后端·golang
go54631584652 小时前
python 从知网的期刊导航页面抓取与农业科技相关的数据
开发语言·python·科技
m0_699659562 小时前
QT知识点复习
开发语言·qt
南玖yy2 小时前
C语言:数组的介绍与使用
c语言·开发语言·算法