如何确保枚举和数组在编译时具有相同的条目?

时间:2016-12-09 11:53:04

标签: c arrays enums embedded

这适用于在uC上以低级别运行的嵌入式应用程序。系统的另一部分需要设置参数,本地uC需要维护参数列表。每个参数由8位ID和8位值组成。由于另一端的内存限制,ID从0x70开始。

为了尽可能降低内存使用率,我已将参数存储实现为包含所有可写参数的ID和值的数组。然后在头文件中列出了这些参数的枚举列表,以允许应用程序的其他部分访问参数。

有没有办法确保枚举列表和参数数组在相同的顺序中具有相同的条目?我已经相当彻底地记录了代码(并没有全部包含在提取中)但是我想在编译时进行某种形式的检查以确保列表和数组匹配。

我不确定的另一件事是,这是否是实现这一目标的最有效方式。我需要能够遍历参数,以便将它们传输到系统的其他部分,我需要使用尽可能少的内存。

来自parameters.h

/*******************************************************************************/
/* IDs for all parameters must be defined                                      */
/* Defaults only need to be defined for rut-time writable parameters           */
/* All parameters must have an ID define                                       */
/* Writable parameters must also have:                                         */
/*    * DefaultValue define                                                    */
/*    * Entry in ParamIndex                                                    */
/*    * Initialesed default entry in Parameters (contained in C file)          */
/*    * If min and max values are not 0x00 and 0xFF then define those too      */
/*******************************************************************************/

// Parameter IDs - All parameters need this defining
#define Param_ActualTemp_ID                         0xE0
#define Param_OperationMode_ID                      0xE1
#define Param_MaintenanceModePID0_ID                0xE5
#define Param_MaintenanceModePID1_ID                0xE6
#define Param_FirmwareVersionL_ID                   0xEB
#define Param_FirmwareVersionH_ID                   0xEC
#define Param_SerialNumberL_ID                      0xED
#define Param_SerialNumberH_ID                      0xEE
#define Param_SerialNumberHH_ID                     0xEF
#define Param_MaxTemperature_ID                     0xFC
#define Param_NULL_ID                               0xFF

// Parameter Default Values - All writable parameters need this defining
#define Param_NULL_DefaultValue                     0xFF
#define Param_OperationMode_DefaultValue            0
#define Param_MaintenanceModePID0_DefaultValue      0xFF
#define Param_MaintenanceModePID1_DefaultValue      0xFF
#define Param_MaxTemperature_DefaultValue           0x54

typedef struct
{
  const uint8    id;
        uint8    value;
} PARAMETER;

// Parameter Index, any writable parameters need an entry here
// Used as array index for parameters[], do not edit existing values
typedef enum
{
  Param_NULL = 0,
  Param_OperationMode,
  Param_MaintenanceModePID0,
  Param_MaintenanceModePID1,
  Param_MaxTemperature,

    /* Additional values must be entered above this line */
  Param_NUMBER_OF_IMPLEMENTED_PARAMS
} ParamIndex;

extern PARAMETER parameters[];

来自parameters.c

PARAMETER parameters[] = {
  { .id = Param_NULL_ID,                 .value = Param_NULL_DefaultValue},
  { .id = Param_OperationMode_ID,        .value = Param_OperationMode_DefaultValue},
  { .id = Param_MaintenanceModePID0_ID,  .value = Param_MaintenanceModePID0_DefaultValue},
  { .id = Param_MaintenanceModePID1_ID,  .value = Param_MaintenanceModePID1_DefaultValue},
  { .id = Param_MaxTemperature_ID,       .value = Param_MaxTemperature_DefaultValue}
};

3 个答案:

答案 0 :(得分:4)

您使用Param_NUMBER_OF_IMPLEMENTED_PARAMS走在正确的轨道上。不幸的是,你不能将它用作数组大小,因为这只能保证数组没有比枚举更多的初始值。它不能防止初始化程序太少的阵列。

确保这一点的方法是对枚举大小与数组大小进行静态断言。将数组声明保持为PARAMETER parameters[],然后执行

_Static_assert(Param_NUMBER_OF_IMPLEMENTED_PARAMS == sizeof(parameters)/sizeof(*parameters), 
               "Array initializer list does not match enum");

标准关键字_Static_assert仅在C11中可用,标准宏static_assert仅在C11和C ++中可用。在较旧的编译器上,您必须自己创建它。例如:

#define STATIC_ASSERT(expr) {typedef uint8_t S_ASSERT[(expr) ? 1 : 0];}

这将给出一个神秘的"无法声明大小为零的数组"如果断言失败,则编译错误。

通过为数组项使用指定的初始值设定项可以确保排序:

PARAMETER parameters[] = {
  [Param_NULL_DefaultValue] = { .id = Param_NULL_ID, .value = Param_NULL_DefaultValue},
  ...

指定的初始化程序不会阻止重复,但是如果重复条目只会使用最后一个(并且通常会收到编译器警告)。此类重复条目不会影响数组大小。

答案 1 :(得分:1)

您可以使用Param_NUMBER_OF_IMPLEMENTED_PARAMS作为数组大小。如果你必须在数组初始化列表中有许多元素,这至少会导致编译器做出反应。

然而,没有(标准和便携式)方式警告您没有初始化所有元素。您未初始化的所有元素都将为"零"初始化。

肯定无法确保订单,而不是在编译时。

可能有静态分析工具可能会对您有所帮助。当然还有严格的代码审查。

答案 2 :(得分:1)

You could use generator macros, like these ones.
When you modify the PARAM_BLOCK, the enum and the array are build automatically.

#define PARAM_BLOCK(GEN_FUNC)  \
  GEN_FUNC(Param_NULL_ID, Param_NULL_DefaultValue)         \
  GEN_FUNC(Param_OperationMode_ID, Param_OperationMode_DefaultValue) \
  GEN_FUNC(Param_MaintenanceModePID0_ID, Param_MaintenanceModePID0_DefaultValue) \
  GEN_FUNC(Param_MaintenanceModePID1_ID, Param_MaintenanceModePID1_DefaultValue) \
  GEN_FUNC(Param_MaxTemperature_ID, Param_MaxTemperature_DefaultValue) \

#define GENERATE_PARAM_ARRAY(paramId,paramValue)   { .id = paramId, .value = paramValue },
#define GENERATE_PARAM_ENUM(paramId,paramValue)          paramId,

typedef enum
{
  GENERATE_PARAM_ENUM(PARAM_BLOCK)
    /* Additional values must be entered above this line */
  Param_NUMBER_OF_IMPLEMENTED_PARAMS
} ParamIndex;

PARAMETER parameters[] = {
    GENERATE_PARAM_ARRAY(PARAM_BLOCK)
};

But perhaps you could simplify your parameter array, as you have an ordered array by id, you don't need the id at all.

#define GENERATE_WRITE_PARAM_ARRAY(paramId,paramValue)   paramValue,
uint8 writeable_parameters[] = {
        GENERATE_WRITE_PARAM_ARRAY(PARAM_BLOCK)
    };