34

Consider following code:

int main() {
    int (*p)[]; // pointer to array with unspecified bounds

    int a[] = {1};
    int b[] = {1,2};

    p = &a; // works in C but not in C++
    p = &b; // works in C but not in C++

    return 0;
}

In pure C you can assign a pointer to this type of address of an array of any dimension. But in C++ you can't. I found one case when compiler allows assign value to such pointer:

struct C
{
    static int v[];
};

int main() 
{
    int (*p)[] = &C::v; // works in C++ if 'v' isn't defined (only declared)
    return 0;
}

But could not find any useful case of this code.

Can anyone give an useful example (in C++) of pointer to array with unspecified bounds? Or is it only vestige remaining from C?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
αλεχολυτ
  • 4,792
  • 1
  • 35
  • 71

2 Answers2

13

Such a pointer cannot participate in pointer arithmetic, potentially useful things that still can be done are to get its type with decltype or reinterpret_cast it to another pointer type or intptr_t. This is because section 3.9p6 says:

A class type (such as "class X") might be incomplete at one point in a translation unit and complete later on; the type "class X" is the same type at both points. The declared type of an array object might be an array of incomplete class type and therefore incomplete; if the class type is completed later on in the translation unit, the array type becomes complete; the array type at those two points is the same type. The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points ("array of unknown bound of T" and "array of N T") are different types. The type of a pointer to array of unknown size, or of a type defined by a typedef declaration to be an array of unknown size, cannot be completed.

5.3.1 says:

Note: indirection through a pointer to an incomplete type (other than cv void) is valid. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see 4.1.

Since array-to-pointer decay can be performed on array lvalues without prior conversion to rvalue, the code dyp left in a comment is correct:

(*p)[i]

Relevant rule, from 4.2:

An lvalue or rvalue of type 'array of N T" or "array of unknown bound of T" can be converted to a prvalue of type "pointer to T". The result is a pointer to the first element of the array.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Yes :) I guess arrays of unknown bounds have their uses, and you can form a pointer to pretty much everything that is not a reference. So maybe they kept it for consistency? I don't quite see what you could achieve with it that couldn't be done with mere pointers to the first element. One minor point might be that they convey that they point to an *array* as opposed to pointers to the first element. – dyp Jun 29 '14 at 21:39
  • @dyp: Well, the type certainly is useful, as in `std::unique_ptr` vs `std::unique_ptr`. But that doesn't actually require a pointer to the array-of-unknown-bound type, since it should be implemented via a specialization anyway. Hmmm, would `delete p;` automatically be `delete[] (int*)*p;`? I think it would, because `*p` is an array type. Except I think the detection of array type is only done for `new`, not `delete`. – Ben Voigt Jun 29 '14 at 21:46
  • 1
    @dyp: In particular, this note "this means that the syntax of the delete-expression must match the type of the object allocated by new, not the syntax of the new-expression." seems to indicate that yes, `std::unique_ptr` needs to be implemented via specialization. – Ben Voigt Jun 29 '14 at 21:49
3

I think a compiler should accept it (irrespective of the -O setting) because a definition of the static can be provided by another compilation unit. (This is perhaps a pragmatic deviation from the standard - I'm not an C++ expert.) The posted snippet can be compiled, but it is incomplete and cannot be brought to execution without a definition of the static member.

File c.h:

struct C {
    static int v[];
};

File x.cpp

#include "c.h"
#include <iostream>
int main(){
    int (*p)[] = &C::v; // works in C++ if 'v' isn't defined (only declared)
    std::cout << (*p)[0] << std::endl; 
    return 0;
}

File y.cpp

#include "c.h"
int C::v[3] = {1,2,3};

Compiled and linked using (dont-tell-me-it's-old) g++ 4.3.3. Prints 1.

anatolyg
  • 26,506
  • 9
  • 60
  • 134
laune
  • 31,114
  • 3
  • 29
  • 42