Ответ 1
Помнить имя массива можно легко разложить на указатель на первый элемент в большинстве выражений (читайте некоторые исключения, в которых имя массива не затухает в указатель на первый элемент? @H 2 CO 3).
Для лучшего понимания рассмотрим диаграммы:
Сначала предположим, что a
хранится в памяти следующим образом.
a
+----+----+----+----+---+
| 0 | 1 | 2 | 3 | 4 |
+----+----+----+----+---+
▲ ▲ ▲ ▲ ▲
| | | | |
a a+1 a+2 a+3 a+3
Объявление static int *p[] = {a, a+1, a+2, a+3, a+4};
создает новый массив указателей на целое число со следующими значениями:
p[0] == a
p[1] == a + 1
p[2] == a + 2
p[3] == a + 3
p[4] == a + 4
Теперь p
также можно считать сохраненным в памяти следующим образом:
p
+----+----+----+----+-----+
| a |a +1| a+2| a+3| a+4 |
+----+----+----+----+-----+
▲ ▲ ▲ ▲ ▲
| | | | |
p p+1 p+2 p+3 p+4
После назначения ptr = p;
все будет примерно так:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a |a +1| a+2| a+3| a+4 | | 0 | 1 | 2 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: ptr points to first location in pointer array p[]
Выражение: ** ptr ++;
Теперь рассмотрим выражение **ptr++;
перед первым выражением printf.
-
ptr
равенp
, который является адресом первого элемента в массиве указателей. Следовательно,ptr
указывает на первый элементp[0]
в массиве (или мы можем сказатьptr
==&p[0]
). -
*ptr
означаетp[0]
и посколькуp[0]
равенa
, поэтому*ptr
являетсяa
(so*ptr
==a
). -
И поскольку
*ptr
естьa
, то**ptr
есть*a
==*(a + 0)
==a[0]
, который0
. -
Примечание в выражении
**ptr++;
, мы не присваиваем его значение какой-либо переменной lhs.
Таким образом, эффект**ptr++;
просто такой же, какptr++;
==ptr = ptr + 1
=p + 1
Таким образом, после этого выраженияptr
, указывающего наp[1]
(или мы можем сказатьptr
==&p[1]
).
Print-1:
Перед первым началом работы printf:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a | a+1| a+2| a+3| a+4 | | 0 | 1 | 2 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: ptr is equals to p + 1 that means it points to p[1]
Теперь мы можем понять Первый printf:
-
ptr - p
вывод1
, потому что:ptr = p + 1
, поэтомуptr - p
==p + 1 - p
==1
-
*ptr - a
вывод1
, потому что:ptr = p + 1
, поэтому*ptr
==*(p + 1)
==p[1]
==a + 1
Это означает:*ptr - a
=a + 1 - a
==1
-
**ptr
вывод1
, потому что:
*ptr
==a + 1
from point-2
Итак**ptr
==*(a + 1)
==a[1]
==1
Выражение: * ++ * ptr;
После первого printf мы имеем выражение *++*ptr;
.
Как известно из вышеприведенной точки 2, *ptr
== p[1]
.
Таким образом, ++*ptr
(то есть ++p[1]
) будет увеличивать p[1]
до a + 2
Снова понять, в выражении *++*ptr;
мы не присваиваем его значение какой-либо переменной lhs, поэтому эффект *++*ptr;
равен ++*ptr;
.
Теперь, перед тем как сделать второй вывод printf, выполните следующие действия:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a |a+2 | a+2| a+3| a+4 | | 0 | 1 | 2 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: p[1] became a + 2
Print-2:
Теперь мы можем понять Второй printf:
-
ptr - p
вывод1
, потому что:ptr = p + 1
, поэтомуptr - p
==p + 1 - p
==1
-
*ptr - a
вывод2
, потому что:ptr = p + 1
so*ptr
==*(p + 1)
==p[1]
==a + 2
Это означает:*ptr - a
==a + 2 - a
==2
-
**ptr
вывод2
, потому что:
*ptr
==a + 2
from point-2
Итак**ptr
==*(a + 2)
==a[2]
==2
Выражение: ++ ** ptr;
Теперь выражение ++**ptr;
перед третьим printf.
Как мы знаем выше, пункт 3, что **ptr
== a[2]
.
Таким образом, ++**ptr
== ++a[2]
будет увеличивать a[2]
до 3
Итак, до того, как третье значение printf станет:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a | a+2| a+2| a+3| a+4 | | 0 | 1 | 3 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: a[2] = 3
Print-3:
Теперь мы можем понять Третий printf:
-
ptr - p
вывод1
, потому что:ptr = p + 1
soptr - p
==p + 1 - p
==1
-
*ptr - a
вывод2
, потому что:ptr = p + 1
so*ptr
==*(p + 1)
==p[1]
==a + 2
Это означает:*ptr - a
=a + 2 - a
==2
-
**ptr
выводит3
, потому что:*ptr
==a + 2
from point-2
Итак**ptr
==*(a + 2)
==a[2]
==3
Изменить Примечание. Разница двух указателей имеет тип ptrdiff_t
, и для этого правильный спецификатор преобразования %td
, а не %d
.
Дополнительная точка:
Я хочу добавить, так как считаю, что это будет полезно для новых учеников
Предположим, что у вас есть две строки с еще одним 4 th printf в коде перед return 0;
**++ptr; // additional
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr); // fourth printf
Можно проверить этот рабочий код @Codepade, эта строка выводит 2 2 3
.
Выражение: ** ++ ptr;
Поскольку ptr
равно p + 1
, после приращения ++
операция ptr
становится p + 2
(или мы можем сказать ptr
== &p[2]
).
После этого операция двойного отсрочки **
== > **(p + 2)
== *p[2]
== *(a + 2)
== a[2]
== 3
.
Теперь, опять же, потому что у нас нет операции присваивания в этом выражении, эффект выражения **++ptr;
- это просто ++ptr;
.
Итак, вещь после выражения **++ptr;
становится как показано ниже на рисунке:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a | a+2| a+2| a+3| a+4 | | 0 | 1 | 3 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: ptr is equals to p + 2 that means it points to p[2]
Print-4:
Учитывая Forth printf, я добавил следующее:
-
ptr - p
вывод2
, потому что:ptr = p + 2
soptr - p
==p + 2 - p
==2
-
*ptr - a
вывод2
, потому что:ptr = p + 2
so*ptr
==*(p + 2)
==p[2]
==a + 2
Это означает:*ptr - a
=a + 2 - a
==2
-
**ptr
выводит3
, потому что:*ptr
==a + 2
сверху точки-2
Итак**ptr
==*(a + 2)
==a[2]
==3