Рассмотрим эту программу на C
int main()
{
int arr[][3] = {1,2,3,4,5,6};
int **p= &arr;
printf("%u %u" , *p, *(p+1));
return 0;
}
Некоторые компиляторы выдают результат как 1,4, некоторые компиляторы выдают ошибку совместимости типов.
По моему мнению, нам нужно двойное разыменование, чтобы получить правильное значение, и если у нас есть 2D-массив, тогда не будет ли двойной указатель указывать на его первый элемент, тогда почему мы получаем ошибку совместимости типов и почему ссылаемся только на один раз, предоставляя массив элемент?
Несмотря на то, что arr
является двумерным массивом, он по-прежнему рассматривается как int*
, а не как двойной указатель. Единственное отличие состоит в том, что доступ к элементам осуществляется немного другим способом.
Внутри программной памяти arr
находится обычный указатель на int
, а p
имеет тип int**
. Кроме того, вы пытаетесь напечатать *p
(который будет иметь тип int*
), форматируя его как unsigned
, поэтому имеет смысл только получить ошибку несовместимого типа.
В заключение, если вы хотите получить доступ к элементу из arr
с помощью p
, формула будет *(p + 3 * row + column)
.
Глядя на вашу первую инициализацию, int arr[][3] = {1,2,3,4,5,6};
,
если вы хотите получить доступ к первому элементу arr
(1), вы должны сделать **arr
.
Чтобы получить второй элемент с обозначением указателя, вы делаете следующее: *(*arr + 1)
и продолжаете добавлять, чтобы получить другие элементы. Это связано с тем, что все элементы двумерного массива хранятся в памяти один за другим (например, если мой первый элемент находится по адресу памяти 0, следующий находится по адресу памяти 4, поскольку int занимает 4 байта).
Вы также можете получить доступ к подмассивам, разыменовав arr[x][y]
следующим образом:
*(*(arr + x_index) + y_index)
Вы получаете указатель на двумерный массив следующим образом:
int (*p)[3] = arr;
А ещё вы можете сделать int *p = *arr;
. Вы можете разыменовать p, как если бы это был одномерный массив.
Здравствуйте, тогда почему int **p = &arr выдает несоответствие типов? Я знаю, что int(*p)[3] — лучшее объявление, но я нахожу причину.
Данный ...
int arr[][3] = {1,2,3,4,5,6};
... тип &arr
— int (*)[2][3]
. Это не то же самое, что и int **
, и не совместимо с ним. Вы можете преобразовать значение первого типа в значение второго типа, но спецификация языка требует для этого явного приведения типов.
Некоторые компиляторы выдают результат как 1,4, некоторые компиляторы выдают ошибку совместимости типов.
Некоторые компиляторы, очевидно, предоставляют расширение, выполняя это преобразование неявно, а некоторые оказывают вам большую услугу, указывая на вашу ошибку. Те, которые выполняют неявное преобразование, не приносят вам пользы. Данный ...
int **p = &arr;
, *p
имеет тип int *
, но на самом деле p
указывает на массив массивов. Массивы не являются указателями, и здесь вы не увидите распада массива, потому что это функция типа выражения. Среди типов p
, *p
и **p
нет типа массива. Если вы оцениваете *p
, вы нарушаете строгое правило псевдонимов и на практике, скорее всего, попытаетесь интерпретировать первые несколько байтов arr
как указатель. Скорее всего, это будут либо 1
отдельно, либо 1
и 2
вместе.
По моему мнению, нам нужно двойное разыменование, чтобы получить правильное значение.
Да и нет. Массивы не являются указателями, но в большинстве выражений они автоматически преобразуются в указатели на свои первые элементы. Чтобы получить отдельный int
из вашего 2D-массива, вам понадобится комбинация преобразований массива в указатель, арифметики указателей и, да, двух разыменований. Но сами по себе разыменования этого не делают.
и если у нас есть 2D-массив, тогда не будет ли двойной указатель указывать на его первый элемент, тогда почему мы получаем ошибку совместимости типов и почему ссылаемся только на один раз, давая элемент массива?
Пытаясь получить доступ через неправильный тип указателя, вы препятствуете правильному выполнению некоторых из этих вещей. Это нарушение строгого правила псевдонимов, и, что касается C, случиться может что угодно.
Вы, вероятно, хотели этого:
int (*p)[3] = arr;
Обратите внимание как на изменение типа p
(теперь это указатель на массив из 3 int
), так и на отсутствие оператора &
. Новый тип p
— это правильный тип указателя, до которого arr
распадается, поэтому преобразование не требуется. Пока p
содержит это значение, вы можете использовать его имя взаимозаменяемо с arr
для большинства (но не всех) целей в их общей области действия.
В частности, если вы скажете *p
, то результат будет в массиве, подверженном распаду до указателя, поэтому вы также можете сделать **p
, чтобы получить 1.
Спасибо, это проясняет ситуацию.
MSVC выдает 5 предупреждений для этого короткого кода. Вы исправили предупреждения? Двойной указатель не несет информации о размерах двумерного массива. И вы передаете неправильные типы
printf
. Вы передаете указатели вместо значений (неправильного типа).