Declaración de puntero C para apuntar a una fila de matriz 2-d

Me encontré con esta statement en el libro de KN King en la página 269

int a[ROWS][COLS], (*p)[COLS]; p = &a[0]; 

p ahora apunta a la primera fila de la matriz 2-d. Entiendo por qué a[0] apunta a la primera fila de la matriz 2-d. Pero no entiendo la syntax para declarar p . ¿Qué significa eso y cómo lo recuerdo?

¿Qué están haciendo los parens alrededor de *p ? (*p) ¿qué significa esta syntax en términos de precedencia del operador?

> "But I do not understand the syntax for declaring p"

Entonces p se declara como:

 int (*p)[COLS]; 

Es un puntero a una matriz de int s que es de tamaño COLS .

> "What does that mean and how do I remember it?"

A continuación le indicamos cómo puede saber, use la regla de la espiral y comience por trabajar en () s:

  ( p) p (*p) p is a pointer (*p)[ ] p is a pointer to an array int (*p)[ ] p is a pointer to an array of ints int (*p)[COLS] p is a pointer to an array of ints of size COLS 

Por supuesto, siempre puedes hacer trampa para obtener la respuesta también :

introduzca la descripción de la imagen aquí

> "what does this syntax mean in terms of operator precedence?"

En el lenguaje C, [] tiene prioridad sobre el unario * , eso significa que necesita () para que p sea ​​un puntero a una matriz de int s, en lugar de una matriz de punteros a int s.

Los operadores postfix [] y () tienen mayor prioridad que el operador unario * , por lo que se unen primero. IOW, T *p[N] se interpreta como T *(p[N]) ; p es una matriz de punteros a T . Para declarar un puntero a una matriz (o puntero a una función), debe usar paréntesis para forzar al operador * a enlazar antes de [] :

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

ignorar la coma y volver a escribir como:

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

p es un puntero a una matriz de ints COLS grande la statement no asigna tanta memoria, pero sí permite algunas comprobaciones de límites. La memoria para la matriz fue asignada en la statement de:
a => int a[ROWS][COLS];

Entiendo por qué un [0] apunta a la primera fila de la matriz 2-d

Esto ya es inexacto. En un contexto de valor, a[0] no apunta realmente “a la primera fila de la matriz 2D”. a[0] realidad apunta al primer elemento de la primera fila de la matriz 2D. En otras palabras, en el contexto de valor a[0] es un puntero a a[0][0] . El tipo de a[0] descompone en int * , como probablemente sepa. Y sizeof *a[0] es igual a sizeof(int) . Entonces, realmente no apunta a toda la fila. Apunta a un objeto int solitario.

Ahora, si realmente desea apuntar a la primera fila de una matriz 2D, es decir, apuntar a toda la fila, necesita &a[0] . Eso le dará un puntero de tipo int (*)[COLS] . Tenga en cuenta que sizeof *&a[0] es igual a sizeof (int[COLS]) , por lo que es realmente un puntero a la primera fila de una matriz 2D. Esto es lo que ves en tu ejemplo.

Tenga en cuenta que numéricamente a[0] y &a[0] (como punteros en el contexto de valor) son los mismos, ya que apuntan al mismo punto en la memoria lineal. Sin embargo, el tipo &a[0] apuntan a toda la fila, mientras que a[0] apunta a un solo elemento.