C++高级教程
+ -

C语言数据位扩展

2025-10-25 2 0

在分析HID报告描述符代码部分时,看到一个很有意思的函数:

VOID
HidParser_SignRange(
    IN ULONG Minimum,
    IN ULONG Maximum,
    OUT PULONG NewMinimum,
    OUT PULONG NewMaximum)
{
    ULONG Mask = 0x80000000;
    ULONG Index;

    for (Index = 0; Index < 4; Index++)
    {
        if (Minimum & Mask)
        {
            Minimum |= Mask;
            if (Maximum & Mask)
                Maximum |= Mask;
           break;
        }

        Mask >>= 8;
        Mask |= 0xff000000;
    }

    *NewMinimum = Minimum;
    *NewMaximum = Maximum;
}

这个函数的功能是对有符号数值范围进行符号扩展处理

函数参数说明:

  • Minimum:输入的最小值
  • Maximum:输入的最大值
  • NewMinimum:输出的处理后的最小值
  • NewMaximum:输出的处理后的最大值

算法逻辑:

  1. 检测符号位:从最高位开始,每8位(一个字节)检测一次符号位

    • 初始掩码 Mask = 0x80000000(最高位为1,其余为0)
    • 循环4次,分别检测每个字节的最高位
  2. 符号扩展处理

    • 如果发现某个字节的最高位为1(表示负数),则对该数值进行符号扩展
    • Minimum |= Mask 将最小值从该字节开始向上进行符号扩展
    • 如果最大值在该位置也是1,同样对最大值进行符号扩展
  3. 提前返回

    • 一旦检测到需要符号扩展的情况,对数据进行位扩展,扩展之后跳出循环,设置新值,并返回
  4. 无符号情况

    • 如果4次循环都没有检测到符号位,说明是正数范围,直接复制到输出参数

应用场景:

这个函数主要用于处理HID(人机接口设备)协议中的数据范围,确保有符号数值在32位系统中能够正确地进行符号扩展,保持数值的符号语义不变。

以上是DEEP SEEK给出的解释,实际本人刚拿到这段代码时,是一个有BUG的函数,原函数中是return,而是break,经过本人的火眼金睛给找出来了。

以上函数的使用场景是因为在HID报告描述符进行数据解析时,统一使用的是ULONG来进行的,这样导致一个有符号的数据如CHAR,SHORT等,当出现负值时,不会进行自动进行位扩展的,所以需要这个函数来实现。

在HID报告描述符中,该函数的触发条件是:Minimum>Minimum

    ULONG LogicalMinimum = GlobalItemState->LogicalMinimum;
    ULONG LogicalMaximum = GlobalItemState->LogicialMaximum;
    if (LogicalMinimum > LogicalMaximum)
    {
        HidParser_SignRange(LogicalMinimum, LogicalMaximum, &LogicalMinimum, &LogicalMaximum);
    }

而在上面数据的赋值时,使用的是

typedef struct
{
    ITEM_PREFIX Prefix;

    union
    {
        UCHAR UData8[4];
        CHAR  SData8[4];
        USHORT UData16[2];
        SHORT SData16[2];
        ULONG UData32;
        LONG SData32;
    }Data;
}SHORT_ITEM, *PSHORT_ITEM;

代码如下:

    ULONG Data;
            if (CurrentItemSize == 1)
                Data = CurrentShortItem->Data.UData8[0];
            else if (CurrentItemSize == 2)
                Data = CurrentShortItem->Data.UData16[0];
            else if (CurrentItemSize == 4)
                Data = CurrentShortItem->Data.UData32;

可见,最主要的原因是无论是有符号还是无符号,都统一使用了UNSIGNED LONG来处理了,这时编译器不会自动进行位扩展。因为只有编译器检测到有符号到无符号的转换时,才会进行位扩展。

所以,HidParser_SignRange函数是为了弥补上述代码编写的BUG.

当然,我们也可以用下面的代码进行测试验证:

#include <iostream>
#include <Windows.h>
#include <tchar.h>

#define IN
#define OUT

VOID
HidParser_SignRange(
    IN ULONG Minimum,
    IN ULONG Maximum,
    OUT PULONG NewMinimum,
    OUT PULONG NewMaximum)
{
    ULONG Mask = 0x80000000;
    ULONG Index;

    for (Index = 0; Index < 4; Index++)
    {
        if (Minimum & Mask)
        {
            Minimum |= Mask;
            if (Maximum & Mask)
                Maximum |= Mask;
           break;
        }

        Mask >>= 8;
        Mask |= 0xff000000;
    }

    *NewMinimum = Minimum;
    *NewMaximum = Maximum;
}

int main()
{
    UCHAR min = -100;
    UCHAR max = 50;

    ULONG Minimum = min;//不会进行位扩展
    ULONG Maximum = max;

    CHAR Testmin = -100;
    ULONG TestMin = Testmin;//自动位扩展

    ULONG NewMinimum;
    ULONG NewMaximum;

    HidParser_SignRange(Minimum, Maximum, &NewMinimum, &NewMaximum);

    printf("min = 0x%02x\n", min);
    printf("max = 0x%02x\n", max);

    printf("Testmin = 0x%02x\n", Testmin);//%02x也限制不了%x扩展到32位
    printf("TestMin = 0x%x\n", TestMin);

    printf("NewMinimum = 0x%x\n", NewMinimum);
    printf("NewMaximum = 0x%x\n", NewMaximum);

    return 0;
}

0 篇笔记 写笔记

C语言数据位扩展
在分析HID报告描述符代码部分时,看到一个很有意思的函数:VOIDHidParser_SignRange( IN ULONG Minimum, IN ULONG Maximum, OUT PULONG NewMinimum, OUT PULONG NewMaxim......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!