【问题标题】:C/C++ statically defined circular data forward definition for array数组的C/C++静态定义循环数据转发定义
【发布时间】:2017-03-13 20:49:07
【问题描述】:

我正在寻找最好的方法来静态定义需要循环链接的 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 之前。

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

我可以为其中一个方向使用整数索引,但这似乎有点做作,我宁愿让链接器为我解析地址。

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

谢谢!

编辑:我需要避免使用 C++ 静态构造函数和前沿 C++17 扩展(遗憾的是)。而且我的方法需要独立于平台。

【问题讨论】:

  • 这是 C 还是 C++?您对struct X* member; 的使用表明您可能需要一个纯 C 的解决方案。
  • 没有语言“C/C++”。选一个。您正在使用类似 C 的代码,但使用 g++ 进行编译,这让我对您真正感兴趣的语言感到困惑。
  • 在您询问静态定义数据结构的地方,似乎您实际上可能在询问初始化。在那种情况下?
  • @JohnBollinger 不,他想要静态数据而不是全局数据,因此它们在其他编译单元中不可见。如果他把静态,他不能再“向前”定义为外部。
  • @Christophe,这些不是相互排斥的。如果默认初始化是可以接受的,那么在 C 中声明所有 static 是没有问题的。也许 C++ 中的情况有所不同,但 OP 想要避免一组模糊指定的 C++ 特性。

标签: c++ arrays linker static-allocation


【解决方案1】:

在 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 的长度,实际上我建议您这样做以避免在两个声明中保持长度一致。

尽管您要求“最佳 [已知] 方法”,但这将是一个意见问题,根据上下文进行着色,并且意见问题在这里是题外话。另一方面,在这种情况下,您似乎需要为孩子提供一个数组,所以我看不出在不改变类型的情况下还有什么其他选择。

【讨论】:

  • 嗯,这是我尝试的第一件事,但它也被 gcc 和 g++ 拒绝,error: redefinition of 'Op op_subops [4]'
  • @Tim,正如我所说,这个答案是针对 C 的,C++ 语义可能会有所不同。
  • 我尝试了 C 和 C++ 并得到了相同的诊断结果。无论如何,如果我不能完善这一点,世界就不会结束。我正在寻找一个简单快速的解决方案。我总是可以退回到整数索引或退后一步并在更高级别消除对这个问题的需求。
  • @Tim,它在 ideone 对我来说编译和运行良好(一旦我提供了一个虚拟的 main() 函数),它在家里也对我编译良好。我很确定如果你的编译器拒绝它,那么你的编译器不符合。也许是 MSVC++?
  • 我使用的是 gcc/g++ 5.2.0 (Cygwin)。在线工具 ideone.com 允许我测试 g++ 5.1 和 clang 3.7。我确实注意到“gcc 5.1”(C)确实通过了。
【解决方案2】:

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

想法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++)

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

【讨论】:

  • 省略数组大小在 C 中不起作用。(也就是说,当根据 C 的规则解释代码时,它不能解决他的声明的潜在问题。)这可能是也可能不是OP的问题。
  • 想法一个被 g++ 拒绝(见ideone.com/U5QuMZ):error: storage size of 'op_subops' isn't known。所以我认为这可能适用于 C,但不适用于 C++。
  • @Tim 你选择了 C++5.1 作为语言。在 ideone 上查看我与 C 的链接,它可以工作。虽然我不得不承认 MSVC 拒绝它。
猜你喜欢
  • 2011-03-30
  • 1970-01-01
  • 2011-08-17
  • 1970-01-01
  • 2011-06-06
  • 2012-05-30
  • 1970-01-01
  • 2011-06-03
  • 2013-12-08
相关资源
最近更新 更多