que - punteros a cadenas
Puntero C a matriz/matriz de punteros desambiguaciĆ³n (12)
¿Cuál es la diferencia entre las siguientes declaraciones?
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
¿Cuál es la regla general para entender declaraciones más complejas?
Aquí hay un sitio web interesante que explica cómo leer tipos complejos en C: http://www.unixwiz.net/techtips/reading-cdecl.html
Así es como lo interpreto:
int *something[n];
nota sobre la precedencia: el operador del subíndice de matriz (''[]'') tiene una prioridad más alta que el operador de referencia (''*'').
Entonces, aquí aplicaremos el ''[]'' antes de ''*'', haciendo que la declaración sea equivalente a:
int *(something[i]);
note cómo tiene sentido una declaración:
int num
significa (num) es un (int),int *ptr
oint (*ptr)
significa, (valor en ptr) es un (int), lo que hace que ptr sea un puntero a int.
Esto se puede leer como, (el valor de (el valor en el índice i del algo)) es un número entero. Entonces, (el valor en el índice i de algo) es un (puntero de entero), lo que hace que el algo sea una matriz de punteros de números enteros.
En el segundo,
int (*something)[n];
Para dar sentido a esta declaración, debe estar familiarizado con este hecho:
nota sobre la representación de puntero de la matriz: somethingElse [i] es equivalente a * (somethingElse + i)
Entonces, reemplazando somethingElse con (* algo), obtenemos * (* something + i), que es un número entero según la declaración. Entonces, (* algo) nos dio una matriz, lo que hace que algo sea equivalente a (puntero a una matriz).
Como regla general, los operadores unarios de derecha (como []
, ()
, etc.) tienen preferencia sobre los de izquierda. Entonces, int *(*ptr)()[];
sería un puntero que apunta a una función que devuelve una serie de punteros a int (obtenga los operadores correctos tan pronto como pueda al salir del paréntesis)
Creo que podemos usar la regla simple ..
example int * (*ptr)()[];
start from ptr
" ptr
es un puntero a" ir hacia la derecha ... es ")" ahora ve a la izquierda es un "(" salir a la derecha "()" así que "a una función que no tiene argumentos" ir a la izquierda "y devuelve un puntero" ir a la derecha "a una matriz" ir a la izquierda "de números enteros"
En el puntero a un entero, si el puntero se incrementa, pasa al siguiente entero.
en la matriz de puntero, si el puntero se incrementa, salta a la siguiente matriz
La respuesta para los dos últimos también se puede deducir de la regla de oro en C:
Declaración sigue uso.
int (*arr2)[8];
¿Qué pasa si no haces referencia a arr2? Obtienes una matriz de 8 enteros.
int *(arr3[8]);
¿Qué pasa si tomas un elemento de arr3? Obtienes un puntero a un entero.
Esto también ayuda cuando se trata de punteros a funciones. Para tomar el ejemplo de sigjuice:
float *(*x)(void )
¿Qué pasa cuando desapruebas x? Obtienes una función a la que puedes llamar sin argumentos. ¿Qué pasa cuando lo llamas? Devolverá un puntero a un flotador.
Sin embargo, la precedencia del operador siempre es difícil. Sin embargo, usar paréntesis también puede ser confuso porque la declaración sigue al uso. Al menos, para mí, intuitivamente arr2 parece una serie de 8 punteros a ints, pero en realidad es al revés. Sólo toma un poco de tiempo para acostumbrarse. Razón suficiente para agregar siempre un comentario a estas declaraciones, si me preguntas :)
editar: ejemplo
Por cierto, me topé con la siguiente situación: una función que tiene una matriz estática y que usa aritmética de punteros para ver si el puntero de fila está fuera de límites. Ejemplo:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))
int *
put_off(const int newrow[2])
{
static int mymatrix[3][2];
static int (*rowp)[2] = mymatrix;
int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);
memcpy(rowp, newrow, sizeof(*rowp));
rowp += 1;
if (rowp == border) {
rowp = mymatrix;
}
return *rowp;
}
int
main(int argc, char *argv[])
{
int i = 0;
int row[2] = {0, 1};
int *rout;
for (i = 0; i < 6; i++) {
row[0] = i;
row[1] += i;
rout = put_off(row);
printf("%d (%p): [%d, %d]/n", i, (void *) rout, rout[0], rout[1]);
}
return 0;
}
Salida:
0 (0x804a02c): [0, 0] 1 (0x804a034): [0, 0] 2 (0x804a024): [0, 1] 3 (0x804a02c): [1, 2] 4 (0x804a034): [2, 4] 5 (0x804a024): [3, 7]
Tenga en cuenta que el valor del borde nunca cambia, por lo que el compilador puede optimizarlo. Esto es diferente de lo que inicialmente desearía usar: const int (*border)[3]
: declara el borde como un puntero a una matriz de 3 enteros que no cambiarán de valor mientras exista la variable. Sin embargo, ese puntero puede apuntarse a cualquier otra matriz en cualquier momento. En cambio, queremos ese tipo de comportamiento para el argumento (porque esta función no cambia ninguno de esos enteros). Declaración sigue uso.
(ps: no dude en mejorar esta muestra!)
No sé si tiene un nombre oficial, pero lo llamo Right-Left Thingy (TM).
Comienza en la variable, luego ve a la derecha, a la izquierda, a la derecha ... y así sucesivamente.
int* arr1[8];
arr1 es una matriz de 8 punteros a enteros.
int (*arr2)[8];
arr2 es un puntero (el bloque de paréntesis de derecha-izquierda) a una matriz de 8 enteros.
int *(arr3[8]);
arr3 es una matriz de 8 punteros a enteros.
Esto debería ayudarte con declaraciones complejas.
Supongo que la segunda declaración es confusa para muchos. Aquí hay una manera fácil de entenderlo.
Permite tener una matriz de enteros, es decir, int B[8]
.
También tengamos una variable A que apunta a B. Ahora, el valor en A es B, es decir (*A) == B
Por lo tanto, A apunta a una matriz de enteros. En tu pregunta, arr es similar a A.
De manera similar, en int* (*C) [8]
, C es un puntero a una matriz de punteros a entero.
Use el programa cdecl , como lo sugiere K&R.
$ cdecl
Type `help'' or `?'' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>
También trabaja de la otra manera.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
int *a[4]; // Array of 4 pointers to int
int (*a)[4]; //a is a pointer to an integer array of size 4
int (*a[8])[5]; //a is an array of pointers to integer array of size 5
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
El tercero es el mismo que el primero.
La regla general es la unixwiz.net/techtips/reading-cdecl.html . Puede ser mucho más complejo a medida que los punteros de función entran en escena.
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];