是否可以在 C 结构体内部声明联合?

Ale*_*kov 5 c embedded struct unions

我想缩短我的代码,该代码的目的是控制步进电机。我想保存步数(32 位)和旋转频率(16 位)。我通过总线以uint8_t格式接收此信息。我的想法是不必将每个位乘以 256/65535.... 来弥补步长和频率。我可以使用联合来做到这一点,但我也想要有多个电机,所以我决定为此声明一个结构并将联合放在里面。它一直给我错误,所以我显然做错了一些事情。

我预计在结构内部声明联合不会成为问题,因为联合占用的内存空间等于其最大成员,它们可能是结构元素似乎是合理的。这是一个代码片段:

struct Stepper_Motor
  {
        union Num_Steps
        {
            uint32_t Sum_Steps;
            uint8_t Arr_Steps[4];
        };

        union Rotation_freq
        {
            uint16_t Sum_Freq;
            uint8_t Arr_Freq[2];
        };

        uint8_t State;
  };
Run Code Online (Sandbox Code Playgroud)

当我在声明结构体成员后尝试访问它时,当我写下其中一个成员时,IDE 会为我提供该结构体成员的列表:

```
struct Stepper_Motor Motor1,Motor2,Motor3;

//Some code... //

Motor1.Arr_Freq[0] = something;  // this gives me an error,  "no members named Arr_Freq"
```
Run Code Online (Sandbox Code Playgroud)

我还尝试了以下方法:

Motor1.Rotation_freq.Arr_Freq[0] = something;  //error
Run Code Online (Sandbox Code Playgroud)

甚至可以做我想做的事吗?我是否必须在结构外部声明联合,然后在结构内部引用它们,如果是这样,怎么做?这是使用联合来节省编写乘法的虚假方法吗?

dbu*_*ush 8

如果您从联合中删除标签名称,以便它们是匿名的:

struct Stepper_Motor
{
        union
        {
            uint32_t Sum_Steps;
            uint8_t Arr_Steps[4];
        };

        union
        {
            uint16_t Sum_Freq;
            uint8_t Arr_Freq[2];
        };

        uint8_t State;
};
Run Code Online (Sandbox Code Playgroud)

然后联合体成员将显示为结构体的成员。

  • 或者,给出工会成员的名称,带或不带标签。然后可以通过相应的结构成员访问每个联合的成员。 (4认同)

zwo*_*wol 2

您的原始声明有问题

struct Stepper_Motor
{
    union Num_Steps
    {
        uint32_t Sum_Steps;
        uint8_t Arr_Steps[4];
    };
    // ...
};
Run Code Online (Sandbox Code Playgroud)

是它声明了一个类型, union Num_Steps,但没有声明具有该类型的结构体字段。warning: declaration does not declare anything除了线路error: 'struct Stepper_Motor' has no member named 'Rotation_freq'上的硬错误(沿着 的行)之外,您还应该收到类似于两个联合的警告消息Motor1.Rotation_freq.Arr_Freq[0] = something;。(这就是为什么我在评论中大惊小怪想要查看错误消息的完整且未经编辑的文本。在 C 中,通常情况下,真正告诉您问题是什么的诊断并不是看起来最重要的诊断对于缺乏该语言经验的用户。)

除了 dbush 建议使用匿名联合之外,您还可以使用具有匿名类型的命名字段

struct Stepper_Motor
{
    union
    {
        uint32_t Sum_Steps;
        uint8_t Arr_Steps[4];
    } Num_Steps;

    union
    {
        uint16_t Sum_Freq;
        uint8_t Arr_Freq[2];
    } Rotation_freq;

    uint8_t State;
};
Run Code Online (Sandbox Code Playgroud)

或者您可以采用不使用标签命名空间的 C++ 约定:

typedef union Num_Steps_u {
    uint32_t Sum_Steps;
    uint8_t Arr_Steps[4];
} Num_Steps_u;

typedef union Rotation_freq_u {
    uint16_t Sum_Freq;
    uint8_t Arr_Freq[2];
} Rotation_freq_u;

typedef struct Stepper_Motor {
    Num_Steps_u Num_Steps;
    Rotation_freq_u Rotation_freq;
    uint8_t State;
} Stepper_Motor;
Run Code Online (Sandbox Code Playgroud)

任何一种都可以使您的原始代码的其余部分正常工作。请注意,如果您选择类似 C++ 的路线,则类型的名称不能与字段的名称相同,并且还有许多其他烦人的陷阱需要我花很长时间来解释 - 例如,我有一个很好的理由为什么我Thing在每个实例中都写了两次typedef union Thing { ... } Thing;,但是我需要一篇关于 ABI 和名称修改的整篇文章来解释它。因此,我建议您尽可能使用 dbush 的建议,或者如果您的编译器不支持 C2011 匿名联合成员,则使用具有匿名类型的命名字段。