C / C ++静态定义的数组循环数据转发定义

时间:2016-10-31 17:57:02

标签: c++ arrays linker static-allocation

我正在寻找用于静态定义需要循环链接的C / C ++数据结构的最佳方法。例如。一个树,其中孩子和父母都需要彼此指针。

extern struct Op op_subops[4]; // fwd ref to children of ops

struct Op
{
    const char *name;
    struct Op  *parent;
    struct Op  *children;
};


struct Op ops[128] = {
    {"op",0,&op_subops[0]}
};

struct Op op_subops[4] = {
    {"subop1",&ops[1],0},
    {"subop2",&ops[1],0}
};

以上编译(g ++ 5.2)。 extern关键字似乎允许我创建从opsops_subops的转发引用,而另一个方向自然opsops_subops之前。{ / p>

我不喜欢的是,我更喜欢两个数组都是static(不在目标文件中创建公开可见的符号)。

我可以为其中一个方向使用整数索引,但这看起来有点不对劲,我宁愿让链接器为我解析地址。

任何人都有一个神奇的关键字来完成这项工作吗?

谢谢!

编辑:我需要避免像C ++静态构造函数和尖端的C ++ 17扩展(遗憾的是)。我的方法需要平台独立。

2 个答案:

答案 0 :(得分:2)

在C或C ++中,底线是您在声明对象之前不能引用它,但是这两种语言都允许您编写声明名称和(可能不完整)类型的“前向声明”而不使用定义对象。我更熟悉C,我会从这个角度回答;一些语义可能在C ++中有所不同。

在C中,你提出的代码的主要问题是它与数组的要求相冲突

  

只要指定了数组类型,元素类型就应该完整。

(C2011,6.2.5 / 20)

这意味着虽然您可以在没有大小的情况下向前声明数组op_subops(在这种意义上保留其类型不完整),但在定义类型struct Op之前,您不能为它发出任何声明。这可以通过交换两个声明的位置来解决。

纠正了排序问题后,在C中声明两个数组都有static链接完全没问题;你必须确保如果任何对象被多次声明(即op_subops),所有声明都同意它的链接:

struct Op
{
    const char *name;
    struct Op  *parent;
    struct Op  *children;
};

static struct Op op_subops[4]; // fwd ref to children of ops

static struct Op ops[128] = {
    {"op",0,&op_subops[0]}
};

static struct Op op_subops[4] = {
    {"subop1",&ops[0],0},
    {"subop2",&ops[1],0}
};

如果您愿意,可以从第一个声明中省略op_subop的长度,事实上我建议您这样做,以避免在两个声明中保持长度一致。

虽然你要求“最好的[已知]方法”,但这可能是一个意见问题,因为背景有色,而且意见问题在这里是偏离主题的。另一方面,在这种情况下,你似乎需要为孩子们​​安排一个数组,所以在没有改变类型的情况下,我看不出有什么其他选择。

答案 1 :(得分:1)

当您正在寻找将数组声明为静态并且无法接受它们保持全局时,我认为您的意图是让两个数组仍然无法从外部世界访问。

想法1:只是(转发)声明静态数组:

我首先想到这将是前向宣言的解决方案。使用gcc编译好C文件: online demo 1 。但不幸的是,它既没有在MSVC上也没有在C ++上编译,因为它不是标准的:

    static struct Op op_subops[];  // compiler dependent - it's standard!

实际上它更简单:只需 声明 数组及其维度:

    static struct Op op_subops[4]; // as simple as that

Online demo 2

后续初始化不会定义/重新定义数组;它指定要初始化的已声明的数组。实际上,您可以从初始化中退出数组维度。这次它在gcc和MSVC上编译:

    static struct Op op_subops[] = {  // [] or [4]
         ...
    };

想法2:将两个数组放在一个未命名的命名空间(仅限C ++)

然后您不再需要将项目视为静态:未命名的命名空间可确保其他编译单元无法引用这些对象