почему эта арифметика указателя на двумерном массиве работает?

Я написал следующий код:

#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 это работает одинаково, хотя я этого не тестировал. Почему это работает, и есть ли официальная документация такого поведения?

2

Решение

Если вам нужен простой ответ, создайте сложный тип с помощью typedefs

это значит:

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]
1

Другие решения

*(a + b)=a[b] вы берете адрес a, перемещаете его на b и берете значение по соответствующему адресу

Так *(*(a + 3) + 4)средства *(a[3]+4) что означает a[3][4]= 12

8

Декларация

int a[m][n];

означает массив m массивы, где каждый такой внутренний массив имеет размер n,

Я помню это, помня, что последний индекс меняется быстрее всего.

В выражении (a + 3) a ссылка на внешний массив распадается на указатель на элемент этого внешнего массива. То есть указатель на размер n массив. + 3 затем добавляет в 3 раза размер байта внутреннего массива к адресу a-по указатель.

Разыменование это дает вам указатель на int,

Добавление 4 к этому дает вам 4 позиции во внутреннем массиве.

Прелесть этой схемы в том, что она также работает с «зубчатыми массивами», массивами указателей, потому что там типы разные.

Очень печально то, что он плохо работает с производными классами C ++, потому что массив Derived распадается, чтобы указывать на Derived который затем может быть неявно преобразован в указатель на Base, который при индексации дает формальное неопределенное поведение (размер Derived может быть больше, чем размер Base).

4

Видеть это вопрос для подробного объяснения.

Двумерные массивы ([][], not **) непрерывны в памяти, в результате вы можете получить доступ к элементам, используя следующую формулу:

*((int *)array + X * NUMBER_OF_COLUMNS + Y);

то есть

std::cout << *((int *)a + 3 * 10 + 4);
2

Вы можете индексировать массив, используя * (arr + index) или arr [index]. Они семантически разные, но функционально одинаковые.

Смотрите это обсуждение переполнения стека для получения дополнительной информации.

Разница между * (указатель + индекс) и указатель []

1

Вывод, который вы получили 12 и это абсолютно правильно.
Ты сделал a[i][j] = i*j ; для каждого элемента массива. Теперь вы печатаете *( *(a + 3) + 4) который так же, как a[3][4], Как ты сделал a[i][j] = i*jТеперь у вас есть a[3][4] = 3*4 = 12, Так будет печатать 12,

-2
По вопросам рекламы [email protected]