"Int (*) []" затухает в "int **" в параметре функции?
Я опубликовал этот question на programers.stackexchange ранее сегодня. Я всегда предполагал, что int (*)[]
не распадается на int **
в функциональных параметрах, но я получил несколько ответов на мой вопрос, который предположил, что он это делает.
Я использовал int (*)[]
в моих функциональных параметрах, но теперь я действительно запутался.
Когда я скомпилирую эту функцию с помощью gcc -std=c99 -pedantic -Wall
void function(int (*a)[])
{
sizeof(*a);
}
Я получаю это сообщение об ошибке:
c99 -Wall -pedantic -c main.c -o main.o
main.c: In function ‘function’:
main.c:3:11: error: invalid application of ‘sizeof’ to incomplete type ‘int[]’
make: *** [main.o] Error 1
Из чего следует, что *a
имеет тип int []
, а не int *
.
Может кто-нибудь объяснить, если такие вещи, как int (*)[]
, распадается на int **
в параметрах функции и дают мне некоторую ссылку (возможно, из стандартных документов), которая доказывает, почему это так.
Ответы
Ответ 1
Только типы массивов, преобразованные в указатель на его первый элемент при передаче функции. a
имеет указатель на тип массива int
, т.е. , он имеет тип указателя и, следовательно, не имеет преобразования.
Для прототипа
void foo(int a[][10]);
Компилятор интерпретирует его как
void foo(int (*a)[10]);
потому что a[]
имеет тип массива. int a[][10]
никогда не будет преобразован в int **a
. Тем не менее, второй пара в том, что ответ является неправильным и вводящим в заблуждение.
В качестве параметра функции int *a[]
эквивалентен int **
, это связано с тем, что a
имеет тип массива.
Ответ 2
int (*)[]
является указателем на массив int
.
В вашем примере *a
может затухать до int*
. Но sizeof(*a)
не затухает; это существенно sizeof(int[])
, что недопустимо.
a
не может вообще затухать (это указатель).
Ответ 3
N1256 §6.7.5.3/p7-8
7 Объявление параметра как '' массива типа должно быть скорректирован на "квалифицированный указатель на тип, где квалификаторы типа (если есть) - это те, которые указаны в [
и ]
для вывода типа массива. Если ключевое слово static
также появляется в пределах [
и ]
типа массива вывода, то для каждого вызова функции значение соответствующий фактический аргумент должен обеспечить доступ к первому элемент массива с по меньшей мере таким количеством элементов, как указано в размер.
8 Объявление параметра как возвращаемого типа функции должно отрегулировать на" указатель на возвращаемый тип функции", как в 6.3.2.1.
int (*)[]
является "указателем на массив int
". Это "массив типа"? Нет, это указатель. Является ли это функцией возвращаемого типа? Нет, это указатель. Поэтому он не корректируется.
Ответ 4
В случае int (*a)[]
, sizeof *a
не работает по одной причине: для массива нет элемента. Без подсчета элементов размер не может быть рассчитан.
В результате этого любая арифметика указателя на a
не будет работать, потому что она определяется в терминах размера объекта. Поскольку размер не определен для массива, вы не можете использовать арифметику указателя на самом указателе. Обозначение массива определяется в терминах арифметики указателя, поэтому sizeof a[0][0]
(или любое выражение, включающее a[n]
, не будет работать, тогда как sizeof (*a)[0]
будет.
Это означает, что вы можете сделать очень мало с указателем. Единственное, что разрешено:
- разыменование указателя с помощью унарного оператора
*
- передача указателя на другую функцию (тип параметра функции должен быть массивом массивов или указателем на массив)
- получение размера указателя (и выравнивания или типа, если ваш компилятор поддерживает их или их обоих)
- назначение указателя на совместимый тип
Если ваш компилятор поддерживает массивы переменной длины (VLAs), и вы знаете размер, вы можете обойти проблему, просто добавив строку в начале тела функции, как в
void
foo (int (*a0)[], size_t m, size_t n)
{
int (*a)[n] = a0;
...
}
Без VLA вы должны прибегнуть к какой-то другой мере.
Стоит отметить, что динамическое распределение не является фактором с int (*)[]
. Массив массивов распадается на указатель на массив (как и здесь), поэтому они взаимозаменяемы при передаче их функции (sizeof
и любые ключевые слова _Alignof
или typeof
являются операторами, а не функциями). Это означает, что указанный массив должен быть статически распределен: как только массив распадается на указатель, больше не происходит распад, поэтому вы не можете сказать, что указатель на массив (int (*)[]
) совпадает с указателем на указатель (int **
). В противном случае ваш компилятор с радостью передаст int [3][3]
функции, которая принимает int **
вместо желаемого параметра формы int (*)[]
, int (*)[n]
, int [][n]
или int [m][n]
.
Следовательно, даже если ваш компилятор не поддерживает VLA, вы можете использовать тот факт, что статически выделенный массив имеет все свои элементы, сгруппированные вместе:
void foo (int (*a0)[], size_t m, size_t n)
{
int *a = *a0;
size_t i, j;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
// Do something with `a[i * n + j]`, which is `a0[i][j]`.
}
}
...
}
Динамически распределенный одномерный массив, который используется в качестве двумерного массива, обладает теми же свойствами, поэтому это все еще работает. Только тогда, когда второе измерение динамически распределено, это означает, что цикл, подобный for (i = 0; i < m; i++) a[i] = malloc (n * sizeof *a[i]);
, распределяет каждую подматрицу индивидуально, что этот принцип не работает. Это связано с тем, что у вас есть массив указателей (int *[]
или int **
после распада массива), которые указывают на первый элемент массива в другом месте в памяти, а не на массив массивов, который сохраняет все элементы вместе.
Итак:
-
нет, int (*p)[]
и int **q
не могут использоваться одинаково. p
является указателем на массив, что означает, что все элементы группируются вместе, начиная адрес, хранящийся в p
. q
является указателем на указатель, что означает, что элементы могут быть разбросаны по разным адресам, которые хранятся в q[0]
, q[1]
,..., q[m - 1]
.
-
sizeof *p
не работает, потому что p
указывает на массив с неизвестным количеством элементов. Компилятор не может рассчитать размер каждого элемента, поэтому операции с самим p
очень ограничены.