11

I am trying to declare an array inside an if-statement. I wrote my code this way so that the object stays in scope once the if-block exits, but now I have a new issue: "taking address of temporary array". How can I re-write this in an alternative way so that maskArray is assigned the correct values?

int* maskArray;
if(conditional==true)
   maskArray = (int[9]) {0,1,0,1,-4,1,0,1,0};
2
  • (int[9]) {0,1,0,1,-4,1,0,1,0}; is a C construct, it is not legal in C++ . Please confirm which language you are trying to use Commented Oct 5, 2015 at 5:50
  • This is actually a C++ extension borrowed from "compound literal" in C. In C this is valid code because the temporary has automatic storage duration. In C++, it usually isn't, at least GCC's version of this extension isn't, because the temporary has a single expression lifetime. Commented Oct 5, 2015 at 5:53

6 Answers 6

10

While it is true that the original construct is not valid in C++, standard C++ does have a fairly similar feature: one can create a temporary array using an explicit type name and list-initializer

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

The above is valid C++ syntax for a temporary array object. But if you try using it in GCC, you will quickly discover that GCC refuses to apply array-to-pointer conversion to such temporary arrays, e.g.

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

The above is perfectly valid C++, but a bug in GCC prevents it from compiling. Clang and MSVC accept this code.

In your original code you are actually relying on a GCC extension, which allows you to use C-style compound literal syntax in C++ code, but this extension apparently happens to suffer from the very same bug as described above.

Sign up to request clarification or add additional context in comments.

Comments

8

Assuming you aren't going to later modify what maskArray points to, then the best/simplest solution is:

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

Static const works if you never plan to update the array, but if you're going to update it, you need a separate copy. This may be created either on the stack or on the heap. To create it on the stack:

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).

To dynamically create new copies of the array on the heap, you need to allocate the memory using new[]. The advantage of this is that it can be kept around for as long as it's useful before you decide to delete it.

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

Remember to delete is later using delete[] maskArray!

Comments

4

(int[9]) {0,1,0,1,-4,1,0,1,0} creates a temporary array which will be destroyed as soon as the full statement is completed. (Note, this is technically not C++, but a C99 feature which your compiler is supporting as an extension in C++.)

maskArray = (int[9]) {0,1,0,1,-4,1,0,1,0}; takes that temporary array, converts it to a pointer and stores that pointer in maskArray. As soon as this statement completes, the temporary array will be destroyed and the value in maskArray will no longer be valid.

The only way it's acceptable to use such a temporary array is to use it in that very same statement, such as by passing it to a function which will use it:

void foo(int (&arr)[9]);

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

This is okay because even though the temporary array is destroyed, it's only destroyed after the function returns and nothing is using the array. (And the function had better not somehow store long-lived references or pointers into the array, but then that's no different from normal.)

5 Comments

In C, a compound literal lasts for the duration of the enclosing scope. In OP's code it is the full statement - but only because there is only a single statement in that scope.
GNU C++ extension treats compound literal differently from C99. GNU C++ version has a shorter lifetime than C99 version, single expression VS automatic.
@user3528438 so you're saying that in GNU C++, int *p = (int []){1,2,3}; creates a dangling pointer? Fantastic extension...
@M.M That's what this warning is for. However, it's useful as described in this answer: to pass array literal as a pointer to a function. More interestingly, usage like foo(&std::vector<int>()) also emits this warning but foo((int[9]) {0,1,0,1,-4,1,0,1,0}) doesn't in GCC.
My mingw C++ compiler is happy with a temporary const array my_fun( (const unsigned char[3]){0,255,0} ); I believe it works because the initialization for this is in the BSS. Is this standard or a non portable quirk of mingw?
3

You can only initialize an array when it is declared. So if it need to be scoped in a loop declare an array inside the loop:

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)

As noticed by M.M, you can avoid the dangling maskArray by declaring it inside the block, (or by omitting it if you can directly use 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
}

3 Comments

Did you mean to have a { somewhere?
maskArray should be declared inside the conditional , to avoid possibility of dangling pointer. if isn't a loop.
@M.M Thanks for the missing {... For the pointer, I cannot know why OP wrote it out of the if block, that's the reason why I left it outside. But it could be declared inside the loop (int *maskArray = locArray;) or even not declared at all if using locArray is enough.
1

You can use a temporary array to achieve this

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

Or simply use a 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
}

1 Comment

memcpy is unnecessary because new[] allows you to pass the array in the initializer list.
-1
if (...)
{
  static int a[9] = { ... };
  maskArray = a;
}

1 Comment

Please provide some more details about your answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.