指向未指定大小数组“(*p)[]”的指针在 C++ 中非法但在 C 中合法

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

我刚刚发现这在 C++ 中是非法的(但在 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;
}

使用 gcc -std=c89 -pedantic 编译没有问题,但使用 g++ 编译失败。当我尝试使用 g++ 编译它时,我收到以下错误消息:

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

我已经在我的 C 代码中使用它很长时间了,我不知道它在 C++ 中是非法的。对我来说,这似乎是一种有用的方法来记录一个函数接受一个大小事先未知的数组。

我想知道为什么这是合法的 C 而无效的 C++。我也想知道是什么让 C++ 委员会决定取消它(并破坏与 C 的兼容性)。

那为什么这个合法的 C 代码是非法的 C++ 代码呢?

Dan Saks wrote about this in 1995,在 C++ 标准化之前:

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++ 没有 C 的 "compatible type" 概念。在 C 中,这是一个完全有效的变量重新声明:

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

在 C 中,这是对同一函数的完全有效的重新声明:

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

在 C 中,这是特定于实现的,但通常是同一变量的有效重新声明:

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

C++ 没有这些。在 C++ 中,类型要么相同,要么不同,如果它们不同,那么它们之间的差异就无关紧要了。

同样,

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

在 C 中有效,但在 C++ 中无效:尽管 []array 被声明为长度为 6 的数组。pointer 被声明为指向未指定长度的数组,这是一种不同的类型。没有从 const char (*)[6]const char (*)[].

的隐式转换

正因为如此,采用指向未指定长度数组的指针的函数在 C++ 中几乎毫无用处,而且几乎肯定是程序员的错误。如果你从一个具体的数组实例开始,你几乎总是已经有了大小,所以你不能获取它的地址以便将它传递给你的函数,因为你会有类型不匹配。

并且在您的示例中也不需要指向未指定长度数组的指针:在 C 中编写它的正常方法恰好在 C++ 中也有效,是

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

被称为accumulate(ARRAY_LENGTH(a), a)