Что влечет за собой объявление и инициализация указателя следующим образом: int (* p) [n] - PullRequest
0 голосов
/ 11 декабря 2019

Я изучаю арифметику указателей, и есть пример, как точно обрабатывать столбцы двумерного массива, используя указатели. Чтобы сделать это, код показывает объявление указателя new-to-me, вот фрагмент:

int a[NUM_ROWS][NUM_COLS], (*p) [NUM_COLS], i;
...
for (p = &a[0]; p < &a[NUM_ROWS]; p++)
    (*p)[i] = 0;

Поведение этого указателя ( (* p) [NUM_COLS] ) странно для меня, так как я только что узнал, что увеличение указателя действует как увеличение нижнего индекса массива, позволяющего вам обрабатывать массив, на который указывает этот указатель. Значит, это не обычный указатель?

Размер (в байтах) одинаков, но что входит в это объявление, учитывая, что оно представляет всю строку, пока назначается в для цикла ?

Ответы [ 2 ]

2 голосов
/ 11 декабря 2019

Синтаксис, подобный

 int (*p)[NUM_COLS];

, по существу определяет указатель p, который является указателем на массив int s с элементом NUM_COLS.

Таким образом, он можетиспользоваться как

 int arr[NUM_COLS] = {0};
 p = &arr;

. Имеет те же свойства, что и все другие указатели,

  • тип указателя: int (*)[NUM_COLS]
  • размерэкземпляр, на который указывает этот указатель, равен int [NUM_COLS], т. е. размер int, умноженный на NUM_COLS.
0 голосов
/ 12 декабря 2019
T (*p)[N];

объявляет p как указатель на массив из N элементов с T. В основном вы видите их в следующих контекстах:

  • В результате выражения массива более высокого размера "затухает" в выражении указателя;
  • При выделении памяти для блока более высокого размерамассив;

В качестве примера первого рассмотрим следующий код:

void foo( int (*p)[3] )
{
  // do something with p
}

int main( void )
{
  int arr[2][3];

  foo( arr );
}

За исключением случаев, когда он является операндом операторов sizeof или унарных &,или является строковым литералом, используемым для инициализации массива символов в объявлении, выражение типа «массив N-элементов из T» будет преобразовано («распад») в выражение указателя типа «указатель»в T ", а значением выражения будет адрес первого элемента массива.

Когда вы передаете выражение arr в качестве параметра в foo он преобразуется из типа «2-элементный массив из 3-элементного массива int» (int [2][3]) в «указатель на 3-элементный массив из int» (int (*)[3]). Таким образом, функция фактически получает значение указателя, а не массив.

Поскольку указатель на синтаксис массива немного уродлив и громоздок, вы можете поочередно использовать

void foo( int p[][3] ) { ... }

для объявления, но только в объявлении параметра функции.

В качестве примера последнего, если вы хотите динамически объявить 2D-массив, вы можете сделать это:

int (*p)[3] = malloc( 2 * sizeof *p );

, который выделяет пространство для массива 2x3 int и назначаетуказатель на это пространство на p. Типом выражения p является «указатель на массив из 3 элементов int», поэтому из *p следует указать «массив из 3 элементов int».

Помните, что операция индексации массива a[i] определяется как *(a + i) - с учетом начального адреса a, смещений i элементов ( не байтов ) из этого адреса и разыменованиярезультат 1 . Следовательно, из этого следует, что

*p == *(p + 0) == p[0]

означает

(*p)[i] == (*(p + 0))[i] == (p[0])[i] == p[0][i]

Таким образом, после того, как вы выделите память для p, вы можете индексировать ее как любой двумерный массив:

for ( size_t i = 0; i < 2; i++ )
  for ( size_t j = 0; j < 3; j++ )
    p[i][j] = some_value;

Общие правила:

T *a[N];    // a is an N-element array of pointer to T
T (*a)[N];  // a is a pointer to an N-element array of T
T *f();     // f is a function returning a pointer to T
T (*f)();   // f is a pointer to a function returning T

Поскольку операторы нижнего индекса [] и вызова функции () имеют более высокий приоритет, чем унарные *, *a[i] будет обрабатываться как *(a[i])и *f() будет проанализирован как *(f()). Если вы хотите трактовать a как указатель на массив и f как указатель на функцию, вы должны явно сгруппировать оператор * с идентификатором, используя скобки - (*a)[i] и (*f)().


C был получен из более раннего языка, называемого B. В B массивы имели явный указатель на первый элемент, и этот указатель был привязан кпеременная массива. Итак, в B синтаксис *(a + i) имел смысл, потому что a всегда был указателем. Когда Ричи разрабатывал C, он не хотел сохранять этот явный указатель в массиве, поэтому вместо этого он ввел правило, согласно которому выражения в массиве «затухают» для указателей, за исключением особых случаев.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...