Не понимаю, почему при разыменовывании указателя на массив получается адрес первого значения массива
int array[2][2] = {{0,1},{0,1}};
int (*p)[2];
p = array;
p = address of a[0][0], p+1 = address of a[1][0],
*p = address of a[0][0], (*p)+1 = the address of a[0][1];
Я понимаю, что p - это указатель на массив из двух целых чисел, а не указатель на целое число. Но если мы выведем значение p, это все равно будет адрес целого числа. Я хочу знать, что происходит под капотом? Является ли p указателем на массив указателей? Итак, мы должны разыменовать его дважды, чтобы получить значение целого числа, на которое он указывает?
Указатель на int,
int* p;
это переменная, хранящаяся в ячейке, которая содержит адрес другой переменной (int).
Когда мы разыменовываем его, мы получаем значение этой переменной. Как этот процесс работает именно с указателем на массив?
хорошо, я только что исправил это
Есть две вещи, которые вам нужно помнить, когда речь заходит о массивах и их связи с указателями. Во-первых, массив сам по себе распадается на указатель на его первый элемент. Это означает, что array точно такое же, как &array[0]. Во-вторых, для любого массива или указателя array и индекса i выражение array[i] в точности равно *(array + i).
2D-массив представляет собой массив 1D-массивов. p указывает на первый одномерный массив в array. Итак, p указывает на array[0], а p+1 указывает на array[1]. Напомним, что при доступе массив преобразуется в указатель на его первый элемент. Таким образом, *p будет указывать на первый элемент первой строки (это то же самое, что *(p+0), то же самое, что и p[0])
А когда имеешь дело с массивами и указателями, я очень рекомендую рисовать их на бумаге. Используйте квадраты для одиночных переменных, прямоугольники для массивов (разделите, чтобы показать элементы) и стрелки для указателей. Обычно это помогает получить общее представление о вещах.
p = address of a[0][0] Вместо этого должен быть адрес a[0] (да, адрес тот же, но тип другой). p+1 = address of a[1][0] Вместо этого должен быть адрес a[1]. *(p+1) = the address of a[0][1] Нет, это противоречит тому, что вы написали выше.
@dxiv Извините, опечатка



Доступ к значению в массиве можно получить с помощьюarray[i] = *(array+i) (расширение [i]).
Аналогично, (*p)[i] = *(*(p+i)). Итак, нам нужно дважды разыменовать значение, чтобы получить доступ к нему.
Если вы хотите получить доступ, array[0][0], вы должны использовать *(*(p+0)+0) = **p;,
Точно так же array[0][1] можно получить с помощью *(*(p+0)+1) = *((*p)+1);
Обратите внимание, *p указывает на первую строку (хранит адрес array[0][0]), а *(p+1) указывает на вторую строку (хранит адрес array[1][0]).
Пожалуйста, проверьте приведенный ниже код:
#include <stdio.h>
int main()
{
int array[2][2] = {{1, 2}, {3, 4}};
int(*p)[2];
p = array;
printf("The address of array[0][0] is %p \n",&array[0][0]);
printf("The address of *p is %p \n",*p);
printf("The address of array[1][0] is %p \n",&array[1][0]);
printf("The address of *(p+1) is %p \n",*(p+1));
printf("The value of array[0][0] is %d \n",array[0][0]);
printf("The value of **p is %d \n",**p);
printf("The value of array[0][1] is %d \n",array[0][1]);
printf("The value of *((*p)+1) is %d \n",*((*p)+1));
return 0;
}
Результат:
The address of array[0][0] is 0x7fff6e8781a0
The address of *p is 0x7fff6e8781a0
The address of array[1][0] is 0x7fff6e8781a8
The address of *(p+1) is 0x7fff6e8781a8
The value of array[0][0] is 1
The value of **p is 1
The value of array[0][1] is 2
The value of *((*p)+1) is 2
Учитывая определение int array[2][2] = {{0,1},{0,1}};, компилятор упорядочивает некоторое место в памяти, чтобы оно содержало значения int 0, 1, 0 и 1. Предположим, что это место имеет адрес 1000, а значение int 0 хранится в байтах 1000-1003, 1 хранится в диапазоне от 1004 до 1007, 0 сохраняется в диапазоне от 1008 до 1011, а 1 сохраняется в диапазоне от 1012 до 1015.
Где в памяти начинается элемент array[0][0]? На месте 1000.
Где в памяти начинается массив array? На месте 1000.
Где в памяти начинается массив array[0]? На месте 1000.
Массив начинается в памяти в том же месте, где его первый элемент начинается в памяти. Кроме того, array[0], который сам по себе является массивом, начинается с позиции 1000.
Итак, после p = array;, p указывает на позицию 1000. И элемент array[0][0] также начинается с позиции 1000. Таким образом, когда вы печатаете p, как и в случае с printf("%p\n", (void *) p);, неудивительно, что вы получаете тот же результат, что и при печати адреса array[0][0], как с printf("%p\n", (void *) &array[0][0]);.
Далее рассмотрим *p. Тип p — это int (*)[2], указатель на массив из 2 int. Следовательно, *p — это массив из 2 int.
В частности, *p — это массив. Предположим, мы пытаемся напечатать его, передав его в качестве аргумента printf. Что происходит?
В C, когда массив используется в выражении, он автоматически преобразуется в адрес своего первого элемента (за исключением случаев, когда массив является операндом sizeof или унарным & или строковым литералом, используемым для инициализации массива). Итак, если вы используете *p в качестве аргумента для printf, изначально это означает array[0], но этот массив преобразуется в адрес своего первого аргумента. Таким образом, передача *p в качестве аргумента фактически передает &array[0][0] (или, что то же самое, &(*p)[0]).
Таким образом, printf("%p\n", (void *) *p); напечатает тот же адрес, что и printf("%p\n", (void *) &a[0][0]);.
Что говорят ваши предупреждения о присваивании несовместимых типов указателей с
p = array;?int (*p)[2];будет правильно.