【问题标题】:incrementing union data in c在c中增加联合数据
【发布时间】:2014-01-25 23:10:08
【问题描述】:

我对工会有点不熟悉,对它们进行了一些阅读,但无法弄清楚这一点。

有人为我定义了一个联合:

union CANDATA   // Unionize for easier cooperation amongst types
{
    unsigned long long ull;
    signed long long   sll;
    u32        ui[2];
    u16        us[4];
    u8     uc[8];
    u8     u8[8];
    s32        si[2];
    s16        ss[4];
    s8         sc[8];
};

还有一个结构体将此联合作为其成员之一:

struct CANRCVBUF        // Combine CAN msg ID and data fields
{ //                               offset  name:     verbose desciption
    u32 id;         // 0x00 CAN_TIxR: mailbox receive register ID p 662
    u32 dlc;        // 0x04 CAN_TDTxR: time & length p 660
    union CANDATA cd;   // 0x08,0x0C CAN_TDLxR,CAN_TDLxR: Data payload (low, high)
};

我正在创建一个 CANRCVBUF 实例:

static struct CANRCVBUF increasingMessage = {
    0x44400000,     /* 11 bit id */
    0x00000002,     /* 2 data bytes */
    {
        0x0000
    }
};

接下来我要做的是创建一个循环来增加increasingMessage 的数据部分。这是我遇到麻烦的地方。我的尝试是:

if(increasingMessage.cd + 1 > 65535) {
    increasingMessage.cd = 0x0000;
} else {
    increasingMessage++;
}

我意识到通过使用increasingMessage.cd 我正在访问联合,而不是联合中的数据。我的问题是,创建increasingMessage时如何知道使用了哪个工会成员?

非常感谢任何提示

【问题讨论】:

  • 例如通过:“increasingMessage.cd.ui[0]++”或“increasingMessage.cd.ull++”或任何你想在联合内增加的东西。

标签: c struct unions


【解决方案1】:

您需要提供您所针对的工会成员。编译器需要知道您是要测试 ull(作为 unsigned long long)还是 sc[0](作为 unsigned char),还是任何其他成员。

在你的情况下你可能想要使用

if(increasingMessage.cd.us[3] + 1 > 65535) {
  ...

--也就是说,如果联合的这部分你要找的数字。 (从您的代码中并不完全清楚。)

联合成员的访问方式与结构成员的访问方式相同,唯一的区别是它们的存储方式不同。


请注意,我选择的特定字段永远不会 >65535 ...因此请谨慎选择您的测试成员...

【讨论】:

  • 所以,你知道它会是 .us[3] 因为 0x0000 是一个整数?以及为什么索引 3(我猜是 0)。
  • 抱歉,只是猜测 -- 没有足够的信息继续下去。这取决于您应该测试联合的哪个部分(您肯定有一些文档吗?),不幸的是,还取决于您系统的字节序。
  • 不幸的是,没有提供太多文档。它确实注意到联合被用来使类型更容易合作。保存的数据是一个 64 位的数据串,将通过 CAN 总线发送。我只是想访问在我的increasingMessage 声明中创建的联合部分。
  • 是的,但您不知道increasingMessage 的哪个部分 仅包含您的数据长度。如果您确定整个成员应该只持有一个数字,您可以测试 increasingMessage.cd.ull -- 一个完整的 64 位值。
【解决方案2】:

union 中,所有字段都是重叠的。因此,union 的大小等于其中最大字段的大小。

关于你的问题:

您需要通过在increasingMessage.cd 之后添加对它的引用来指定要对其应用操作的字段的名称。例如,increasingMessage.cd.ull。但请注意,每次更改此字段时,union 中的所有其他字段也会受到影响,因为它们都相互重叠。

通常,为了指示相关字段,整个union 被放置在struct 中,其中包含一个额外的enum,用于指示该字段。例如:

typedef enum
{
    MSG_1,
    MSG_2,
    MSG_3,
    ...
}
msg_type_e;


typedef struct
{
    msg_type_e type;
    union CANDATA cd; // 0x08,0x0C CAN_TDLxR,CAN_TDLxR: Data payload (low, high)
}
msg_t;

【讨论】:

    【解决方案3】:

    通常当您使用联合时,您应该知道哪个成员中有正确的数据。如果不可能,您需要向CANRCVBUF 添加一个变量以指示哪个成员是正确的。这是使用继承的 C 方式。

    【讨论】:

    • @EricPostpischil 我的错,对不起。我将编辑我的答案以遵守
    【解决方案4】:

    标准说它是接收静态初始化器的联合的第一个命名成员,因此当你声明它时你隐含地说increasingMessage.cd.ull = 0x0000

    一般来说,在这种情况下,对工会中最大的成员进行工作是最稳健的。如果你不知道更好,那么:

    increasingMessage.cd.ull++;
    increasingMessage.cd.ull &= 0xFFFF;
    

    是进行 16 位计数的最安全方法 - 如果您不知道字节顺序,那么您不知道 cd.us[0]cd.us[3] 中的哪一个是 CANDATA 的最低有效 16 位,所以您不妨保证它在本地字节序中是一致的。当然,如果必须将其传达给另一个系统,那就是另一回事了,但文档应该清楚地定义那里的任何要求。

    【讨论】:

      【解决方案5】:

      所以,如果我的意图正确,您希望循环从 0 到 65535 增加 Message.cd。正如其他人所说,工会的成员相互重叠,即 ull、sll、ui[2]、... ,都指向同一个内存。也就是说,这些不同的字段在同一个内存地址,存储的是同一个值,但是你可以通过访问不同的字段,用不同的类型来表示同一个值。

      因此,一个简单的循环例如 ull 就可以解决问题,例如:

      for (increasingMessage.cd.ull = 0;increasingMessage.cd.ull<65536;increasingMessage.cd.ull++)
      {
      }
      

      现在,如果您只想循环遍历数据的租用有效位,则需要使循环稍微复杂一点:

      for (increasingMessage.cd.ull &= 0xFFFFFF00;(increasingMessage.cd.ull & (0x000000FF))<65536; increasingMessage.cd.ull++)
      {
      ...
      }
      

      最后,请注意,increasingMessage.cd 的不同字段如何解释取决于目标机器的字节序以及程序语义。例如,如果increasingMessage.cd.ull=0x11223344,这可能意味着cd.us[0]=0x11cd.us[3] = 0x44,或者cd.us[0]=0x44cd.us[3]=0x44,具体取决于字节序。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-21
        • 1970-01-01
        • 1970-01-01
        • 2021-09-30
        • 2019-04-11
        • 1970-01-01
        • 2021-10-01
        相关资源
        最近更新 更多