How to cast struct to uint8_t (error: conversion to non-scalar type requested)
How to cast struct to uint8_t (error: conversion to non-scalar type requested)
我需要在EEPROM中存储8个继电器的状态。我不想为移位而烦恼,我喜欢使用位域。所以我想这样定义它们:
typedef struct{
uint8_t RELAY0_STATE:1;
uint8_t RELAY1_STATE:1;
uint8_t RELAY2_STATE:1;
uint8_t RELAY3_STATE:1;
uint8_t RELAY4_STATE:1;
uint8_t RELAY5_STATE:1;
uint8_t RELAY6_STATE:1;
uint8_t RELAY7_STATE:1;
}relay_nvm_t;
relay_nvm_t relay_nvm;
在我的主要代码流程中,我使用 relay_nvm
变量设置每个继电器的状态。范例
...
if(something)
{
relay_nvm.RELAY0_STATE = 1;
relay_nvm.RELAY1_STATE = 0;
relay_nvm.RELAY2_STATE = 1;
relay_nvm.RELAY3_STATE = 0;
relay_nvm.RELAY4_STATE = 1;
relay_nvm.RELAY5_STATE = 1;
relay_nvm.RELAY6_STATE = 0;
relay_nvm.RELAY7_STATE = 1;
}
然后最后当我需要 read/write 到 EEPROM 时,我只是将 relay_nvm
转换为 uint8_t
到 read/write 一个字节到 EEPROM。但是我收到 error: conversion to non-scalar type requested
错误。以下是我的功能。
static void NVM_Relay_Read(void)
{
relay_nvm = (relay_nvm_t)NVM_ReadEepromByte(NVM_RELAY_INDEX);
}
static void NVM_Relay_Write(relay_nvm_t rs)
{
NVM_WriteEepromByte(NVM_RELAY_INDEX, (uint8_t)rs);
}
有什么方法可以克服这个错误吗?我以为我可以通过类型转换来做到这一点。位域的使用使我的工作变得非常轻松,并使代码易于理解。
我知道在这种情况下,由于填充,位域可能不安全,但我认为我可以使用 POP-PUSH
来克服它(值得吗?)
我看到了更多处理这个问题的方法:
使用并集。
使用指针类型转换:*((uint8_t*)&relay_nvm)
使用uint8_t:
uint8_t relay_nvm;
#define RELAY0_MASK 1
#define RELAY1_MASK 2
#define RELAY2_MASK 4
...
#define RELAY7_MASK 128
// set exact relays state:
relay_nvm = RELAY0_MASK | RELAY2_MASK | RELAY4_MASK | ... ;
// set single relay (others left unchanged):
relay_nvm |= RELAY2_MASK;
// clear single relay (others left unchanged):
relay_nvm &= ~RELAY2_MASK;
// check current state of a relay:
if (relay_nvm & RELAY2_MASK) { ... }
I didn't want to bother with shifting and I like using bitfields.
如果使用按位运算符是 "bother",那么在您掌握它们之前,您可能不应该编写嵌入式系统代码...这是编写非运算符的一个非常糟糕的理由-标准的、不可移植的代码。
与按位版本不同,位域有很多问题:未定义的位顺序、字节顺序依赖性、符号性指定不当、对齐和填充问题等等。您已经在编写带有 uint8_t
位字段的特定于平台的代码,因为 C 标准未涵盖这些代码。
如果你坚持使用位域,你必须阅读具体的编译器文档来了解它们是如何实现的。不要假设事物的分配方式有任何保证,因为在这种情况下没有标准化。
您的具体问题是您不能直接从结构类型(聚合 - 简单的英语 "container type")转换为 uint8_t
来回,原因与不能使用数组执行此操作的原因相同。您将不得不改用指向第一个元素的指针,然后将该元素转换为 uint8_t*
并取消引用。但这会带来一系列与对齐、兼容类型和 "strict aliasing".
有关的其他问题
一般而言,结构和联合不太适合内存映射目的,尤其是涉及到可移植性时。至少,您必须使用 #pragma pack(1)
或类似的特定于编译器的命令来启用打包。
因此,您真的应该考虑完全放弃位域并在原始 uint8_t
上使用按位运算符,因为这样可以解决很多问题。
作为旁注,存储在 EEPROM 中的所有变量都必须 volatile
合格,所有指向它们的指针也必须如此。否则,当您启用优化时,程序很可能会失控。
我需要在EEPROM中存储8个继电器的状态。我不想为移位而烦恼,我喜欢使用位域。所以我想这样定义它们:
typedef struct{
uint8_t RELAY0_STATE:1;
uint8_t RELAY1_STATE:1;
uint8_t RELAY2_STATE:1;
uint8_t RELAY3_STATE:1;
uint8_t RELAY4_STATE:1;
uint8_t RELAY5_STATE:1;
uint8_t RELAY6_STATE:1;
uint8_t RELAY7_STATE:1;
}relay_nvm_t;
relay_nvm_t relay_nvm;
在我的主要代码流程中,我使用 relay_nvm
变量设置每个继电器的状态。范例
...
if(something)
{
relay_nvm.RELAY0_STATE = 1;
relay_nvm.RELAY1_STATE = 0;
relay_nvm.RELAY2_STATE = 1;
relay_nvm.RELAY3_STATE = 0;
relay_nvm.RELAY4_STATE = 1;
relay_nvm.RELAY5_STATE = 1;
relay_nvm.RELAY6_STATE = 0;
relay_nvm.RELAY7_STATE = 1;
}
然后最后当我需要 read/write 到 EEPROM 时,我只是将 relay_nvm
转换为 uint8_t
到 read/write 一个字节到 EEPROM。但是我收到 error: conversion to non-scalar type requested
错误。以下是我的功能。
static void NVM_Relay_Read(void)
{
relay_nvm = (relay_nvm_t)NVM_ReadEepromByte(NVM_RELAY_INDEX);
}
static void NVM_Relay_Write(relay_nvm_t rs)
{
NVM_WriteEepromByte(NVM_RELAY_INDEX, (uint8_t)rs);
}
有什么方法可以克服这个错误吗?我以为我可以通过类型转换来做到这一点。位域的使用使我的工作变得非常轻松,并使代码易于理解。
我知道在这种情况下,由于填充,位域可能不安全,但我认为我可以使用 POP-PUSH
来克服它(值得吗?)
我看到了更多处理这个问题的方法:
使用并集。
使用指针类型转换:
*((uint8_t*)&relay_nvm)
使用uint8_t:
uint8_t relay_nvm;
#define RELAY0_MASK 1
#define RELAY1_MASK 2
#define RELAY2_MASK 4
...
#define RELAY7_MASK 128
// set exact relays state:
relay_nvm = RELAY0_MASK | RELAY2_MASK | RELAY4_MASK | ... ;
// set single relay (others left unchanged):
relay_nvm |= RELAY2_MASK;
// clear single relay (others left unchanged):
relay_nvm &= ~RELAY2_MASK;
// check current state of a relay:
if (relay_nvm & RELAY2_MASK) { ... }
I didn't want to bother with shifting and I like using bitfields.
如果使用按位运算符是 "bother",那么在您掌握它们之前,您可能不应该编写嵌入式系统代码...这是编写非运算符的一个非常糟糕的理由-标准的、不可移植的代码。
与按位版本不同,位域有很多问题:未定义的位顺序、字节顺序依赖性、符号性指定不当、对齐和填充问题等等。您已经在编写带有 uint8_t
位字段的特定于平台的代码,因为 C 标准未涵盖这些代码。
如果你坚持使用位域,你必须阅读具体的编译器文档来了解它们是如何实现的。不要假设事物的分配方式有任何保证,因为在这种情况下没有标准化。
您的具体问题是您不能直接从结构类型(聚合 - 简单的英语 "container type")转换为 uint8_t
来回,原因与不能使用数组执行此操作的原因相同。您将不得不改用指向第一个元素的指针,然后将该元素转换为 uint8_t*
并取消引用。但这会带来一系列与对齐、兼容类型和 "strict aliasing".
一般而言,结构和联合不太适合内存映射目的,尤其是涉及到可移植性时。至少,您必须使用 #pragma pack(1)
或类似的特定于编译器的命令来启用打包。
因此,您真的应该考虑完全放弃位域并在原始 uint8_t
上使用按位运算符,因为这样可以解决很多问题。
作为旁注,存储在 EEPROM 中的所有变量都必须 volatile
合格,所有指向它们的指针也必须如此。否则,当您启用优化时,程序很可能会失控。