C 在编译时查找数组元素位置

C 在编译时查找数组元素位置,c,metaprogramming,C,Metaprogramming,--编辑-- 大家好。我有一个元素数组,它在程序的所有执行过程中都不会改变,在这个数组中,项可以在自己的数组中有子元素。我必须在处理阵列之前准备好它。但是,因为我知道数组不会改变,所以我想将它声明为const,并在编译时准备好所有内容,这样我就可以扔掉整数int-son\u-id[NUM\u-of-son],prepare\u-items()函数,我认为数组声明会更清晰 #include <stdlib.h> #include <stdio.h> #define NUM

--编辑--

大家好。我有一个元素数组,它在程序的所有执行过程中都不会改变,在这个数组中,项可以在自己的数组中有子元素。我必须在处理阵列之前准备好它。但是,因为我知道数组不会改变,所以我想将它声明为
const
,并在编译时准备好所有内容,这样我就可以扔掉整数
int-son\u-id[NUM\u-of-son]
prepare\u-items()
函数,我认为数组声明会更清晰

#include <stdlib.h>
#include <stdio.h>

#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;i<NUM_OF_SONS;++i){
            if(item->son_id[i]!=0)
                    item->son[i] = find_item(item->son_id[i]);
    }
}

void prepare_items(){
    int i;
    for(i=0;i<sizeof(items)/sizeof(items[0]);++i){
            fill_sons(&items[i]);
    }
}

void print_sons(const struct item *item);

void print_item(const struct item *item){
    printf("The item %d has the text %s.\n",item->id,item->str);
    print_sons(item);
}

void print_sons(const struct item *item){
    int i;
    for(i=0;i<NUM_OF_SONS;++i){
            if(NULL!=item->son[i])
                    print_item(item->son[i]);
    }
}

int main(){
    prepare_items();

    print_item(&items[0]);
    print_item(&items[3]);
}

但是,数组中可能有大约200个元素,并且我需要能够在其中间插入或删除元素(在编译时)。所以

&项[1],&items[2]
应该是
项ID(5),项ID(10)
,某种预处理器指令。如何才能做到这一点


提前感谢您,并为这篇长篇大论感到抱歉。

关于我所能提供的最好的信息——因为C绝对不是用来跟踪这种元数据的——是
\uuuuuuuu行
常量

\uuuuu LINE\uuuu
将当前行号插入程序,您可以将其用作
结构项中的字段。显然,您还需要承诺在
的定义中不包含任何空格,并且还需要知道
在文件中的起始位置。对于后者,您可以执行以下操作:

int first_line = __LINE__ + 2;
const struct item items[] = {
    {4, "FIRST_ELEMENT", __LINE__},
然后记得从
行id
(或者你想叫它什么)字段中减去
第一行


这不是一个好的解决方案,但我认为C最好不用编写代码将一个数组的内容加载到另一个数组中,并在移动过程中跟踪元数据。

首先,您必须知道,一旦您将
声明为常量项结构的数组,编写
const struct item items[]时执行的操作,则在初始化后将无法更改这些结构的内容

例如,您将无法在
数组中的任何一个结构中为
数组的任何元素分配任何内容。哇,太棒了,让我们举个代码示例:

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 <stdio.h>
#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;
}
然后在初始化过程中按如下方式分配它们:

static struct item items[] = {
    {4, "FIRST ELEMENT"},
    {5, "SND ELM"},
    {10, "THIRD ELM"},
    {15, "FATHER", {&items[1],&items[2]}},
    {0, 0 }
};
... = {
    ...
    ...
    { ..., ..., { &son1, &son2 } },

    ...
};

如果这不是你要找的东西,我很抱歉。我真的很难理解原因。

与C语言中的模板(据我所知)最接近的等价物是X宏。我认为您可以实现这一结果,但它需要为每个结构引入另一个标识符(实际上不是——向下滚动到“编辑”!),我们可以通过在枚举中声明这些标识符来与数组同步

首先,我们将初始值设定项元素更改为宏调用的形式。对于我喜欢的样式,此宏的名称并不重要,因此我将其命名为
\uuu
。所有调用都需要相同数量的元素,因此在必要时添加一个空列表。整个过程都被包装在一个大宏中。这个大宏将接收另一个宏作为参数,它为每个元素调用该参数

#define DATA(_) \
    _(4, "FIRST_ELEMENT", {}) \
    _(6, "SECOND_ELEMENT", {}) \
    _(10, "FATHER ELEMENT", {15, 20}) \
    _(15, "SON ELEMENT 1", {}) \
    _(20, "SON ELEMENT 2", {}) \
    _(0, NULL, {})
现在,我们可以通过定义一个usage宏来声明数组数据,该宏以数组声明的正确形式发出参数

#define CREATE_ARRAY(a, b, c) \
    {a, b, c},

struct item items[] = {
DATA(CREATE_ARRAY)
}
到目前为止,我们刚刚取得了同样的结果。但现在它的形式更灵活了。下一步是添加新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\u id
成员,因为我们的新标识符直接是所需的数组索引


编辑。稍等片刻您已经有了标识符。它们已经是独一无二的了。所以我们不需要引进新的。我们可以把它们弄碎<代码>\uuuu VA\u ARGS\uuuuu
也需要处理子列表中可能嵌入的逗号

#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
输出(添加换行符):



有关X宏的更多信息,请参阅答案(我写了其中一个:p)。

在编译时已知数组中的项,因为它们在源代码的初始值设定项列表中的顺序相同。例如,我可以判断数组元素
0
具有id号为4的
项。请澄清,也许用一个你想写但不能写的代码示例。我认为OP要求的是一种独立于初始值设定项元素顺序的方式。例如,为了允许他在索引1中插入一个编号为5的项,将项5向下移动到索引2,等等。我不知道编译时的方法,所以我建议最好是一个索引数组,在运行时开始时初始化,它对项数组进行索引。例如,如果索引器指向示例项数组的索引2,则为索引10。(不存在的索引器将包含NULL)请考虑进一步解释您的问题。现在,通过查看上面的部分,在——长版本——行中,我建议执行
intsearchedd=4;int i=0;虽然(items[i].id!=searchedID)如果(++i>items的大小){/*不存在*/break;}
i
最终将是保存id
searchedID=4
的元素的索引,但我感觉所要求的并不是这么简单。也许是语言问题……对不起,我会尽量解释清楚。@DoxyLover,我不能这样做,因为id可能是,例如,50000。我不能保存一个50000长度的数组,其中99%的数组是空元素。这到底是如何解决这个问题的?他将如何利用这一点,比如说,找出什么
#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)
};
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, {} },
};