【问题标题】:C++ error: "taking address of temporary array"C++ 错误:“获取临时数组的地址”
【发布时间】:2015-10-05 05:31:48
【问题描述】:

我试图在 if 语句中声明一个数组。我以这种方式编写代码,以便在 if 块退出后对象保持在范围内,但现在我有一个新问题:“获取临时数组的地址”。如何以另一种方式重写它,以便为 maskArray 分配正确的值?

int* maskArray;
if(conditional==true)
   maskArray = (int[9]) {0,1,0,1,-4,1,0,1,0};

【问题讨论】:

  • (int[9]) {0,1,0,1,-4,1,0,1,0}; 是 C 构造,在 C++ 中是不合法的。请确认您尝试使用的语言
  • 这实际上是从 C 中的“复合文字”借用的 C++ 扩展。在 C 中这是有效代码,因为临时具有自动存储持续时间。在 C++ 中,它通常不是,至少 GCC 版本的这个扩展不是,因为临时有一个表达式生命周期。

标签: c++ arrays


【解决方案1】:

虽然原始结构确实在 C++ 中无效,但标准 C++ 确实有一个非常相似的特性:可以使用显式类型名称和列表初始化器创建一个临时数组

using I9 = int [9];
I9{ 0, 1, 0, 1, -4, 1, 0, 1, 0 };

以上是临时数组对象的有效 C++ 语法。但是如果你尝试在 GCC 中使用它,你会很快发现 GCC 拒绝将数组到指针的转换应用于此类临时数组,例如

using C10 = char [10];
C10 str;
std::strcpy(str, C10{ 'a', 'b', 'c' });
// GCC: error: taking address of temporary array

以上是完全有效的 C++,但是 GCC 中的一个错误阻止了它的编译。 Clang 和 MSVC 接受此代码。

在您的原始代码中,您实际上依赖于 GCC 扩展,它允许您在 C++ 代码中使用 C 风格的 复合文字 语法,但这个扩展显然恰好遇到了同样的错误如上所述。

【讨论】:

    【解决方案2】:

    假设您以后不打算修改maskArray 指向的内容,那么最好/最简单的解决方案是:

    const int* maskArray;
    if(conditional==true)
    {
         static const int myArray[9] = {0,1,0,1,-4,1,0,1,0};
         maskArray = &myArray[0];
    }
    

    如果您从不打算更新数组,则静态 const 可以工作,但如果您要更新它,则需要单独的副本。这可以在堆栈或堆上创建。在堆栈上创建它:

    int* maskArray;
    int myArray[9] = {0,1,0,1,-4,1,0,1,0};
    if(conditional==true)
    {
         maskArray = &myArray[0];
    }
    // when `myArray` goes out of scope, the pointer stored in maskArray will be useless! If a longer lifetime is needed, use the heap (see below).
    

    要在堆上动态创建数组的新副本,您需要使用new[] 分配内存。这样做的好处是它可以在您决定删除它之前一直保留。

    int* maskArray;
    if(conditional==true)
    {
         maskArray = new int[9] {0,1,0,1,-4,1,0,1,0};
    }
    

    记得删除是稍后使用delete[] maskArray

    【讨论】:

      【解决方案3】:

      (int[9]) {0,1,0,1,-4,1,0,1,0} 创建一个临时数组,该数组将在完整语句完成后立即销毁。 (请注意,这在技术上不是 C++,而是您的编译器支持的 C99 功能,作为 C++ 中的扩展。)

      maskArray = (int[9]) {0,1,0,1,-4,1,0,1,0}; 获取该临时数组,将其转换为指针并将该指针存储在maskArray 中。此语句完成后,临时数组将被销毁,maskArray 中的值将不再有效。

      使用这种临时数组的唯一方法是在相同的语句中使用它,例如将它传递给将使用它的函数:

      void foo(int (&arr)[9]);
      
      foo((int[9]) {0,1,0,1,-4,1,0,1,0});
      

      这没关系,因为即使临时数组被销毁,它也只是在函数返回后才被销毁,并且没有任何东西使用该数组。 (并且该函数最好不要以某种方式将长期存在的引用或指针存储到数组中,但这与正常情况没有什么不同。)

      【讨论】:

      • 在 C 中,复合文字在封闭范围内持续存在。在 OP 的代码中,它是完整的语句 - 但仅仅是因为该范围内只有一条语句。
      • GNU C++ 扩展对复合文字的处理与 C99 不同。 GNU C++ 版本的生命周期比 C99 版本短,单表达式 VS 自动。
      • @user3528438 所以你是说在 GNU C++ 中,int *p = (int []){1,2,3}; 创建了一个悬空指针?很棒的扩展...
      • @M.M 这就是这个警告的目的。但是,如此答案中所述,它很有用:将数组文字作为指向函数的指针传递。更有趣的是,像 foo(&std::vector<int>()) 这样的用法也会发出此警告,但 foo((int[9]) {0,1,0,1,-4,1,0,1,0}) 不会在 GCC 中。
      • 我的 mingw C++ 编译器对临时 const 数组 my_fun( (const unsigned char[3]){0,255,0} ); 很满意,我相信它可以工作,因为它的初始化在 BSS 中。这是标准还是 mingw 的非便携式怪癖?
      【解决方案4】:

      您只能在声明数组时对其进行初始化。因此,如果它需要在循环中限定范围,请在循环内声明一个数组:

      int* maskArray;
      if (conditional == true) {
          int locArray[] = {0,1,0,1,-4,1,0,1,0};
          maskArray = locArray;
          // ...
      } // locArray goes out of scope here
      // BEWARE : maskArray is now a dangling pointer (maskArray = NULL is safer)
      

      正如 M.M 所指出的,您可以通过在块内声明它来避免悬空的maskArray,(或者如果您可以直接使用locArray,则省略它):

      if (conditional==true) {
          int locArray[] = {0,1,0,1,-4,1,0,1,0};
          int *maskArray = locArray; // may be fully omitted if locArray is enough
      }
      

      【讨论】:

      • 你的意思是在某个地方有一个{ 吗?
      • maskArray 应在 conditional 中声明,以避免出现悬空指针的可能性。 if 不是循环。
      • @M.M 感谢缺少 {... 对于指针,我不知道为什么 OP 将其写出 if 块,这就是我将其留在外面的原因。但它可以在循环中声明(int *maskArray = locArray;),如果使用 locArray 就足够了,甚至根本不声明。
      【解决方案5】:

      你可以使用一个临时数组来实现这个

      int temp [] = {0,1,0,1,-4,1,0,1,0};
      size_t n = sizeof(temp)/sizeof(int);
      
      if (condition == true )
      {
         maskArray = new int[n]{0,1,0,1,-4,1,0,1,0};
      }
      
      // ...
      
      delete [] maskArray; // Free memory after use
      

      或者干脆使用std::vector

      std::vector<int> maskArray;
      
      if( condition == true )
      {
        maskArray = {0,1,0,1,-4,1,0,1,0}; // C++11 initializer_list vector assignment
      }
      

      【讨论】:

      • memcpy 是不必要的,因为new[] 允许您在初始化列表中传递数组。
      【解决方案6】:
      if (...)
      {
        static int a[9] = { ... };
        maskArray = a;
      }
      

      【讨论】:

      • 请提供有关您的答案的更多详细信息
      • @silwar 当然,OP 应该得到一本体面的 C++ 书籍并阅读对象生命周期。或者干脆使用网络搜索。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      • 2013-05-05
      • 1970-01-01
      • 2011-03-21
      • 1970-01-01
      相关资源
      最近更新 更多