Разница между *p[num] и (*p)num

В моей книге по программам на языке C я встречаю вопрос о (*p)[num2] существует двумерный массив с именем a[num1][num2] и (*p)[num2] далее в заявлении

for(p=&a[0];p<&a[num1];p++)
(*p)[i]=0;

этот оператор присвоит столбцу i двухмерного массива значение 0 но я не понимаю, почему (*p)[num2] указывает на строку a, я думаю, создайте *p и p=&a[num1] тоже может это сделать. и почему p++ указывает на следующую строку, а не на следующее значение a[num][i]

почему?, я понял разницу между *p[3] и (*p)[3] Я думаю, *p может указывать на массив (первый адрес), поэтому используется (*p)[num] для указания массива.

сначала скобки, как в математике. Если вы проверите таблицу приоритета операторов, оператор [] предшествует унарному *

Swift - Friday Pie 11.03.2024 08:26
p=&a[0];p<&a[num1]... — это необычный способ сказать p=a,end=p+num1;p<end....
Neil 11.03.2024 08:33

Взгляните на определение p. Тогда вы поймете, почему p++ ведет себя именно так.

Support Ukraine 11.03.2024 08:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
150
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

На самом деле p указывает не только на двумерный массив a, но и на одномерный массив a[0], a[1] и т. д. Итак, p++ указывает на первую позицию следующей строки a.

Определения означают

TYPE a[num1][num2];  // Array of array of TYPE, i.e.
                     // an array with num1 elements where each element
                     // is an array with num2 elements of type TYPE

TYPE (*p)[num2];     // Pointer to array of TYPE, i.e.
                     // a pointer to an array with num2 elements of type TYPE

так

p=&a[0];

даст вам указатель на первый «внутренний» массив a. В 2D это часто называют «первым рядом».

Затем

p++;

даст вам указатель на второй «внутренний» массив a. Другими словами, указатель на вторую строку. Таким образом, увеличение p создаст p указатель на следующую строку.

Разыменование p, т. е. *p, даст вам массив (т. е. строку), на который в данный момент указывает p. В большинстве ситуаций массив преобразуется в указатель на первый элемент, то есть указатель типа TYPE. Как будто вы это сделали:

TYPE* row = *p;

Теперь вы можете получить доступ к элементам строки, используя row[i].

Нравиться

TYPE* row = *p;
TYPE value = row[i];

В качестве отдельного утверждения это то же самое, что (*p)[i].

Круглые скобки необходимы из-за приоритета оператора .

Без круглых скобок типа *p[i] выражение будет выглядеть как *(p[i]), что скорее означает: получить указатель на i-ю строку и прочитать первый элемент этой строки.

Пример:

  int a[2][2] = {{1, 2}, {3, 4}};
  int (*p)[2] = &a[0];
  
  // Print all elements in the row p point to (here row 0)
  for (int i=0; i < 2; i++) {
    printf("%d ", (*p)[i]);
  }

  puts("\n-----------------------");

  // Print the first element of all rows
  for (int i=0; i < 2; i++) {
    printf("%d ", *p[i]);
  }

Выход:

1 2 
-----------------------
1 3 
Ответ принят как подходящий

Давайте начнем с полезной цитаты из стандарта C относительно массивов и неявных преобразований (6.3.2.1 L-значения, массивы и обозначения функций)"

3 За исключением случаев, когда это операнд оператора sizeof или унарный & оператор или строковый литерал, используемый для инициализации массива, выражение типа «массив типа» преобразуется в выражение с типом «указатель на тип», который указывает на начальный элемент объект массива и не является lvalue. Если объект массива имеет регистр класс хранения, поведение не определено.

Итак, если, например, у вас есть такой массив

int a[num1][num2];

затем используемый в выражениях, за редкими исключениями, упомянутыми в кавычках, он неявно преобразуется в указатель на его начальный элемент.

Элементы двумерного массива a, в свою очередь, являются массивами типа int[num2]. Указатель на тип элемента массива имеет тип int ( * )[num2]. Вы можете объявить соответствующий указатель следующим образом

int ( *p )[num2] = a;

это то же самое, что

int ( *p )[num2] = &a[0];

Теперь указатель p указывает на первую «строку» двумерного массива a. После его увеличения с помощью префиксного оператора приращения ++p или постфиксного оператора приращения p++ он будет указывать на следующий (второй) элемент (строку) типа int[num2] массива a. Разыменовав указатель типа *p, вы получите одномерный массив (строку) типа int[num2], на который указывает указатель. А если применить к полученному выражению оператор индекса, например ( *p )[i], вы получите элемент i-th текущей строки массива a.

С другой стороны, если у вас есть

int ( *p )[num2] = a;

тогда выражение p + num1 будет указывать на память после последнего элемента (одномерного массива типа int[num2]) массива a. Выражение p + num1 эквивалентно выражению a + num1 (из-за неявного преобразования массива a в указатель на его начальный элемент), которое, в свою очередь, эквивалентно выражению &a[num1]. Согласно последнему выражению написано в стандарте C (6.5.3.2 Операторы адреса и косвенности)

3 Унарный оператор & возвращает адрес своего операнда. Если операнд имеет тип «тип», результат имеет тип «указатель на тип». Если операнд является результатом унарного оператора *, ни этот оператор, ни оператор & оценивается, и результат такой, как если бы оба были опущены, за исключением того, что ограничения на операторы все еще применяются и результат не является lvalue. Аналогично, если операнд является результатом оператор [], ни оператор &, ни унарный *, который подразумевается с помощью [] оценивается, и результат такой, как если бы оператор & был удален, а оператор [] заменен на оператор +. В противном случае, результатом является указатель на объект или функцию, обозначенную его операнд.

Итак, как следует из цитаты, выражение &a[num1] эквивалентно выражению a + num1.

Таким образом, это цикл for

for ( p = &a[0]; p < &a[num1]; p++ )
    ( *p )[i] = 0;

может быть переписано как

for ( p = a; p < a + num1; p++ )
    ( *p )[i] = 0;

Что касается оператора индекса, то (стандарт C, 6.5.2.1 индексация массива):

2 Постфиксное выражение, за которым следует выражение в квадратных скобках [] — это индексное обозначение элемента объекта массива. определение оператора индекса [] заключается в том, что E1[E2] идентичен до (*((E1)+(E2))). Из-за правил преобразования, применимых к двоичный+ оператор, если E1 является объектом массива (эквивалентно указателем на начальный элемент объекта массива), а E2 — целое число, E1[E2] обозначает E2-й элемент E1 (считая с нуля).

Таким образом, этот оператор выражения в теле цикла for

( *p )[i] = 0;

также может быть переписано как

*( *( p + 0 ) + i ) = 0;

или нравится

*( *p + i ) = 0;

Другие вопросы по теме