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);

仍存疑中。

相关推荐
炸膛坦客1 小时前
单片机/C/C++八股:(二十六)IIC 专题(I²C)---- 上集
c语言·c++·单片机
灯澜忆梦2 小时前
GO_并发编程---定时器
开发语言·后端·golang
-银雾鸢尾-2 小时前
C#中的StringBuilder相关方法
开发语言·c#
-银雾鸢尾-2 小时前
C#中结构体与类的区别;抽象类与接口的区别
开发语言·c#
世人万千丶3 小时前
参数管理_Flutter在鸿蒙平台路由参数最佳实践
学习·flutter·华为·harmonyos·鸿蒙
大模型码小白3 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
段一凡-华北理工大学5 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化
Ljwuhe5 小时前
C++——多态
开发语言·c++
遇乐的果园6 小时前
前端学习笔记-vue加载渲染优化
前端·笔记·学习
心平气和量大福大6 小时前
C#-WPF-Window主窗体
开发语言·c#·wpf