简体   繁体   English

强制结构中枚举字段的大小

[英]Forcing the size of an enum'd field in a structure

[NOTE: this is related to Specifying size of enum type in C and What is the size of an enum in C? [注意:这与在C中指定枚举类型 的大小有关,并且在C中枚举的大小是多少? , but in those questions, the issue was how to minimize the size of the enclosing struct. ,但是在这些问题中,问题在于如何最小化封闭结构的大小。 Here, we want to specify the size of the individual members of the struct, but still get the documentation advantage of typedef'd enums] 在这里,我们要指定结构中各个成员的大小,但仍然可以获得typedef枚举的文档优势]

I'm implementing the client side of a serial communication protocol. 我正在实现串行通信协议的客户端。 I'd like to use C structs to capture the format of a serial packet, and I'd like to use C enums to define the legal values of the various slots in the packet. 我想使用C结构来捕获串行数据包的格式,并且我想使用C枚举来定义数据包中各个插槽的合法值。

I don't see a way to do both, but since I'm using GCC, it may be possible. 我看不到同时做到这两种方法的方法,但是由于我使用的是GCC,因此有可能实现。

As a hypothetical example of the problem, assume the packet I receive looks like this: 作为问题的假设示例,假设我收到的数据包如下所示:

typedef struct {
  uint16_t appliance_type;
  uint8_t voltage;
  uint16_t crc;
} __attribute__((__packed__)) appliance_t;

That's pretty clear, and I'm pretty sure I'll get a struct that is five bytes long. 这很清楚,我很确定会得到一个五字节长的结构。 But it doesn't capture the fact that appliance_type and voltage can only take on certain values. 但这并不能说明appliance_typevoltage只能取某些值的事实。

I'd prefer something like this... 我更喜欢这样的东西...

typedef enum {
  kRefrigerator = 600,
  kToaster = 700,
  kBlender = 800
} appliance_type_t;

typedef enum {
  k120vac = 0,
  k240vac = 1,
  k12vdc = 2
} voltage_t;

typedef struct {
  appliance_type_t appliance_type;
  voltage_t voltage;
  uint16_t crc;
} appliance_t;

...but there's no way that I know of to specify that appliance_type_t is 16 bits and voltage_t is 8 bits. ...但是我不知道要指定哪个device_type_t为16位,而voltage_t为8位。

Is there a way to have my cake and eat it too? 有办法吃我的蛋糕吗?

update: 更新:

I need to make it clear that I'm NOT expecting the compiler to enforce the enum'd values as setters for the respective fields! 我需要说明的是,我不希望编译器将枚举值强制为各个字段的设置器! Rather, I find that typedef'd enums are a useful construct for maintainers of the code, since it makes explicit what the intended values are. 相反,我发现typedef枚举对于代码的维护者来说是一个有用的构造,因为它可以明确表明预期的值是什么。

Note: As I researched this, I noticed that GCC enums accept __attribute__ specifications, but I'm not sure that helps. 注意:在研究此内容时,我注意到GCC枚举接受__attribute__规范,但不确定是否有帮助。

One way of doing this would be to use the bit field with the definition of the member in the struct. 一种方法是将bit字段与结构中成员的定义一起使用。

So your struct would be - 所以你的结构是-

typedef struct {
    appliance_type_t appliance_type:16;
    voltage_t voltage:8;
    uint16_t crc;
} appliance_t;

But this will leave a padding after the voltage field (depends on the implementation of the compiler though). 但这会在电压场之后留下填充(尽管取决于编译器的实现)。 The packed attribute should help you with that. 包装属性应该可以帮助您。

I would suggest that you keep the "raw data" separate from the abstraction. 我建议您将“原始数据”与抽象分开。 For example: 例如:

typedef struct {
  uint16_t type;
  uint8_t  voltage;
  uint16_t crc;
} __attribute__((__packed__)) raw_data_t;

typedef struct {
  appliance_type_t type;
  voltage_t voltage;
  uint16_t crc;
} appliance_t;

inline appliance_t raw_data_to_appliance (const raw_data_t* raw)
{
  appliance_t app =
  {
    .type    = (appliance_type_t)raw->type,
    .voltage = (voltage_t)raw->voltage,
    .crc     = raw->crc,
  };
  return app;
}

This shouldn't yield much overhead code. 这不会产生太多的开销代码。 See also How to create type safe enums? 另请参阅如何创建类型安全枚举?

So I understand that when you declare an enum, you have no control over its size and in 因此,我知道当您声明一个枚举时,您无法控制它的大小和大小。

typedef struct {
  appliance_type_t appliance_type;
  voltage_t voltage;
  uint16_t crc;
} appliance_t;

you want eg voltage_t voltage to be of a specific size, let's say uint8_t but you are not able to specify that. 例如,您希望voltage_t voltage具有特定大小,例如uint8_t但您无法指定该大小。

I don't know if you can provide size constraints on an enum, but there are two mechanisms you can use. 我不知道您是否可以对枚举提供大小限制,但是可以使用两种机制。 In both mechanisms you specify unit8_t voltage as the member of the struct: 在这两种机制中,都将unit8_t voltage指定unit8_t voltage结构的成员:

typedef struct {
  uint8_t appliance_type;
  uint8_t voltage;
  uint16_t crc;
} appliance_t;

Method 1 uses plain old #define s. 方法1使用普通的旧#define You just #define the values and use the symbolic constants in assignments: 您只需#define值并在分配中使用符号常量:

#define k120vac 0
appliance.voltage= k120vac;

In method 2 you have the enums, but still declared a member of the size you want. 在方法2中,您具有枚举,但仍声明为所需大小的成员。 Now you can assign the enum values to the member, eg: 现在,您可以将枚举值分配给成员,例如:

appliance.voltage= k120vac;


Note: I ran i simple test (VC2008) and also with voltage_t voltage; 注意:我进行了简单测试(VC2008),并且使用了voltage_t voltage; declaration, you can do appliance.voltage= kToaster; 声明,您可以执行appliance.voltage= kToaster; so not even gives the compiler "enum type safety" (checking only values from the domain are assigned/used), so not even that has been lost with the 2 methods I showed. 因此,甚至没有赋予编译器“枚举类型安全性”(仅检查/分配了来自域的值),所以即使是我展示的2种方法,它也不会丢失。
Did some more tests: 再做一些测试:

 unsigned char x = k12vdc; // <== OK unsigned char y = kToaster; // <== warning: truncation of int to unsigned char 

So it seems the compiler uses the smallest type possible for the values of the enum when assigning . 因此,似乎在分配时 ,编译器将最小的类型用于枚举的值。

There seems no size associated with the enum itself, only with its individual values. 枚举本身似乎没有大小相关联,仅与它的各个值有关。 Redefining k12vdc= 2000 now also gives a warning on the assignment x= k12vdc but not on x=k120vac . 现在,重新定义k12vdc= 2000还会在分配x= k12vdc发出警告,但不会在x=k120vac上给出警告。

Now, given that the compiler (VC2008) doesn't enforce type safety of enums, and that you can't control the size of a variable or member of an enum type, there seems no use in declaring a variable or member of enum type, except maybe for documentation purposes. 现在,由于编译器(VC2008)并未强制枚举的类型安全,并且您无法控制变量或枚举类型的成员的大小,因此在声明变量或枚举类型的成员时似乎没有用,除非出于文档目的。 A variable/member of enum type will be the size of an int, as the following example shows: 变量/枚举类型的成员将是int的大小,如以下示例所示:

 appliance_type_t a; voltage_t v; printf("sizeof appliance_type_t= %d\\n", sizeof(a)); // prints '4' printf("sizeof voltage_t= %d\\n", sizeof(v)); // prints '4' 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM