Подтвердить что ты не робот

Указатель на массив неопределенного размера "(* p) []" незаконный в С++, но законный в 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-код, но незаконный код на С++?

4b9b3361

Ответ 1

Дэн Сакс написал об этом в 1995 году, во время подготовки к стандартизации С++:

Комитеты решили, что такие функции, которые указатель или ссылка на массив с неизвестной границей, усложняют правила сопоставления объявлений и правил перегрузки в С++. комитеты согласились с тем, что, поскольку такие функции мало полезны и являются довольно необычными, было бы проще просто запретить их. Следовательно В проекте С++ теперь говорится:

Если тип параметра включает тип указателя формы на   массив неизвестной границы T или ссылка на массив неизвестной границы   T, программа плохо сформирована.

Ответ 2

С++ не имеет понятия "совместимый тип". В 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;

У С++ этого нет. В С++ типы либо одинаковые, либо разные, и если они разные, тогда очень мало внимания в том, насколько они различны.

Аналогично,

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

действителен в C, но недействителен в С++: array, несмотря на [], объявляется как массив длины 6. pointer объявляется как указатель на массив неопределенной длины, который является другой тип. Нет никакого неявного преобразования от const char (*)[6] до const char (*)[].

Из-за этого функции, содержащие указатели на массивы неопределенной длины, в С++ почти бесполезны, и почти наверняка ошибка со стороны программиста. Если вы начинаете с конкретного экземпляра массива, вы почти всегда имеете размер уже, поэтому вы не можете взять его адрес, чтобы передать его своей функции, потому что у вас будет несоответствие типа.

И нет необходимости в указателях на массивы неопределенной длины в вашем примере: обычный способ записи в 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).