Apéndice 6.1 Reglas de lectura
Nota: Este apéndice no debe ser considerado tema de estudio
en una primera lectura. La notación y sintaxis de las entidades C++ son
contempladas en los epígrafes correspondientes. Aquí se incluye
simplemente una recopilación de casos relativos a matrices,
funciones y punteros (los más propensos a una notación complicada), que
puede utilizarse una vez que se ha realizado una primera aproximación al lenguaje.
§1 Sinopsis
Lanotación de C/C++ tiende a ser algo confusa, sobre todo para el principiante. Algunas veces las expresiones son realmente difíciles de interpretar. Por ejemplo:
void (_USERENTRY *signal(int sig, void (_USERENTRY *func) (int sig[, int subcode])))(int);
verdaderamente es una expresión para nota; representa el prototipo de la función signal, definida en <signal.h> de la Librería Estándar, la analizaremos más adelante; por ahora, seguiremos con alguna más sencilla, por ejemplo:
int (* afpt[10])(char);
declara que afpt es una matriz de diez punteros a función que reciben un char y devuelven int. Por su parte:
int* aptr[10];
declara aptr como matriz de diez punteros a entero, mientras que:
int (*ptai)[10]
declara ptai como puntero a matriz de diez enteros. Finalmente:
int (*(*(*fptr)(char))[5])();
declara que fpta es un puntero a función que recibe un char y devuelve un puntero a matriz de 5 punteros a función que no reciben ningún parámetro y devuelven int.
Como regla general, en este tipo de expresiones es necesario buscar el
identificador, a partir de este punto, moverse sucesivamente a derecha e
izquierda, deteniéndose en los posibles paréntesis, y teniendo en cuenta que:
() significa función, eventualmente incluyendo tipo de parámetros. "función recibiendo ..."
* significa puntero "puntero a:"
[ ] significa matriz, eventualmente incluyendo la dimensión: "matriz de ..."
§2 Ejemplos
Para ilustrarlo con unos ejemplos efectuemos el análisis de algunas expresiones mostrando paso a paso el proceso seguido:
Expresión: (* name[])()
identificador
: → name
name[]
→ name: matriz
* name[]
→ name: matriz de punteros
(* name[])()
→ name: matriz de punteros a función
Expresión:
int (* name)[10]
identificador: → name
* name
→ name: puntero a
(* name)[10]
→ name: puntero a matriz de 10
int (*name)[10]
→ name: puntero a matriz de 10 enteros
Expresión:
int * name[10]
identificador: → name
name[10]
→ name: matriz de 10
* name10]
→ name: matriz de 10 punteros
int * name[10]
→ name: matriz de 10 punteros a entero
Expresión:
int name(char)
identificador: → name
name(char)
→ name: función recibiendo char
int name(char)
→ name: función recibiendo char y devolviendo entero
Expresión:
int *name(char)
identificador: → name
name(char)
→ name: función recibiendo char
* name(char)
→ name: función recibiendo char devolviendo puntero a
int *name(char)
→ name: función recibiendo char devolviendo puntero a int
Expresión:
int (* name)(char)
identificador: → name
* name
→ name: puntero a
(* name)(char)
→ name: puntero a función recibiendo char
int (*name)(char)
→ name: puntero a función recibiendo char devolviendo int
Expresión:
int (*(*name())[2])()
identificador: → name
name()
→ name: función (no recibe argumentos)
(*name())
→
name: función devolviendo puntero a
(*name())[2]
→ name: función devolviendo puntero a matriz de dos...
(*(*name())[2])
→ name: función devolviendo puntero a matriz de dos punteros a...
(*(*name())[2])()
→
name: función devolviendo puntero a matriz de dos punteros a función...
int (*(*name())[2])()
→ name: función devolviendo puntero a matriz de dos punteros a
función que devuelven int
Expresión:
int (*(*name())[])(char)
identificador: → name
name()
→ name: función (no recibe argumentos)
(*name())
→
name: función devolviendo puntero a
(*name())[]
→ name: función devolviendo puntero a
matriz (no se indica dimensión)
*(*name())[]
→ name: función devolviendo puntero a matriz de
punteros a
(*(*name())[])(char)
→ name: función devolviendo puntero a matriz de punteros a función recibiendo char
int (*(*name())[])(char)
→ name: función devolviendo puntero a matriz de punteros a función recibiendo char
devolviendo int.
Expresión:
int (*(*name[2])())[3]
identificador: → name
name[2]
→ name: matriz de dos
(*name[2])
→ name: matriz de dos punteros a
(*name[2])()
→ name: matriz de
dos punteros a función (no recibe argumentos)
*(*name[2])()
→ name: matriz de dos punteros a función devolviendo puntero a
(*(*name[2])())[3]
→ name: matriz de dos punteros a función devolviendo puntero a matriz de tres
int (*(*name[2])())[3]
→ name: matriz de dos punteros a función devolviendo puntero a matriz de tres
enteros.
Expresión: void* (*(*name)(int))[5]
identificador: → name
*name
→ name: puntero
(*name)(int)
→ name: puntero a función que recibe un entero
*(*name)(int)
→ name: puntero a función que recibe un entero y devuelve un puntero...
(*(*name))[5]
→ name: puntero a
función que recibe un entero y devuelve un puntero a matriz de 5 elementos...
void* (*(*name))[5]
→ name: puntero a
función que recibe un entero y devuelve un puntero a matriz de 5 punteros a void
Expresión: void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
Se trata de un caso real, el prototipo de la función qsort de la Librería Standar. Para simplificar dejamos la expresión en su estructura, olvidándonos de los detalles de nombres:
void qsort(void *, size_t, size_t, int (*fcmp)(const void *, const void *));
identificador: → qsort
qsort(,,,
→ qsort: función recibiendo cuatro parámetros...
void qsort(,,,)
→ qsort: función recibiendo cuatro parámetros y no devolviendo nada.
podemos ahora concentrarnos en los parámetros:
void *
→ primer argumento: puntero de tipo genérico.
size_t
→ segundo y tercero:
variables
de tipo size_t. Es el tipo estándar C para los tamaños, por ejemplo es
el tipo que hay que pasar a malloc(), en nuestro caso está definido como
unsigned (int).
int (*fcmp)(const void *, const void *)
→ cuarto parámetro: procedemos a analizarlo separadamente:
identificador → fcmp (del parámetro)
*fcm
→ fcmp: puntero
(*fcmp)(..,..)
→ fcmp: puntero a
función recibiendo dos argumentos tipo puntero genérico a constante.
int (*fcmp)(..,..)
→ fcmp: puntero a función recibiendo... y devolviendo un entero.
Expresión:
void (_USERENTRY *name)(int)
identificador: → name
*name → name: puntero a
*name)(int)
→ name: puntero a función recibiendo un int
(_USERENTRY *name)(int)
→ name: puntero a función recibiendo un int, debe usarse la convención de
llamada _USERENTRY
void (_USERENTRY *name)(int)
→ name: puntero a
función recibiendo un int según la convención de llamada _USERENTRY y que no devuelve nada.
Expresión: void
(_USERENTRY *signal(int sig, void (_USERENTRY *func) (int sig[, int subcode])))(int);
Para simplificar dejaremos la expresión en su estructura e iremos añadiendo detalles:
void (*signal(int, void (*func)(int)))(int);
identificador: → signal
signal(...)
→ signal: función
signal(int, void (*func)(int)))
→ signal: función recibiendo dos parámetros, un int y ...