我已经定义了一个枚举类型的位域来匹配嵌入式系统中的一组跳线。我试图在MSVC中为代码编写测试工具,但比较应该相等的值失败了。
定义如下:
typedef enum { SERIAL_TEST_MODE, PARALLEL_TEST_MODE } TEST_MODE_e;
typedef union {
struct {
ACTUATOR_TYPE_e ActuatorType : 1; // 1
NORMAL_POSITION_e Damper1NormalPos : 1; // 2
NORMAL_POSITION_e Damper2NormalPos : 1; // 3
bool EnableDamper2 : 1; // 4
NETWORK_MODE_e NetworkMode : 1; // 5
FIRE_ZONE_TYPE_e FireZoneType : 1; // 6
bool PeriodicTestEn : 1; // 7
TEST_TIME_e TestTime : 3; // 8-10
TEST_MODE_e TestMode : 1; // 11
bool TestAHUEn : 1; // 12
bool TestDelayEn : 1; // 13
INPUT_1_MODE_e Input1Mode : 1; // 14
bool NightModeAHUEn : 1; // 15
ETR_ACT_MODE_e ETRActMode : 1; // 16
bool DSTEnable : 1; // 17
} Bits;
uint32_t Word;
} DIP_SWITCHES_t;
以下比较失败:
config.jumpers.Bits.TestMode = PARALLEL_TEST_MODE;
if (config.jumpers.Bits.TestMode == PARALLEL_TEST_MODE)
...
我在调试器中检查了TestMode
布尔值,它看起来很奇怪。 TestMode
的值为-1。
好像MSVC认为该值是2的补码,但是1位宽,因此0b1是十进制-1。枚举将PARALLEL_TEST_MODE
设置为1,因此两者不匹配。
使用LLVM或GCC在嵌入式方面进行比较比较好。
哪种行为是正确的?我认为在位字段等领域,GCC和LLVM比MSVC更好地支持C标准。更重要的是,我可以在不对嵌入式代码进行重大更改的情况下解决这种差异吗?
答案 0 :(得分:2)
详细剖析此问题,您会遇到以下问题:
ActuatorType : 1
是MSB或LSB。通常,根本无法保证内存中的位域布局。int
或_Bool
都不相同,则编译器完全不需要支持它。C标准在位字段中称为“存储单元”的大小未指定。通常,它是基于对齐的。 C标准确实保证,如果您有多个相同类型的位域彼此尾随,则必须将它们合并到同一存储单元中(如果有空间)。对于不同类型,没有这样的保证。
当您从NORMAL_POSITION_e
这样的一种类型转到另一种bool
时,编译器将它们放置在不同的存储单元中是很常见的。实际上,这意味着每次发生填充位插入的风险很高。实际上,许多主流编译器的行为都是这样。
此外,结构或联合可以在任何地方包含填充字节。
此外,还有Endianess问题。
结论:位字段不能用于需要任何形式的可移植性的程序中。它们不能用于内存映射。
此外,您真的不需要所有这些抽象层-这是一个简单的DIP开关,而不是航天飞机! :)
解决方案:
我强烈建议您放弃所有这些,以使用简单的uint32_t
。您可以使用纯整数常量屏蔽各个位:
#define DIP_ACTUATOR_TYPE (1u << 31)
#define DIP_DAMPER1_POS (1u << 30)
...
uint32_t dipswitch = ...;
bool actuator_active = dipswitch & DIP_ACTUATOR_TYPE; // read
dipswitch |= DIP_DAMPER1_POS; // write
这是可移植的,定义明确的,标准化的,符合MISRA-C的-您甚至可以将其移植到不同的字节序体系结构之间。它解决了上述所有问题。
答案 1 :(得分:1)
我想出的一个简单修复方法是:
仅对MSVC和GCC / LLVM有效#ifdef _WIN32
#define JOFF 0
#define JON -1
#else
#define JOFF 0
#define JON 1
#endif
typedef enum { SERIAL_TEST_MODE = JOFF, PARALLEL_TEST_MODE = JON } TEST_MODE_e;
答案 2 :(得分:0)
用于表示renderToStaticMarkup
的确切类型是实现定义的。因此,最有可能发生的事情是MSVC正在为此签名的特定enum
使用char
。因此,声明这种类型的1位位字段意味着您将获得0和-1的值。
与其将位域声明为enum
的类型,不如将它们声明为enum
或unsigned int
,以便正确地表示值。
答案 3 :(得分:0)
我将使用以下方法。
active
然后按如下所示设置值并测试该值。
gender
值为-1时,最低有效位将打开,值为0时,最低有效位将关闭。
这应该可以在多个编译器之间移植。