8

In this excellent answer by AndreyT, he explains that in C, when a function needs an array whose dimension is known at compile-time, it's a major technique-level error to declare

void process_array(int *ptr, size_t plen);

instead of

void process_array(int (*arr_ptr)[10]);

Furthermore, he opines that many programmers are oblivious to the second option and know only about the first. One of the reasons, he writes, for this behaviour is when an array needs to be dynamically allocated and passed to the second version, programmers don't know how to do it; they're so accustomed to int *p = malloc(sizeof(*p) * 10) which returns an int *. In C, the way to do it is, as he shows

int (*arr_ptr) [10] = malloc(sizeof(*arr_ptr));

This got me thinking on how one would do the same in C++. I know we've std::array, std::vector, etc. but I'm interested in understanding new's usage. So I tried doing this:

typedef int Array10[10];
Array10 *p = new Array10;    // error: cannot convert ‘int*’ to ‘int (*)[10]’

When I change p's type to int* the compiler (GCC 4.8.1) is happy. I looked up C++11 standard (draft n3337, §5.3.4/5) to understand this further, it says:

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. [Note: both new int and new int[10] have type int* and the type of new int[i][10] is int (*)[10]end note]

I understand that new int [10] is in action; what I get back should be freed using delete [] p and not delete p. However what I need seems to be the latter, where the allocation is not an array of integers but an array itself, as a single/whole object.

Is there a way to do it? Or my trying to do this in itself shows a misunderstanding of the C++ type system? Or it is right but is simply not allowed by the standard?


Aside: When a function takes an array whose size is fixed, as in option 2, IMHO the best thing to do for the calling function is to declare an automatic array instead of resorting to dynamically allocating one and worrying about clean-up.

Community
  • 1
  • 1
legends2k
  • 27,643
  • 22
  • 108
  • 196
  • a better highlight in that standards quote is **denotes an array type**, which explains your Array10 test – Cubbi Oct 19 '13 at 16:25
  • @Cubbi: Haha... perhaps. I've faithfully formatted it as-is in the document i.e. whatever is in boldface and italics are from the standard and the emphasis isn't mine. – legends2k Oct 19 '13 at 16:26
  • The answer is in the text you quoted: `int(*p)[10] = new int[1][10];` You could also do this: `int(*p)[10] = reinterpret_cast(::operator new(sizeof(int[10])));` – jrok Oct 19 '13 at 17:28
  • @jrok: but that seems to be a hack to me; with multidimensional array and `reinterpret_cast`. Dunno, may be it's not a hack. – legends2k Oct 20 '13 at 05:28
  • possible duplicate of [int (\*p) \[4\]?](http://stackoverflow.com/questions/3382951/int-p-4) – M.M Sep 25 '14 at 01:34
  • 1
    Not an exact duplicate of the question, but [Charles Bailey's answer](http://stackoverflow.com/a/3383747/1505939) fully answers this question – M.M Sep 25 '14 at 01:35
  • Thanks for the link to Bailey's answer. It explains the rationale behind why the returned pointer can never have the right dimension embedded in it. – legends2k Sep 29 '14 at 15:33
  • well.. I agree that the new[] can allocate an array of a size dictated by the runtime state, but then, now, 9 years later than Charles's answer, we **do have** compilers that trace `constexpr`s very well, and there's an obvious compile time vs run time difference between `new int[6]` and `new int[x]` where x is `int` and `new int[x]` where x is constexpr.. IMHO, the first one could easily return `(int(*)[6])` type and the last: `(int(*)[x])`... I only hope this gets included in the standard at some point of time – quetzalcoatl Aug 01 '19 at 13:11

2 Answers2

1

Yes, in fact it is suggested in the standard text you quoted:

int (*arr_ptr)[10] = new int[1][10];

The pointer returned by the new-expression has type int (*)[10], i.e. pointer to array of 10 ints. You need delete[] to delete it.

M.M
  • 130,300
  • 18
  • 171
  • 314
  • +1 Not what I asked, but close. It doesn't return just a pointer to an array, it returns a pointer to the first element of an array of _pointer to an array_, hence one can't `delete` it, but only `delete []` it. – legends2k Sep 29 '14 at 15:29
  • It does return a pointer to an array, it doesn't return a pointer to "the first element of an array of pointer to an array". The latter would be `new (int (*[5])[5])`. It does require `[]` to delete , as you say. – M.M Sep 29 '14 at 20:37
  • Had it been `int [2][10]` wouldn't it still return `int (*) [10]` which is nothing but the pointer to the first element of an array of (array of 10 `int`s). Just that the count is 1 in the previous case, but still an array of one element, thus one has to use `delete []`. – legends2k Sep 30 '14 at 04:50
  • Yes that's right. The first element of "array of array of 10 ints" is an array of 10 ints. Also, any object can be considered as an array of 1 element of whatever type it is. – M.M Sep 30 '14 at 04:57
0

If I have understood correctly what you want then you can use std::array. For example

{
    typedef std::array<int, 10> Array10;

    Array10 *p = new Array10;
    delete p;
}
Vlad from Moscow
  • 224,104
  • 15
  • 141
  • 268
  • No. I've clearly stated in the question that there're workarounds using the standard C++ library, but I want to do it using the language grammar only, not its library facilities. It's more on the lines of understanding the C++ type system. – legends2k Oct 19 '13 at 16:20
  • @legends2k the thing to understand here is not the C++ type system (well - that is useful but not the key issue); the key issue is with the syntax for `new`. Doing `new ` *some-array-type* gets you a pointer to *the first element of* an array with that type, whereas `new ` *non-array-type* gets you a pointer to the whole object of non-array-type. – M.M Sep 30 '14 at 05:00
  • Yes, I agree, it's the syntax of `new` that should be inspected and not type system in general here. Agree to the next point as well. – legends2k Sep 30 '14 at 06:22