【问题标题】:Is it possible to forward declare a static array是否可以转发声明一个静态数组
【发布时间】:2009-06-01 19:51:05
【问题描述】:

我需要将一个静态数组放入一个 .cpp 文件中。这个数组只在这个 .cpp 中使用,所以我想将它声明为静态的。数组定义很大,自然要转发声明。

static int bigIntArray[5000];

/* other code using bitIntArray */

static int bigIntArray[5000] = {
  0x00, 0x9900, 0xffee,
  ...
};

VC 9.0 报错:error C2086: 'int bigIntArray[5000]' : redefinition

如果我将“静态”更改为“外部”,问题就会消失,但我不喜欢这种解决方案。

为什么我不能转发声明一个静态变量?这是 C++ 标准要求的吗?

【问题讨论】:

  • 请务必查看 D.A. 的解决方案。

标签: c++


【解决方案1】:

冒着回答稍微不同的问题(Charles Bailey 很好地回答了您的问题)的风险,您可能希望使用带有 extern 的匿名命名空间。这可以防止其他翻译单元访问数组变量名。

namespace {
    extern int bigIntArray[5000];
}

// Code that uses bigIntArray

namespace {
    int bigIntArray[5000] = { ... };
}

这可能满足您的需要。

【讨论】:

  • "这会阻止其他翻译单元访问数组。"您不能在其他 TU 中引用数组按名称 bigIntArray。显然,如果你以某种方式获得指向它的指针,你就可以访问它。
  • @DyP 但是,即使static 对象也是如此——一旦指针松动,任何 TU 都可以访问该对象...
  • @twalberg 是的,因为链接是 name 的属性,而不是 object(object = 存储区域)的属性。名称是否因为匿名命名空间或static 说明符而具有内部链接并不重要。
  • 无论如何,匿名命名空间应该优先于static
【解决方案2】:

如果您使用extern 关键字并且不指定初始化程序,则只能在 C++ 中前向声明对象。任何其他声明对象的尝试也将是一个定义。这意味着前向声明的对象将具有外部链接。无法转发声明 static 对象,即具有内部链接的对象。

这与 C 中的任何不带初始化程序的声明都是临时定义不同,可以提供后续定义,但它们都必须指定相同的链接。

【讨论】:

  • 这似乎是正确的答案。添加一些规范参考会很好。
  • @D.A. ISO/IEC 9899 的相关部分是 6.2.2 链接标识符(第 2 和 7 段)、6.7 声明(第 3、4 和 5 段)和 6.9.2 外部对象定义(第 1 和 2 段)。
【解决方案3】:

将定义(也是声明)放在前面并取消“前向声明”有什么问题?

static int bigIntArray[5000] = {  0x00, 0x9900, 0xffee,  ...};

/* other code using bitIntArray */

有人说原因是“可读性”。原始发帖人并没有将其作为动机。

无论如何,我不认为做“奇怪”的事情可以证明“可读性”。我认为创建一个新的文件类型(例如,下面的“*.def”)很奇怪。

定义事物的位置似乎并不重要(至少对我而言)。

最干净,最清晰。最简单的做法是将定义移到顶部(不要太在意“可读性”)。

其他人说使用“外部”。这样做的问题是它打开了对象名称的范围(可能)超出了一个模块。


原发帖人也可能没有意识到,在这种情况下,“静态”是一个 scope 修饰符(而不是 storage 修饰符。

【讨论】:

  • 如果你有多个在初始化器中相互引用的静态变量,你也需要这个。
【解决方案4】:

是否可以转发声明静态数组

我认为如果你想用静态来做的话,可以用指针来转发声明。

指针声明将作为您的前向声明。如果您的/* other code using bitIntArray */仅在之后调用的函数定义,您可以分配内存并初始化它们,您可以通过传统方式 bigIntArray[index] 访问元素。

static int *bigIntArray;  // pointer to static integer

/*other code using bitIntArray: function definitions using forward declaration */
int func()
{
        printf("\nfunc %d \n",bigIntArray[3]);
}

int allocate()
{
        bigIntArray = new int[5]{1,2,3,4,5};
}

int main()
{
    allocate();
    func();
    return 0;
}

数组是静态整数,仅限于您的编译单元。

警告:应始终根据您的优先事项做出此类决定。 这样,您可能会增加代码的可读性或任何其他您想要前向声明的原因,但这将以堆为代价。

IMO,正如 D.A 所建议的,最好的选择是在定义的命名空间内使用 extern。 extern 通知编译器该变量是在别处定义的,并且定义的命名空间将其范围仅限于将使用该命名空间的单元。

namespace limited
{
       extern int bigIntArray[];
};
/* other code using bitIntArray */
int func()
{
        using namespace limited;   
        printf("\nfunc %d \n",bigIntArray[3]);
}

namespace limited
{
        int bigIntArray[5] ={1,2,3,4,5};
};

int main()
{
        func();
        return 0;
}

【讨论】:

    【解决方案5】:

    我想您这样做的原因是通过将长常量列表放在代码末尾来提高可读性,对吧? 另一种选择(恕我直言,既不好也不坏,只是不同)是使用包含定义的预处理器,例如:

    [main file]
    #include <iostream>
    #include "bigIntArray.def"
    
    int main()
    {
        for( int i = 0; i < 10000 ; ++i)
        {
            std::cout << bigIntArray[i] << std::endl;
        }
    
        std::cin.ignore();
        return 0;
    }
    
    [bigIntArray.def]
    static int bigIntArray[10000] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ... };
    

    更好的设计方式是在标头中声明数组extern,并将定义放在独立的代码文件中...

    【讨论】:

      【解决方案6】:

      我遇到了同样的问题,我的解决方案是仅将其转换为一行数字,因为任何格式(空格、换行符等)无论如何都不会使数据可读:

      static unsigned char XXX_certificate[1376]={
      48,130,5,92,48,176,231,119,149,87,108,108,225,239,138,233,91,116,236,200,117,213,130, <cut>
      };
      

      因此我可以将它放在源文件的开头,请注意它可以工作,因为我不使用自动换行。

      【讨论】:

        【解决方案7】:

        您可以像这样仅使用 extern 转发声明您的数组:

        extern int bigIntArray[5000];
        

        您还可以删除数组大小

        extern int bigIntArray[];
        

        这将告诉编译器该数组是在其他地方定义的(稍后或其他翻译单元)。在您的情况下,它将稍后在与静态全局相同的翻译单元中定义。

        在 VC++ 2010 Express 中为我工作。

        【讨论】:

          【解决方案8】:

          这是一种未定义的行为。

          更多详情请查看此主题:

          https://groups.google.com/forum/#!topic/comp.lang.c.moderated/bmiF2xMz51U

          此链接的引文:

          因为 1989 年之前的链接器处理“隐藏”的能力各不相同 符号。如果链接器无法同时处理两个名为“foo”的符号 不是同一个对象的不同翻译单元,那么 编译器必须在翻译时解决问题 - 而不是链接 - 时间。而且很多 C 编译器都是一次性的,所以他们需要分配一个 当他们第一次看到“foo”时的地址和空间,或者至少是 第一次被引用。因此有限制。

          正如基本原理所说:“在 C90 之前,实现差异很大 关于具有内部链接的前向引用标识符。”

          顺便说一句,它需要在单个文件中工作的原因是 具有外部链接的符号是不完整的版本(“int foo[];") 可能在#included 头文件中。

          【讨论】:

            【解决方案9】:

            @D。 A. 答案是最好的。 - 赏金他
            在这方面我同意@Leafy。

            想要补充一点,如果所有初始化器都在那里,则在实例化中不需要数组大小。

            此外,预先设置数组大小的好处是可以使用sizeof(bigIntArray)

            namespace {
                extern int bigIntArray[5];
            }
            
            // Code that uses bigIntArray
            void fred() {
              size_t N = sizeof(bigIntArray)/sizeof(bigIntArray[0]);
            }
            
            namespace {
                int bigIntArray[/* 5 not needed */] = { 1, 2, 3, 4, 5 };
            }
            

            【讨论】:

              【解决方案10】:

              我能想到的最佳解决方案:前向声明一个指向数组的指针,然后定义静态静态数组并将其分配给 EOF 处的指针。

              static int *bigIntArray;
              
              /* other code using bitIntArray */
              
              static int _bigIntArray[5000] = {
                0x00, 0x9900, 0xffee,
                ...
              };
              
              static int *bigIntArray = _bigIntArray;
              

              【讨论】:

                【解决方案11】:

                This is different from C where any declaration without an initializer is a tentative definition, subsequent definitions can be supplied but they must all specify the same linkage.

                很高兴知道,这意味着我们不必在 C 前向声明中显式放置 extern

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2013-11-19
                  • 2020-03-10
                  • 2012-07-05
                  • 2012-06-10
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多