Я написал следующий код:
#include <iostream>
using namespace std;
int main()
{
int a[10][10];
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
a[i][j] = i * j;
cout << *(*(a + 3) + 4) << endl;
return 0;
}
Я ожидал, что он напечатает некоторые данные мусора или ошибку сегментации. То, что я получил, было 12. Я проверил его как на c, так и на c ++ (используя gcc и g ++ соответственно), и я понял, что на VS это работает одинаково, хотя я этого не тестировал. Почему это работает, и есть ли официальная документация такого поведения?
Если вам нужен простой ответ, создайте сложный тип с помощью typedef
s
это значит:
int a[10][10];
будет:
typedef int a10[10]; //a10 is array of 10 ints
a10 a[10]; //a is array of 10 a10s
Теперь, чтобы понять размеры и положение:
sizeof(a) = 10 * 10 * sizeof int
sizeof(a[0]) = is 10 * sizeof int
a+1 is equal to &a[1]
Когда вы увеличиваете указатель на 3, это означает увеличение на размер типа.
address of a[1] == address of a[0] + sizeof(a[0])
Следовательно:
*(*(a + 3) + 4) == a[3][4]
*(a + b)=a[b]
вы берете адрес a, перемещаете его на b и берете значение по соответствующему адресу
Так *(*(a + 3) + 4)
средства *(a[3]+4)
что означает a[3][4]
= 12
Декларация
int a[m][n];
означает массив m
массивы, где каждый такой внутренний массив имеет размер n
,
Я помню это, помня, что последний индекс меняется быстрее всего.
В выражении (a + 3)
a
ссылка на внешний массив распадается на указатель на элемент этого внешнего массива. То есть указатель на размер n
массив. + 3
затем добавляет в 3 раза размер байта внутреннего массива к адресу a
-по указатель.
Разыменование это дает вам указатель на int
,
Добавление 4 к этому дает вам 4 позиции во внутреннем массиве.
Прелесть этой схемы в том, что она также работает с «зубчатыми массивами», массивами указателей, потому что там типы разные.
Очень печально то, что он плохо работает с производными классами C ++, потому что массив Derived
распадается, чтобы указывать на Derived
который затем может быть неявно преобразован в указатель на Base
, который при индексации дает формальное неопределенное поведение (размер Derived
может быть больше, чем размер Base
).
Видеть это вопрос для подробного объяснения.
Двумерные массивы ([][], not **
) непрерывны в памяти, в результате вы можете получить доступ к элементам, используя следующую формулу:
*((int *)array + X * NUMBER_OF_COLUMNS + Y);
то есть
std::cout << *((int *)a + 3 * 10 + 4);
Вы можете индексировать массив, используя * (arr + index) или arr [index]. Они семантически разные, но функционально одинаковые.
Смотрите это обсуждение переполнения стека для получения дополнительной информации.
Вывод, который вы получили 12
и это абсолютно правильно.
Ты сделал a[i][j] = i*j ;
для каждого элемента массива. Теперь вы печатаете *( *(a + 3) + 4)
который так же, как a[3][4]
, Как ты сделал a[i][j] = i*j
Теперь у вас есть a[3][4] = 3*4 = 12
, Так будет печатать 12
,