#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[101][101];
a[2][0]=10;
cout<<a+2<<endl;
cout<<*(a+2)<<endl;
cout<<*(*(a+2));
return 0;
}
Почему значения a+2 и *(a+2) одинаковы? Заранее спасибо!
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[101][101];
a[2][0]=10;
cout<<a+2<<endl;
cout<<*(a+2)<<endl;
cout<<*(*(a+2));
return 0;
}
Почему значения a+2 и *(a+2) одинаковы? Заранее спасибо!
a
— это двумерный массив, то есть массив массивов. Но он превращается в указатель на массив при использовании в соответствующем контексте. Так:
a+2
, a
распадается на указатель на массивы int размером 101. Когда вы передаете is в ostream, вы получаете адрес первого элемента этого массива, то есть &(a[2][0])
*(a+2)
по определению a[2]
: это массив размером 101, начинающийся с a[2][0]
. Он распадается на указатель на int, и когда вы передаете его в ostream, вы получаете адрес его первого элемента, то есть по-прежнему &(a[2][0])
**(a+2)
по определению a[2][0]
. Когда вы передаете его ostream, вы получаете его значение int, здесь 10.Но будьте осторожны: a + 2
и a[2]
оба являются указателями на один и тот же адрес (static_cast<void *>(a+2)
совпадает с static_cast<void *>(a[2])
), но они являются указателями на разные типы: первый указывает на массив int размером 101, последний на int.
Я попытаюсь объяснить вам, как память отображается компилятором:
Давайте рассмотрим более практичный пример многомерного массива:
int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Вы можете выполнить команду
x/10w a
В GDB и посмотрите на память:
0x7fffffffe750: 1 2 3 4
0x7fffffffe760: 5 6 7 8
0x7fffffffe770: 9 0
Каждый элемент хранится в типе int (32 бита/4 байта). Таким образом, первый элемент матрицы был сохранен в:
1) a[0][0] -> 0x7fffffffe750
2) a[0][1] -> 0x7fffffffe754
3) a[0][2] -> 0x7fffffffe758
4) a[1][0] -> 0x7fffffffe75c
5) a[1][1] -> 0x7fffffffe760
6) a[1][2] -> 0x7fffffffe764
7) a[2][0] -> 0x7fffffffe768
...
Команда:
std::cout << a + 2 << '\n'
Он напечатает адрес 0x7fffffffe768 из-за арифметики указателя: тип a — int**, поэтому это указатель на указатели. a+2 — это a[0] (первая строка) + 2. Результатом является указатель на третью строку.
*(a+2) уважает третью строку, это {7,8,9}
Третья строка представляет собой массив int, это указатель на int.
Затем оператор‹‹ напечатает значение этого указателя.
a
на самом деле не int[3][3]
?
- person wally; 05.07.2016
Двумерный массив — это массив массивов, поэтому он хранится в памяти следующим образом:
char v[2][3] = {{1,3,5},{5,10,2}};
Content: | 1 | 3 | 5 | 5 | 10 | 2
Address: v v+1 v+2 v+3 v+4 v+5
Чтобы получить доступ к v[x][y], компилятор переписывает его как: *(v + y * M + x)
(где M — указанное второе измерение)
Например, для доступа к v[1][1] компилятор переписывает его как *(v + 1*3 + 1)
=> *(v + 4)
Имейте в виду, что это не то же самое, что указатель на указатель (char**). Указатель на указатель не является массивом: он содержит и адрес на ячейку памяти, которая содержит другой адрес.
Чтобы получить доступ к элементу двумерного массива с помощью указателя на указатель, делается следующее:
char **p;
/* Initialize it */
char c = p[3][5];
p
;Чтобы получить доступ к элементу через традиционный двумерный массив, выполните следующие действия:
char p[10][10];
char c = p[3][5];
p
и суммируйте первое смещение (3), умноженное на размер строки (10).Если у вас есть такой массив
T a[N];
то имя массива неявно преобразуется в указатель на его первый элемент за редкими исключениями (как, например, использование имени массива в операторе sizeof
).
Так например в выражении ( a + 2 )
a
преобразуется тип T *
со значением &a[0]
.
Относительно вашего примера с массивом
int a[101][101];
в выражении
a + 2
a преобразуется в rvalue типа int ( * )[101]
и указывает на первую «строку» массива. a + 2
указывает на третью «строку» массива. Тип строки int[101]
Выражение *(a+2)
дает эту третью строку, которая имеет тип int[101]
, который является массивом. И этот массив по мере того, как он используется в выражении, в свою очередь преобразуется в указатель на его первый элемент типа int *
.
Это тот же начальный адрес области памяти, которую занимает третья строка.
Только выражение ( a + 2 )
имеет тип int ( * )[101]
, а выражение *( a + 2 )
имеет тип int *
. Но оба дают одно и то же значение - начальный адрес области памяти, занимаемой третьей строкой массива a
.
Первый элемент массива находится в том же месте, что и сам массив — в массиве нет «пустого места».
В cout << a + 2
a
неявно преобразуется в указатель на свой первый элемент, &a[0]
, а a + 2
является расположением третьего элемента a
, &a[2]
.
В cout << *(a + 2)
массив *(a + 2)
, то есть a[2]
, преобразуется в указатель на его первый элемент, &a[2][0]
.
Поскольку расположение третьего элемента a
и расположение первого элемента третьего элемента a
совпадают, вывод будет таким же.
int a[2][2]
. Поместите в него четыре значения, скажем, 1, 2, 3 и 4. Получите указатель на первый элемент,int *p=&a[0][0];
. Затем посмотрите с помощью отладчика, на что указываетp
, и ответьте на свой вопрос. Какая сделка!! - person Sam Varshavchik   schedule 05.07.2016