如何将结构体转换为uint8_t(错误:要求转换为非标量类型)

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

我需要在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;
}

最后,当我需要对EEPROM进行读写的时候,我只需投下 relay_nvmuint8_t 读写一个字节到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);
}

有什么办法可以克服这个错误吗?我想我可以通过类型化来实现。使用bitfields使我的工作非常容易,并使代码易于理解。

我知道在这种情况下,由于有填充物,bitfields可能不安全,但我想我可以通过使用 POP-PUSH (是否值得?)

c casting bit-fields
2个回答
3
投票

我看到了更多处理这个问题的方法。

  1. 使用联合。

  2. 使用指针类型转换。*((uint8_t*)&relay_nvm)

  3. 使用 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) { ... }

3
投票

我不想为移位而烦恼,我喜欢使用位域。

如果使用位元运算符是一个 "麻烦",那么在你掌握它们的窍门之前,你可能不应该写嵌入式系统代码......这是写非标准、非可移植代码的一个非常糟糕的理由。

与位智版本不同,位字段会有大量的问题:未定义的位序、endianess-dependence、不好指定的签名性、对齐& padding hiccup等等。你已经在用 uint8_t 比特字段,因为C标准没有涉及到这些。

如果你坚持使用位字段,你必须阅读具体的编译器文档,了解它们是如何实现的。不要假设关于东西如何分配有任何保证,因为在这种情况下没有标准化。


你的具体问题是,你不能直接从结构类型转换(一个 汇总 - 容器类型")到一个 uint8_t 并返回,原因与为什么不能用数组来做同样的事情。你必须使用指向第一个元素的指针来代替,然后将这个指针转为 uint8_t* 和去引用。但是,这也带来了很多其他的问题,比如对齐、兼容类型和 "严格的别名"。

一般来说,structs和联合体都不太适合用于内存映射的目的,尤其是在可移植性方面。最起码,你必须使用 #pragma pack(1) 或类似的编译器专用命令。

所以,你真的应该考虑完全放弃位域,在原始的 uint8_t,因为这样可以解决很多问题。


顺便说一下,所有存储在EEPROM中的变量必须是 volatile 是合格的,所有指向它们的指针也必须是合格的。否则很有可能在启用优化的时候,程序就会乱套。

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