如何使用按位运算符检测变量和掩码之间的大小不匹配

问题描述 投票:0回答:1

我想检查 C 源文件中与变量一起使用的按位运算符掩码是否在变量的最小范围内。如果我投射太大的蒙版,我确实会收到警告,但我宁愿不需要投射来触发警告。

换句话说,我想要按位运算中使用的掩码值大于

unsigned short
中可以包含的值,例如0x80000,当该掩码值与无符号短变量一起使用时,将生成警告或错误,例如,
usVal & MY_MASK
,其中
MY_MASK
定义为
#define MY_MASK  0x80000

我意识到

unsigned short
可能大于最小范围 0 到 65,535,但是我使用 Visual Studio 进行 x86、32 位编译,这似乎将
unsigned short
视为 16 位值。

我可能可以使用其他替代方案,只要它可以与 Visual Studio 2019 一起使用,我们将不胜感激。其他可与 Visual Studio 一起使用的 C 编译器可能会生成此类警告或 lint 类型的实用程序。 Visual Studio 2019 中也可能有一个源分析器设置,也会标记此类错误。或者 Visual Studio 2022 可能会发出这样的警告。

看起来 gccclang 有一个

-Wconversion
标志,嵌入的最佳和最差 GCC 编译器标志:-Wconversion,用于警告隐式转换,那么 Visual Studio 2019 是否有类似的东西?这样能解决这类问题吗?

在 Visual Studio 2019 中我可以拥有以下 C 源代码:

typedef unsigned long ULONG;
typedef unsigned short USHORT;

#define KPS_MASK16_ALT_MAN   (ULONG)0x80000
#define KPS_MASK16_ALT_AUTO  0x4000

#define MASK_CHECK(t,v,op,m)  ((v) op (t)(m))

int    KpsIsAlt1(USHORT usOutPrinterInfo) {
    return  (usOutPrinterInfo & (USHORT)KPS_MASK16_ALT_MAN) || (usOutPrinterInfo & (USHORT)KPS_MASK16_ALT_AUTO);
}


int    KpsIsAlt2(USHORT usOutPrinterInfo) {
    return  (usOutPrinterInfo & KPS_MASK16_ALT_MAN) || (usOutPrinterInfo & KPS_MASK16_ALT_AUTO);
}

int    KpsIsAlt3(USHORT usOutPrinterInfo) {
    return  (MASK_CHECK(USHORT,usOutPrinterInfo, &, KPS_MASK16_ALT_MAN) || (usOutPrinterInfo & KPS_MASK16_ALT_AUTO));
}

这将使用警告级别 3 或 4 生成以下编译器警告,其中第 136 行是函数

KpsIsAlt1()
中的掩码操作,第 145 行是函数
KpsIsAlt3()
中使用定义
MASK_CHECK
:

的掩码操作
1>Source2.c
1>D:\Users\rickc\Documents\vs2019repos\ConsoleApplicationClosure\Source2.c(136,38): warning C4310: cast truncates constant value
1>D:\Users\rickc\Documents\vs2019repos\ConsoleApplicationClosure\Source2.c(145,11): warning C4310: cast truncates constant value
1>ConsoleApplicationClosure.vcxproj -> D:\Users\rickc\Documents\vs2019repos\ConsoleApplicationClosure\Debug\ConsoleApplicationClosure.exe
1>Done building project "ConsoleApplicationClosure.vcxproj".

我希望在函数

KpsIsAlt2()
的第 141 行看到某种屏蔽警告。然而,没有警告掩码的值超出了变量的范围。

如果掩码与类型太小而无法与该掩码一起使用的变量一起使用,Visual Studio 似乎不会发出警告。就好像编译器默默地将无符号短变量提升为无符号长变量,并执行掩码。

我希望看到的是“按位运算超出范围”的警告,或其他某种提示,表明按位运算使用的掩码不会测试变量中的任何位。

我尝试使用

MASK_CHECK()
定义来测试可能的解决方案,但这样做确实没有任何意义,而不仅仅是使用函数
KpsIsAlt1()
中使用的强制转换。除非有某种方法使预处理器识别变量类型,否则它并不实际。

我还做了一个快速测试,将定义替换为:

//#define KPS_MASK16_ALT_MAN   (ULONG)0x80000
//#define KPS_MASK16_ALT_AUTO  0x4000

ULONG const KPS_MASK16_ALT_MAN = 0x80000;
USHORT const KPS_MASK16_ALT_AUTO = 0x4000;

根本没有看到任何警告。

如果我更改为

enum
,我会收到与使用定义相同的警告。

//#define KPS_MASK16_ALT_MAN   (ULONG)0x80000
//#define KPS_MASK16_ALT_AUTO  0x4000

//ULONG const KPS_MASK16_ALT_MAN = 0x80000;
//USHORT const KPS_MASK16_ALT_AUTO = 0x4000;

enum { KPS_MASK16_ALT_MAN = 0x80000, KPS_MASK16_ALT_AUTO = 0x4000 };
c visual-c++ language-lawyer bitwise-operators
1个回答
0
投票

就好像编译器默默地将无符号短变量提升为无符号长变量,并执行掩码。

几乎。在应用

unsigned short
运算符之前,它将
int
值提升为
&

您所看到的称为“整数促销”。一般来说,在表达式中使用小于 int 的整型变量或表达式时,它首先会被提升为

int
unsigned int
这在 

C 标准第 6.3.1.1p2 节中有详细说明

只要可以使用
in

t 或

unsigned int
,就可以在表达式中使用以下内容:

具有整数类型(
    int
  • unsigned int
    除外)的对象或表达式,其整数转换等级小于或等于
    int
    unsigned int
    的排名。
    类型为 
  • _Bool
  • int
    signed int
    unsigned int
    的位字段。
    
    
  • 如果
int

可以表示原始类型的所有值(受限制 通过宽度(对于位字段),该值被转换为

int
; 否则,它会转换为
unsigned int
。这些被称为
整数促销
。所有其他类型均不受整数影响 促销活动。

© www.soinside.com 2019 - 2024. All rights reserved.