如何正确地将负值转换为 C 中的无符号类型?

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

我想存储一个寄存器值并像下面那样做

bitwise NOT operator

typedef union TEST_REG {
  uint32_t                         u32Register;
  uint8_t                          Byte[4];
  uint16_t                         hword[2];
} tst_reg;

#define TEST_REGISTER              ((volatile tst_reg*) 0x7023100CUL) // Register

下面是存储值的示例代码

#define CHECK_FLAG                  (0x00000004)
TEST_REGISTER->u32Register &= (uint32_t)(~(CHECK_FLAG));

但是当我执行

TEST_REGISTER->u32Register &= (uint32_t)(~(CHECK_FLAG));
代码时,
QAC
会发出像

这样的警告
Constant: Negative value cast to an unsigned type

我知道如果我们做一个

NOT (~) operator
六角值将变为负数。

所以我想知道如何正确地将负值转换为无符号类型?有什么建议吗?

c type-conversion unsigned misra
4个回答
4
投票

但是当我执行

TEST_REGISTER->u32Register &= (uint32)(~(CHECK_FLAG));
代码时,QAC 会发出像这样的警告

Constant: Negative value cast to an unsigned type

首先,C 不反对将负整数值转换为无符号类型。行为是明确定义的。反对的是 MISRA。事实上,MISRA 从根本上反对将

~
应用于有符号整数类型的表达式,and 它反对将有符号整数类型的复合表达式转换为不同的“基本类型类别”,例如无符号整数。我不确定 QAC 的确切来源,但 MISRA 不关心被转换的表达式的值是否实际上是负数。

要做的事情是首先从一个无符号整数开始。您可以通过在常量后缀

u
U
来做到这一点:
0x00000004u
。或者,
stdint.h
包含用于编写显式宽度类型常量的宏,并且通过使用其中适当的一个,您可以解决签名问题and避免强制转换:

#define CHECK_FLAG                  (UINT32_C(0x00000004))
TEST_REGISTER->u32Register &= ~CHECK_FLAG;

0
投票

如何正确做负值...

使用掩码,最好使用 unsigned 常量(那时没有负值)。不需要

()
,但也无害。

通常一个 cast 表示弱代码。

// #define CHECK_FLAG                  (0x00000004)
#define CHECK_FLAG                  0x00000004U
// TEST_REGISTER->u32Register &= (uint32)(~(CHECK_FLAG));
TEST_REGISTER->u32Register &= ~CHECK_FLAG;

0
投票

在使用

~
运算符之前进行转换,或者将 CHECK_FLAG 定义为带有转换的 uint32 或通过向文字添加“u”以指定它是无符号文字。

~((uint32)CHECK_FLAG)

#define CHECK_FLAG ((uint32)0x00000004)
#define CHECK_FLAG (0x00000004u)

请参阅此页面:http://port70.net/~nsz/c/c11/n1570.html#6.4.4.1 C 中的不同后缀

没有后缀的文字总是被解释为

int
。那么让我们来看看当你写的时候会发生什么:

(uint32)(~(CHECK_FLAG))

CHECK_FLAG,它是文字

4
,因此在反转位时表现为带符号的 int,由于位反转而变成负值。

(uint32)(-5)

现在您将负值转换为无符号类型,编译器会警告您。

如果将 uint32 强制转换为 before 反转(或使用无符号文字开头),则反转不会导致负值,因为在反转位时变量已经无符号,并且无符号值不能变得消极。


0
投票

您的 MISRA 检查器应该已经在

#define CHECK_FLAG  (0x00000004)
处发出警告。这需要有一个
U
/
u
后缀才能符合 MISRA,这就是这里的实际问题。

其他一些注意事项:

  • 整数常量周围的括号没有任何意义(但如果将其与其他宏一起使用可能会出现问题。)

  • 在十六进制数前添加一堆

    0000....
    没有任何意义,只会让程序员感到困惑。当涉及到十六进制整数常量时,C 有点破烂/厚颜无耻。假设 32 位系统,那么
    0x00000004
    int
    类型,
    0x4
    int
    类型,但是
    0x80000000
    unsigned int
    类型,而
    0x100000000
    long
    long long
    类型。

    因此很容易写出细微的错误。作为一些 RL 轶事,我曾经接手一个 8 位 16 位

    int
    的失败项目,他们养成了像这样输入零的习惯,自欺欺人地认为他们得到了 32 位数字。事实上,该项目实际上包含 16 位
    int
    、16 位
    unsigned int
    和 32 位
    long
    的混合,这是整个程序被破坏的主要原因之一。

因此正确的代码应该是:

#define CHECK_FLAG 0x4u
...
TEST_REGISTER->u32Register &= ~CHECK_FLAG;

在这种情况下,括号垃圾邮件对 MISRA 合规性没有影响 - 它只是膨胀所以我删除了它。也不再需要强制转换,因为表达式的所有操作数都是无符号的,MISRA 称之为“本质上无符号”,因此不需要强制转换。

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