Pointer to array of unspecified size "(*p)[]" illegal in C++ but legal in C

I just found out that this is illegal in C++ (but legal in C):

#include <stdio.h>
#include <stdlib.h>
#define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0]))

int accumulate(int n, const int (*array)[])
{
    int i;
    int sum = 0;
    for (i = 0; i < n; ++i) {
        sum += (*array)[i];
    }
    return sum;
}

int main(void)
{
    int a[] = {3, 4, 2, 4, 6, 1, -40, 23, 35};
    printf("%d\n", accumulate(ARRAY_LENGTH(a), &a));
    return 0;
}

It compiles without problems using gcc -std=c89 -pedantic but fails to compile using g++. When I try to compile it using g++ I get these error messages:

main.cpp:5:37: error: parameter 'array' includes pointer to array of unknown bound 'int []'
 int accumulate(int n, int (*array)[])
                                     ^
main.cpp: In function 'int main()':
main.cpp:18:50: error: cannot convert 'int (*)[9]' to 'int (*)[]' for argument '2' to 'int accumulate(int, int (*)[])'
     printf("%d\n", accumulate(ARRAY_LENGTH(a), &a));

I have been using this in my C code for a long time and I had no idea that it was illegal in C++. To me this seems like a useful way to document that a function takes an array whose size is not known before hand.

I want to know why this is legal C but invalid C++. I also wonder what it was that made the C++ committee decide to take it away (and breaking this compatibility with C).

So why is this legal C code but illegal C++ code?


Dan Saks wrote about this in 1995, during the lead up to C++ standardisation:

The committees decided that functions such as this, that accept a pointer or reference to an array with unknown bound, complicate declaration matching and overload resolution rules in C++. The committees agreed that, since such functions have little utility and are fairly uncommon, it would be simplest to just ban them. Hence, the C++ draft now states:

If the type of a parameter includes a type of the form pointer to array of unknown bound of T or reference to array of unknown bound of T, the program is ill-formed.


C++ doesn't have C's notion of "compatible type". In C, this is a perfectly valid redeclaration of a variable:

extern int (*a)[];
extern int (*a)[3];

In C, this is a perfectly valid redeclaration of the same function:

extern void f();
extern void f(int);

In C, this is implementation-specific, but typically a valid redeclaration of the same variable:

enum E { A, B, C };
extern enum E a;
extern unsigned int a;

C++ doesn't have any of that. In C++, types are either the same, or are different, and if they are different, then there is very little concern in how different they are.

Similarly,

int main() {
  const char array[] = "Hello";
  const char (*pointer)[] = &array;
}

is valid in C, but invalid in C++: array, despite the [], is declared as an array of length 6. pointer is declared as a pointer to an array of unspecified length, which is a different type. There is no implicit conversion from const char (*)[6] to const char (*)[].

Because of that, functions taking pointers to arrays of unspecified length are pretty much useless in C++, and almost certainly a mistake on the part of the programmer. If you start from a concrete array instance, you almost always have the size already, so you cannot take its address in order to pass it to your function, because you would have a type mismatch.

And there is no need for pointers to arrays of unspecified length in your example either: the normal way to write that in C, which happens to also be valid in C++, is

int accumulate(int n, int *array)
{
    int i;
    int sum = 0;
    for (i = 0; i < n; ++i) {
        sum += array[i];
    }
    return sum;
}

to be called as accumulate(ARRAY_LENGTH(a), a).