如何在不违反 MISRA 规则的情况下将位域变量分配给 uint8_t 变量?
How to assign bitfield variable to uint8_t variable without violating MISRA rules?
我有一个 typedef struct
命名的角色。
typedef struct {
unsigned int a : 1;
unsigned int b : 1;
unsigned int c : 1;
unsigned int d : 1;
unsigned int o : 1;
unsigned int p : 1;
unsigned int q : 1;
unsigned int x : 1;
} Character;
static Character tempChar;
void writeVar(const uint8_t *pData)
{
tempChar.a = pData[0] >> 5;
...
}
当我尝试将一个 uin8_t 变量(值为 0
或 1
)分配给这些位域之一时,我违反了 MISRA 规则 10.6,该规则规定:
The value of a composite expression shall not be assigned to an
object with wider essential type
有没有办法在不违反 MISRA C 的情况下将位域分配给 uint8_t?
出于这个原因,我发现 MISRA C 过于复杂。
无论如何,您没有说要直接分配它。如果是这种情况,您可以采取以下措施:
typedef union {
struct {
unsigned int a : 1;
unsigned int b : 1;
unsigned int c : 1;
unsigned int d : 1;
unsigned int o : 1;
unsigned int p : 1;
unsigned int q : 1;
unsigned int x : 1;
};
uint8_t u8Value;
} Character;
并通过访问 tempChar.u8Value
而不是位字段来设置这些值。例如,
tempChar.u8Value |= (1 << 0);
会将 tempChar.a
设置为 1
。
那仍然会保持代码的整洁度(可读性)到同样的程度。例如,
if(1 == tempChar.a)
{
// Some code
}
如果需要,表达式 pData[0] >> 5
中的两个操作数将是 promoted 到 int
(pData[0]
会发生)。
表达式的结果是 int
.
提升和从 int
到 unsigned int
的转换,虽然在正常情况下完全有效且很好,但足以让非常严格的 MISRA 抱怨。
简单的解决方案(如评论中所示)是使用转换将 pData[0]
显式转换为 unsigned int
。
tempChar.a = pData[0] >> 5;
在这个5
中是一个带符号的整型常量。您应该使用 5U
作为无符号常量
此外,右移操作的结果将是一个 int
所以你需要将结果类型转换回 unsigned int
tempChar.a = (unsigned int) (pData[0] >> 5U);
这里的核心问题与 MISRA 无关,而是与试图将值存储在位域中的特定槽有关。你无法知道你的位域布局实际上是如何在内存中结束的,因为那在 C 标准中没有定义。
你的位域是在MS字节还是LS字节中分配8个值位?它是否符合字节顺序?位序是什么?没人知道。第 1 步是摆脱位域。
第 2 步是摆脱任何东西 unsigned int
并使用 uint16_t
/uint32_t
。
具体到 MISRA-C 10.6,禁止隐式转换为更广泛类型的规则总是被误导。 MISRA 用于此规则的基本原理是防止人们编写像 uint32_t u32 = u16a + u16b;
这样的代码,并认为 =
的 u32
操作数不知何故神奇地意味着该操作将在 32 位上执行16的。但是在8/16位系统上,它是用16位算法进行的,可能会有overflows/wrap-around。
碰巧,对有符号类型进行位移 总是 一个非常糟糕的主意。 pData[0]
隐式提升为已签名的 int
。还有其他 MISRA 规则处理此问题,而不是您引用的规则。
不管 MISRA 是什么,你都应该养成总是对无符号类型进行转换的习惯。 "It's not dangerous in this case" 是一个凄凉的理由。这意味着总是写 (uint32_t)pData[0] >> 5
并且应该在 之前应用强制转换 而不是在它之后。这消除了所有关于未定义行为左移和潜在算术右移等的不确定性。让优化器从那里担心操作数的实际使用大小。
我有一个 typedef struct
命名的角色。
typedef struct {
unsigned int a : 1;
unsigned int b : 1;
unsigned int c : 1;
unsigned int d : 1;
unsigned int o : 1;
unsigned int p : 1;
unsigned int q : 1;
unsigned int x : 1;
} Character;
static Character tempChar;
void writeVar(const uint8_t *pData)
{
tempChar.a = pData[0] >> 5;
...
}
当我尝试将一个 uin8_t 变量(值为 0
或 1
)分配给这些位域之一时,我违反了 MISRA 规则 10.6,该规则规定:
The value of a composite expression shall not be assigned to an object with wider essential type
有没有办法在不违反 MISRA C 的情况下将位域分配给 uint8_t?
出于这个原因,我发现 MISRA C 过于复杂。 无论如何,您没有说要直接分配它。如果是这种情况,您可以采取以下措施:
typedef union {
struct {
unsigned int a : 1;
unsigned int b : 1;
unsigned int c : 1;
unsigned int d : 1;
unsigned int o : 1;
unsigned int p : 1;
unsigned int q : 1;
unsigned int x : 1;
};
uint8_t u8Value;
} Character;
并通过访问 tempChar.u8Value
而不是位字段来设置这些值。例如,
tempChar.u8Value |= (1 << 0);
会将 tempChar.a
设置为 1
。
那仍然会保持代码的整洁度(可读性)到同样的程度。例如,
if(1 == tempChar.a)
{
// Some code
}
如果需要,表达式 pData[0] >> 5
中的两个操作数将是 promoted 到 int
(pData[0]
会发生)。
表达式的结果是 int
.
提升和从 int
到 unsigned int
的转换,虽然在正常情况下完全有效且很好,但足以让非常严格的 MISRA 抱怨。
简单的解决方案(如评论中所示)是使用转换将 pData[0]
显式转换为 unsigned int
。
tempChar.a = pData[0] >> 5;
在这个5
中是一个带符号的整型常量。您应该使用 5U
作为无符号常量
此外,右移操作的结果将是一个 int
所以你需要将结果类型转换回 unsigned int
tempChar.a = (unsigned int) (pData[0] >> 5U);
这里的核心问题与 MISRA 无关,而是与试图将值存储在位域中的特定槽有关。你无法知道你的位域布局实际上是如何在内存中结束的,因为那在 C 标准中没有定义。
你的位域是在MS字节还是LS字节中分配8个值位?它是否符合字节顺序?位序是什么?没人知道。第 1 步是摆脱位域。
第 2 步是摆脱任何东西 unsigned int
并使用 uint16_t
/uint32_t
。
具体到 MISRA-C 10.6,禁止隐式转换为更广泛类型的规则总是被误导。 MISRA 用于此规则的基本原理是防止人们编写像 uint32_t u32 = u16a + u16b;
这样的代码,并认为 =
的 u32
操作数不知何故神奇地意味着该操作将在 32 位上执行16的。但是在8/16位系统上,它是用16位算法进行的,可能会有overflows/wrap-around。
碰巧,对有符号类型进行位移 总是 一个非常糟糕的主意。 pData[0]
隐式提升为已签名的 int
。还有其他 MISRA 规则处理此问题,而不是您引用的规则。
不管 MISRA 是什么,你都应该养成总是对无符号类型进行转换的习惯。 "It's not dangerous in this case" 是一个凄凉的理由。这意味着总是写 (uint32_t)pData[0] >> 5
并且应该在 之前应用强制转换 而不是在它之后。这消除了所有关于未定义行为左移和潜在算术右移等的不确定性。让优化器从那里担心操作数的实际使用大小。