使用“指针”在C中定义数据块

时间:2014-11-25 13:13:33

标签: c++ c c-preprocessor

为了支持嵌入式设备中的遗留菜单结构(基于STM32处理器),正在寻找一种定义数据块的方法。定义是固定的,不能更改。重要的是,块没有间隙,数据在内存中的定义方式。

结构看起来像这样的字节

[Option] [VariableByte1] [VariableByte2] [VariableByte3] [VariableByte4] [MetaDataOffsetByte1] [MetaDataOffsetByte1] ...(定义数量).... [MetadataFormat] [MinValue] [MaxValue] ...(数字定义)....

在伪C中,第一部分可以实现为

#define OPTIONA 0x33
#define VAR(name) (&Name & 0xFF), ((&Name >> 8) & 0xFF)

#define METAFORMAT1 0x2
#define MAKEINT(value) (value & 0xFF), ((value >> 8) & 0xFF)
#define OFFSET /*Some preprocessor magic*/
char data[] =
{
 OPTION, VAR(someVariable), OFFSET(meta1)
 OPTION, VAR(someVariable), OFFSET(meta1),
 OPTION, VAR(someVariable), OFFSET(meta2),
meta1:
 METAFORMAT1, MAKEINT(0), MAKEINT(99),
meta2:
 METAFORMAT2, MAKEINT(-99), MAKEINT(0),
}

现在正在寻找“一些预处理器魔术”实现。

作为替代方案,我们已经研究过将meta和meta部分定义为结构或联合,但是在这样做时,没有太多保证将值按此顺序放入内存中。对于奖励积分,如果解决方案可以在某些编译器上移植,并且定义在编译时完成,那将是很好的。

一些环境事实:

  • 我们使用keil-arm-mdk编译器
  • 它使用标准C ++设置编译C代码
  • 我们只想在内存中定义字节模式(应用程序不与此结构交互

1 个答案:

答案 0 :(得分:1)

结论:您无法按照要求的形式获得所要求的内容。预处理器可以做各种有趣的技巧,但它从左到右,从上到下工作,所以没有办法让它向前看以确定它尚未处理的相对偏移。

您应该考虑编写一个函数来执行此初始化。这是迄今为止最好的选择,除非您需要在声明它们的位置初始化全局数据块(例如,在main()的开头)。

或者,如果由于某种原因你需要在全局范围内做很多事情,那么你应该考虑编写一个代码生成器来生成数据块初始化器,而不是直接用C语言。

如果你不想做上述任何一项,那么你可以考虑以下几点:

#define BYTE(i) ((i) & 0xff)
#define LE2(i)  BYTE(i), BYTE((i) >> 8)
#define LE4(i)  BYTE(i), BYTE((i) >> 8), BYTE((i) >> 16), BYTE((i) >> 24)

/* The number of bytes in a variable entry */
#define VAR_SIZE    7
/*
 * The bytes of a variable entry.  The entries for one data block must appear
 * one per line on consecutive lines, without any intervening lines, by the
 * metadata.
 * - option is the option code
 * - var is the value to be stored in the entry (as a variable or a literal)
 * - meta is the 1-based index of the metadata format entry for this variable
 * - start is the source line number of the first variable entry
 * - nvars is the total number of variable entries in the data block
 */
#define VARIABLE(option, var, meta, start, nvars) BYTE(option), LE4(var), \
    LE2((((start) + (nvars)) - __LINE__) * VAR_SIZE + ((meta) - 1) * MD_SIZE)

/* The number of bytes in a metadata format entry */
#define MD_SIZE     5
/*
 * The bytes of a metadata format entry.  The metadata format entries for one
 * data block must appear one per line on consecutive lines, starting on the
 * line after that of the last variable entry for the block.
 * - format is the format code
 * - min is the minimum value
 * - max is the maximum value
 */
#define METADATA(format, min, max) BYTE(code), LE2(min), LE2(max)

#define OPTIONA     0x32
#define OPTIONB     0x33
#define METAFORMAT1 0x2
#define METAFORMAT2 0x7

#define THIS_BLOCK_NVARS 3
/*
 * Hack alert!
 * The source layout of the following declarations is critical.  Do not merge
 * lines or introduce additional lines without understanding what you are doing!
 */
static int decl_start = __LINE__ + 2;
unsigned char data[] = {
    VARIABLE(OPTIONA, varName1, 1, decl_start, THIS_BLOCK_NVARS),
    VARIABLE(OPTIONA, varName2, 1, decl_start, THIS_BLOCK_NVARS),
    VARIABLE(OPTIONB, varName3, 2, decl_start, THIS_BLOCK_NVARS),
    METADATA(METAFORMAT1, 0, 99),
    METADATA(METAFORMAT2, -99, 0)
};

(根据您需要的偏移的精确定义,可能需要稍微调整一下。)除了THIS_BLOCK_NVARS之外的所有宏定义都可以重复使用任意数量的数据块声明。这给你一个相对简单的形式。但它有点脆弱,因为它依赖于预处理器的__LINE__宏来确定每个变量条目的索引,因此它对行的添加和删除很敏感(这部分是关键)。

同样非常重要:代码是有效的C ++,但由于初始化程序使用的数据不是编译时常量,因此有效C99( varname1等)。由于您实际上正在使用C ++编译器进行编译,因此您可以适应它。