在编译时查找数组元素位置
– 编辑 –
大家好。 我有一个元素数组,这些元素在程序的所有执行中都不会改变,并且item可以在自己的数组中包含子。 我必须在处理之前准备好arrays。 但是,因为我知道数组不会改变,我想将它声明为const
,并在编译时准备所有这些,所以我可以丢弃整数int son_id[NUM_OF_SONS]
, prepare_items()
函数和数组在我看来,宣言将更清楚。
#include #include #define NUM_OF_SONS 5 struct item{ int id; char *str; int son_id[NUM_OF_SONS]; const struct item *son[NUM_OF_SONS]; }; const struct item *find_item(int id); static struct item items[] = { {4, "FIRST ELEMENT"}, {5, "SECOND ELM"}, {10, "THIRD ELM"}, {15, "FATHER", {5,10}}, {0, 0 } }; const struct item *find_item(int id){ int i; for(i=0; items[i].str != NULL; ++i){ if(items[i].id == id) return &items[i]; } return NULL; } void fill_sons(struct item *item){ int i; for(i=0;ison_id[i]!=0) item->son[i] = find_item(item->son_id[i]); } } void prepare_items(){ int i; for(i=0;iid,item->str); print_sons(item); } void print_sons(const struct item *item){ int i; for(i=0;ison[i]) print_item(item->son[i]); } } int main(){ prepare_items(); print_item(&items[0]); print_item(&items[3]); }
我喜欢这样的事情:
static struct item items[] = { {4, "FIRST ELEMENT"}, {5, "SND ELM"}, {10, "THIRD ELM"}, {15, "FATHER", {&items[1],&items[2]}}, {0, 0 } };
但是,数组中可能有大约200个元素,我需要能够在其中间插入或删除元素(在编译时)。 所以&items[1],&items[2]
应该是ITEM_ID(5),ITEM_ID(10)
,某种预处理器指令。 怎么可能实现呢?
在此先感谢,并为长篇文章感到抱歉。
与C中的模板(我所知道的)最接近的等价物是X-Macros。 我认为你可以实现这个结果,但它需要为每个结构引入另一个标识符( 实际上它没有 – 向下滚动到“编辑”! ),我们可以通过在枚举中声明这些标识符来与数组同步。
首先,我们将初始化元素更改为宏调用的forms。 对于我喜欢的样式,这个宏的名称并不重要,所以我称之为_
。 所有调用都需要相同数量的元素,因此必要时添加一个空列表。 整个事情都包含在一个大宏中。 这个大宏将接收另一个宏作为参数,它调用每个元素。
#define DATA(_) \ _(4, "FIRST_ELEMENT", {}) \ _(6, "SECOND_ELEMENT", {}) \ _(10, "FATHER ELEMENT", {15, 20}) \ _(15, "SON ELEMENT 1", {}) \ _(20, "SON ELEMENT 2", {}) \ _(0, NULL, {})
现在我们可以通过定义一个用法宏来声明数组数据,该宏用于以数组声明的正确forms发出参数。
#define CREATE_ARRAY(a, b, c) \ {a, b, c}, struct item items[] = { DATA(CREATE_ARRAY) }
到目前为止,我们刚刚取得了相同的结果。 但现在它的forms更加灵活。 下一步是添加新ID。
#define DATA(_) \ _(FIRST, 4, "FIRST_ELEMENT", {}) \ _(SECOND, 6, "SECOND_ELEMENT", {}) \ _(FATHER, 10, "FATHER ELEMENT", {15, 20}) \ _(SON1, 15, "SON ELEMENT 1", {}) \ _(SON2, 20, "SON ELEMENT 2", {}) \ _(END, 0, NULL, {})
并调整CREATE_ARRAY
宏以考虑新参数。
#define CREATE_ARRAY(a, b, c, d) \ {b, c, d}, struct item items[] = { DATA(CREATE_ARRAY) };
现在有趣的部分。 我们创建另一个宏来生成ID作为枚举值。
#define CREATE_IDS(a, b, c, d) \ a, enum identifiers { DATA(CREATE_IDS) };
现在数据可以使用这些标识符来索引数组。
#define DATA(_) \ _(FIRST, 4, "FIRST_ELEMENT", {}) \ _(SECOND, 6, "SECOND_ELEMENT", {}) \ _(FATHER, 10, "FATHER ELEMENT", {SON1, SON2}) \ _(SON1, 15, "SON ELEMENT 1", {}) \ _(SON2, 20, "SON ELEMENT 2", {}) \ _(END, 0, NULL, {})
当然,从结构中删除child_id
成员,因为我们的新标识符是所需的数组索引,直接。
编辑。 等一会。 你已经有了标识符。 它们已经是独一无二的了。 所以我们不需要引入新的。 我们可以简单地破坏他们! 还需要__VA_ARGS__
来处理子列表中可能的嵌入式逗号。
#define CREATE_ARRAY(a, b, ...) \ {a, b, __VA_ARGS__ }, #define ID_(x) ID ## x #define CREATE_IDS(a, b, ...) \ ID_(a), #define DATA(_) \ _(4, "FIRST_ELEMENT", {}) \ _(6, "SECOND_ELEMENT", {}) \ _(10, "FATHER ELEMENT", {ID15, ID20}) \ _(15, "SON ELEMENT 1", {}) \ _(20, "SON ELEMENT 2", {}) \ _(0, NULL, {}) enum identifiers { DATA(CREATE_IDS) }; struct item items[] = { DATA(CREATE_ARRAY) };
cpp -P
输出(添加了换行符):
enum identifiers { ID4, ID6, ID10, ID15, ID20, ID0, }; struct item items[] = { {4, "FIRST_ELEMENT", {} }, {6, "SECOND_ELEMENT", {} }, {10, "FATHER ELEMENT", {ID15, ID20} }, {15, "SON ELEMENT 1", {} }, {20, "SON ELEMENT 2", {} }, {0, NULL, {} }, };
有关X-macros的更多信息,请参阅此问题的答案(其中一个我写道:P)。
关于我能提供的最好的 – 因为C绝对不是为了跟踪这种元数据而设计的 – 是__LINE__
常数。
__LINE__
将当前行号插入到程序中,您可以将其用作struct item
的字段。 显然,您还需要承诺在items
定义中没有任何空格,并且还要知道items
在文件中的起始位置。 后者,你可以这样做:
int first_line = __LINE__ + 2; const struct item items[] = { {4, "FIRST_ELEMENT", __LINE__},
然后记得从line_id
(或任何你想要的line_id
)字段中减去first_line
。
这不是一个好的解决方案,但我认为这是C将要做的最好的事情,而无需编写代码将一个数组的内容加载到另一个数组中,并在移动过程中跟踪元数据。
首先,你必须知道,一旦你将items
声明为一个数组到常量项目结构,你在编写const struct item items[];
时会这样做const struct item items[];
,然后,您将无法在初始化后更改这些结构的内容。
因此,例如,您将无法将任何内容分配给items
数组中任何一个结构内的child
数组的任何元素。 哇,这很大,让我们给出一个代码示例:
items[2].child[0] = &( items[3] ); // or just ... items + 3; // you won't be able to do this because items[2] is defined as a constant
我真的不明白你的目标是什么,但这里有一件事可能会帮助你。 您可以执行以下操作:
#include #define MaximumChildCount 5 // personal preference, easier to read, change as you wish typedef struct item{ int id; char *str; // I left the childs_id out, since you've implied that you'd like that out struct item *child[MaximumChildCount]; }; int main( ){ const struct item sons[] = { { 15, "SON ELEMENT 1" }, { 20, "SON ELEMENT 2" } }; // creating sons before the parent, biologically nonsense? // well, if you want to keep the record of child elements inside parent // and make them all constants, then you need to have children before const struct item items[] = { { 4, "FIRST_ELEMENT" }, { 6, "SECOND_ELEMENT" }, { 10, "FATHER ELEMENT", { sons, sons + 1 } }, // sons points to first son, sons + 1 points to the second one // assigned them at initialization, just like you had with { 15, 20 } { 0, NULL } }; printf( "%s", items[2].child[1]->str ); return 0; }
这将打印"SON ELEMENT 2"
。 你可以用以下方式制作儿子:
const struct item son1 = { 15, "SON ELEMENT 1" }; const struct item son2 = { 20, "SON ELEMENT 2" };
然后在初始化期间分配它们,如下所示:
... = { ... ... { ..., ..., { &son1, &son2 } }, ... };
如果这不是你想要的东西,我很抱歉。 我真的很难理解事业。