В моей книге по программам на языке 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] для указания массива.
p=&a[0];p<&a[num1]... — это необычный способ сказать p=a,end=p+num1;p<end....
Взгляните на определение p. Тогда вы поймете, почему p++ ведет себя именно так.





На самом деле 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;
сначала скобки, как в математике. Если вы проверите таблицу приоритета операторов, оператор
[]предшествует унарному*