я думал так
int a1[5];
и
int a2[6];
были отдельными типами.
Но
void foo(int a[5]){};
void foo(int a[6]){};
не будет компилироваться, говоря, что это дублированные определения foo (т.е. foo(int *a))
Я был очень удивлен этим. Одна из причин, почему VLA не разрешены в стандарте C++, заключается в том, что они нарушают систему типов.
Я также был удивлен, обнаружив, что c делает то же самое.
void foo(int a[4]){
printf("sz=%ld", sizeof(a));
}
сообщает размер указателя. Я ожидал, что он сообщит 4*sizeof(int) (и примет только переменную типа int[4], которая будет принята в качестве аргумента вызова)
Ну, да. a — указатель. Массивы в аргументах — это просто указатели. Это имеет место как в C, так и в C++.
в C/C++, когда вы передаете массив функции, вы получаете только указатель на 0-й индекс массива, обойти его невозможно, поэтому многие функции в C/C++ принимают массив и массив sizeof при каждом вызове возможно, вы захотите посмотреть это видео youtube.com/watch?v=GaclhRKfxtI
@pm100, int a[5] как параметр функции имеет тот же тип, что и int *a. Как и int a[6]. Это не проблема с массивом, указывающим на первый элемент, а проблема языка, связанная с объявлениями функций.
Если вам нужен настоящий массив, вам нужно передать его по ссылке (void foo(int (&a)[5]){})
@pm100 printf("sz=%ld", sizeof(a)); лучше как printf("sz=%zu", sizeof(a));, чтобы избежать проблем с боковой панелью из-за использования неправильного спецификатора.
Я думаю, это одна из причин использования std::array: разные размеры являются разными типами.
@EtiennedeMartel - Хорошо, теперь я понимаю, что объявление функции arg как int[5] — это то же самое, что и int * (что для меня неожиданно). Но отличаются ли int[5] и int[6] в других отношениях?
@pm100 Да. Аргументы функций особенные — и в C, и в C++ (некоторые могут сказать, что это аномалия языка, но это так). Существуют и другие контексты, в которых int[5] и int [6] относятся к разным типам. например int x[5]; int (&r)[5] = x; int (*p)[5] = &x; действителен (и определяет r и p соответственно как ссылку и указатель на массив из 5 int), но int x[6]; int (&r)[5] = x; int (*p)[5] = &x имеет две диагностируемые ошибки (поскольку ни ссылка, ни указатель на массив из 5 элементов не могут ссылаться/указывать на массив из 6 элементов).
Типы int[5] и int[6] различны, а типы void(int[5]), void(int[6]) и void(int*) из-за распада массива одинаковы.
Вас сбивает с толку то, что вы не можете иметь аргумент с типом массива - он всегда будет преобразован в указатель на тип элемента массива, чтобы соответствовать типу указателя, в который массив будет автоматически преобразован при вы пытаетесь использовать его в качестве аргумента при вызове.
Полностью избегайте этой проблемы, просто забывая о существовании массивов в стиле C и вместо этого используйте std::array или std::vector.





Да, это разные типы, это можно подтвердить:
int a[5];
int b[6];
static_assert(!std::is_same_v<decltype(a), decltype(b)>);
Ваша проблема в том, что в списках параметров функций массивы молча заменяются указателями, поэтому и void foo(int a[5]), и void foo(int a[6]) в конечном итоге становятся void foo(int *a).
Это (замена типа массива указателем) происходит только с реальными массивами, в отличие, например, от ссылки на массивы, поэтому void foo(int (&a)[5]) будет работать (или int (*a)[5] в C).
различны ли они в c, есть ли способ показать это?
@pm100 sizeof будут разными, что AFAIK означает, что они не могут быть одного типа.
@pm100 int x[5]; int (*p)[6] = &x не будет компилироваться, поскольку x — это массив из пяти элементов, а p — указатель на массив из 6 элементов. Это верно как для C, так и для C++. Кроме того, sizeof(int [5]) и sizeof(int[6]) будут давать разные значения на обоих языках (а sizeof(int *) — это еще одно значение, которое, вероятно, не будет равно ни одному из них)
Типы функций аналогичным образом заменяются указателями, когда вы используете их в качестве типов аргументов в объявлении, так что это не просто типы массивов.
Любопытство в этой теме заключается в том, что объект массива может иметь другой объявленный тип в одной точке, скажем, в одной единице перевода (TU; в этот момент его тип является неполным), а в другой единице трансляции может быть полным и иметь другой объявленный тип. , то есть int a[] в TU #1 имеет другой тип, чем int a[5] в TU #2. Добавление еще одного определения int a[7] в ТУ №3 — это UB через нарушение ODR. Объекты-массивы — одни из немногих объектов, которые могут иметь разные типы в разных контекстах.
Re «Это происходит только с массивами»: я думаю, вы имеете в виду, что это происходит с параметрами, которые напрямую объявлены как массивы, а не с массивами, вложенными в объявление параметра. Однако, как уже говорилось, это происходит не только с массивами. Параметр, объявленный как функция, также будет преобразован в параметр, который является указателем на функцию.
@EricPostpischil Под «этим» я имел в виду замену массива указателем. Теперь должно звучать лучше.
Мне нужно проверить, но я думаю, что вы просто сталкиваетесь с обычным старым преобразованием указателя массива.