Выражения указателя: ** ptr ++, * ++ * ptr и ++ ** ptr use

Я пытаюсь использовать литературу C-указателя. На одной из иллюстраций я обнаружил следующий код.

# include <stdio.h>

int main()
{
     static int a[]={0,1,2,3,4};
     static int *p[]={a, a+1, a+2, a+3, a+4};

     int **ptr;

     ptr =p;

     **ptr++;
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     *++*ptr; 
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     ++**ptr;
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     return 0;
}

Я получаю вывод как.

1 1 1
1 2 2
1 2 3

У меня возникает проблема в оправдании этого вывода. Я сделал много коробок на копии, чтобы легко понять проблему. Я могу оправдать вывод 1 1 1, моя проблема начинается с утверждения, *++*ptr.

Так как унарные операторы выполняются справа налево. Таким образом, сначала будет решаться *ptr, тогда значение в ptr будет увеличиваться. После этого прироста я не уверен, что происходит, в книге говорится, что как-то p также увеличивается, чтобы указывать на следующий элемент в этом массиве. Выход 1 2 2 может быть достигнут только с помощью приращения p.

Я не уверен, что этот вопрос подходит именно к stackoverflow.
Я старался изо всех сил, растратил по крайней мере 10 страниц с надписями над ними.

Любые разъяснения будут оценены.

Ответ 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 so 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 выводит 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 so ptr - 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

Ответ 2

Если вы компилируете с некоторыми предупреждениями (clang даже не требовал каких-либо флагов), вы увидите, что ваша программа имеет три посторонних оператора *. Упрощение ваших сумасшедших выражений дает:

ptr++;
++*ptr;
++**ptr;

И из этого вы сможете увидеть, что происходит довольно четко:

  • ptr++ просто увеличивает ptr, поэтому он указывает на второй элемент p. После этой операции ptr - p всегда будет 1.

  • ++*ptr увеличивает значение, на которое указывает ptr. Это изменяет второй элемент p, чтобы указать на третий элемент a, а не на второй (который был инициализирован). Это делает *ptr - a равным 2. Аналогично **ptr является 2 из a.

  • ++**ptr увеличивает то, на что указывает то, на что указывает ptr. Это увеличивает третий элемент a, делая его 3.

Ответ 3

Помните, что ++ имеет более высокий приоритет, чем *, поэтому, когда вы выполняете **ptr++, это увеличивает указатель и двойные разметки старое значение, которое будет не более чем сбой, если оно не является допустимым указателем- to-pointer, и ваш компилятор (по крайней мере с включенными предупреждениями) должен предупредить вас о неиспользованном результате.

 static int a[]={0,1,2,3,4};

 static int *p[]={a, a+1, a+2, a+3, a+4};

 int **ptr;

 ptr = p; // ptr = &(p[0]); *ptr = a; **ptr = 0.

 **ptr++; // ptr = &(p[1]); *ptr = a+1; **ptr = 1
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

 *++*ptr; // ptr = &(p[1]); *ptr = a+2; **ptr = 2; p = {a, a+2, a+2, a+3, a+4} 
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

 ++**ptr; // ptr = &(p[1]); *ptr = a+2; **ptr = 3; a = {0, 1, 3, 3, 4}
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

Ответ 4

Значение int* по адресу ptr было увеличено с помощью оператора *++*ptr; (фактически с помощью части ++*ptr ведущий * является неиспользуемым разыменованием). Таким образом, расширение int *p[] должно выглядеть следующим образом:

int *p[]={a, a+2, a+2, a+3, a+4};

Финал ++**ptr; теперь увеличил значение по адресу a+2, поэтому исходный массив теперь будет выглядеть следующим образом:

int a[]={0,1,3,3,4};