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