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 分钟前
实时读取另一个串口发来的返回数据
android·开发语言·kotlin
GH小杨8 分钟前
Kotlin D1
android·开发语言·kotlin
Root062410 分钟前
【笔记】记一次easyExcel中注解ExcelProperty映射字段赋值无效问题
java·开发语言·笔记
GH小杨10 分钟前
Kotlin D2
开发语言·微信·kotlin
&岁月不待人&10 分钟前
Kotlin 协程(三)协程的常用关键字使用及其比较
android·开发语言·kotlin
带上一无所知的我11 分钟前
解锁Conda:Python环境与包管理的终极指南
开发语言·python·conda
一个努力学习的小男孩12 分钟前
java环境部署
java·开发语言
A boy CDEF girl17 分钟前
【JavaEE】阻塞队列
java·开发语言·java-ee
酷爱码44 分钟前
2025DNS二级域名分发PHP网站源码
开发语言·php
MSTcheng.1 小时前
【C语言】动态内存管理
c语言·开发语言·算法