3

I have a corner case with regards to pointers and arrays.

How do I allocate a fixed-size (automatic) array on the heap? Let's get to the code right away to understand what I'm trying to ask:

typedef int ArrayOf10Ints[10];

int f()
{
        ArrayOf10Ints * p = new ArrayOf10Ints; // Error: cannot initialize a variable of type 'ArrayOf10Ints *'
                                               // (aka 'int (*)[10]') with an rvalue of type 'int *'

        ArrayOf10Ints * q = new ArrayOf10Ints[1] // OK: allocates space for 10 ints

        delete q; // Warning: use delete []
}

Why doesn't the expression to allocate p work? Why is the rvalue an int* and not a ArrayOf10Ints*? And why does q work?

Note: my goal is to understand the unexpected behavior allocating p and q. As others have pointed out there are many straightforward ways to solve this problem. For example, in my case, I'm using a pointer to denote that the array is optional—it may or may not exist—so I would do this instead:

boost::optional<std::array<int, 10> > optional_array;
alexc
  • 534
  • 2
  • 9
  • 2
    You don't! Use a [standard container](http://en.cppreference.com/w/cpp/container/vector) instead. – πάντα ῥεῖ Jul 26 '16 at 23:27
  • 3
    When trying to allocate `p`, you get an error saying _cannot convert ‘int*’ to ‘int (*)[10]’ in initialization_. As you defined the type `ArrayOfTenInts`, it is a pointer to memory area consisting of 10 integers (a 40-bit data type), so `int*`. Also, this might be helpful: http://stackoverflow.com/questions/4523497/typedef-fixed-length-array – Polb Jul 26 '16 at 23:35
  • Specifically use `std::array` – πάντα ῥεῖ Jul 26 '16 at 23:36
  • 1
    _"Note: for reasons beyond this discussion, I need to do exactly this."_ Elaborate about that restriction please. What are the parts you cannot change actually? The typedef? – πάντα ῥεῖ Jul 27 '16 at 00:17
  • Also note we don't do _"discussions"_ here. Stack Overflow _isn't a forum_. – πάντα ῥεῖ Jul 27 '16 at 00:24
  • You're right, I've edited my question to clarify my intent and make clear for other readers that there're better solutions to what I'm looking for. – alexc Jul 27 '16 at 01:58

2 Answers2

3

This is a behavior of new that is somewhat surprising. Even though ArrayOf10Ints is an alias for int[10], when you use it in a new expression, the result is as if you were writing new int[10] instead.

This is specified in [expr.new]/5

When the allocated object is an array (that is, the noptr-new-declarator syntax is used or the new-type-id or type-id denotes an array type), the new-expression yields a pointer to the initial element (if any) of the array.

So in your example, the new expression returns an int *, hence the error.

A workaround is to do what you've shown

ArrayOf10Ints* q = new ArrayOf10Ints[1];
delete[] q;

or place the array in a struct, or use std::array.

Note that even if you were to write

int* p = new ArrayOf10Ints;

you must then use delete[] p because operator new[] is called in this case too.

Live demo

Praetorian
  • 106,671
  • 19
  • 240
  • 328
0

Note: for reasons beyond this discussion, I need to do exactly this

Certainly not, because wrong things don't work.

Use a std::array<int,10> instead. This code should work just smoothly:

typedef array<int,10> ArrayOf10Ints;

int f() {
     ArrayOf10Ints * p = new ArrayOf10Ints;
     // ...

     delete p;
}

However I won't recommend you manage new and delete yourself, unless you're absolutely sure you need to.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • Believe me, if I could, I'd use boost::optional > – alexc Jul 27 '16 at 00:02
  • 1
    @alexc If you don't have access to the current c++11 standard, roll your own version of the `std::array` template, it's pretty straightforward and simple. If you are using something like Turbo C++, you're beyond of all hopes. – πάντα ῥεῖ Jul 27 '16 at 00:05
  • It's not just potential older compilers. I'm dealing with a system where I can't make such changes. My goal here is to understand why p doesn't work and q does. – alexc Jul 27 '16 at 00:05
  • 1
    @alexc What _changes_ actually? The `typedef` is predefined by legacy code, and you need to make use of it? – πάντα ῥεῖ Jul 27 '16 at 00:07