Regla 0x0012h: Validador de formato canónico en firmas de punteros a función
Sintaxis y formato visual (0x00XX)
0x0012h: Validador de formato canónico en firmas de punteros a función¶
Enunciado normativo¶
Las declaraciones y
typedefs de punteros a función DEBEN seguir la forma canónicatipo (*identificador)(parámetros): el asterisco unido al nombre entre paréntesis, los paréntesis de la lista de parámetros pegados al cierre del nombre y sin espacios sueltos alrededor del*.
¿Por qué existe esta regla?¶
El problema¶
El declarador de un puntero a función es el más engañoso de C. El paréntesis no
es decorativo: sin él, int *f(int, int) declara una función que devuelve
int *, no un puntero a función. Los espacios dispersos
(int (* f) (int, int), int ( *f )(int, int)) fragmentan el grupo
(*identificador) que el lector necesita reconocer de un golpe. La forma
canónica agrupa el asterisco con el nombre y deja pegado el paréntesis de
parámetros: se lee “puntero llamado X a función que toma ... y devuelve tipo”.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Compilación | Colocar mal el paréntesis cambia el tipo y produce called object is not a function o incompatibilidad de tipos. |
| Bug silencioso | int *f(int, int) y int (*f)(int, int) son declaraciones distintas con semántica opuesta. |
| Mantenibilidad | Un typedef ilegible se propaga por todo el proyecto y se copia mal. |
| Revisión | gaff marca el formato no canónico aunque el código compile. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §6.7.6.3 define los declaradores de función y §6.7.6.1 los de
puntero; la combinación de ambos produce la sintaxis de puntero a función. La
cátedra fija la forma canónica para que el typedef de un callback se lea igual
en todos los archivos. Se articula con 0x0005h: El asterisco de los punteros debe declararse junto al identificador (asterisco junto al
identificador) y con 0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t (sufijo _t para el alias).
Alcance y excepciones¶
Aplica a variables, parámetros, campos de struct y typedefs cuyo tipo sea
puntero a función. No aplica a punteros comunes a datos (0x0005h: El asterisco de los punteros debe declararse junto al identificador) ni
a punteros a puntero (0x0009h: Espaciado consistente en declaraciones de doble puntero (tipo **var)). No hay excepciones: también en prototipos
en línea se usa la forma canónica.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — typedef con espacios dispersos¶
typedef int (* operacion_t )(int, int);Por qué falla: hay un espacio entre ( y * y otro entre ) y (, de modo
que el grupo (*operacion_t) queda partido y el alias se lee como si el asterisco
y el nombre pertenecieran a frases distintas.
❌ Contraejemplo 2 — Variable y firma mal formateadas¶
int (* suma) (int, int);
int ( *resta )(int, int);Por qué falla: en el primer caso el nombre queda separado de su lista de parámetros; en el segundo el asterisco flota. Ambos estilos se propagan a las llamadas y confunden al lector sobre qué es el nombre.
✅ Ejemplo conforme 1 — typedef canónico¶
typedef int (*operacion_t)(int, int);Por qué cumple: el asterisco está pegado al nombre, el nombre entre paréntesis y
la lista de parámetros pegada; el alias respeta el sufijo _t de
0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t.
✅ Ejemplo conforme 2 — Callback como parámetro y puntero global¶
static int aplicar(int (*operacion)(int, int), int a, int b)
{
return operacion(a, b);
}
int (*comparar)(const void *, const void *) = NULL;Por qué cumple: el parámetro operacion y la variable comparar siguen
(*nombre)(...). Los parámetros const void * respetan 0x3011h: Si una función recibe un puntero genérico para operaciones de solo lectura, la firma de la función debe utilizar const void* y la
inicialización a NULL se alinea con 0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0.
⚠️ Casos límite¶
Arreglo de punteros a función:
int (*tabla[4])(int, int);conserva el grupo(*tabla[4])y también se rige por esta forma.Función que devuelve puntero a función: el declarador se lee de adentro hacia afuera; la cátedra recomienda un
typedefintermedio para no anidar.Calificadores: el orden de
constsigue 0x3017h: Orden canónico de calificadores: ‘const tipo’ en lugar de ‘tipo const’.Sin parámetros: se usa
voidexplícito,int (*callback)(void), según 0x200Eh: Uso obligatorio de ‘void’ explícito en funciones sin parámetros.En un cast:
(int (*)(int, int))fmantiene la forma canónica.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Marca espacios alrededor del * o entre ) y ( en punteros a función. |
gcc / clang | gcc -Wall -Wextra -Wpedantic -std=c11 -c archivo.c | Advierte incompatibilidades de tipo entre firmas distintas. |
| Revisión manual | — | Buscar (* y * ) con espacios intermedios. |
Checklist de autocontrol¶
Escribí
tipo (*identificador)(params)sin excepción.Verifiqué que el paréntesis de la lista de parámetros está pegado al cierre del nombre.
Usé un
typedefcon sufijo_tcuando la firma se repite.No dejé
*ni*sueltos dentro del declarador.
Reglas relacionadas¶
0x0005h: El asterisco de los punteros debe declararse junto al identificador — asterisco de punteros junto al identificador.
0x0009h: Espaciado consistente en declaraciones de doble puntero (tipo **var) — espaciado en punteros a puntero.
0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t — alias de tipo con sufijo
_t.0x200Eh: Uso obligatorio de ‘void’ explícito en funciones sin parámetros —
voidexplícito en funciones sin parámetros.0x3017h: Orden canónico de calificadores: ‘const tipo’ en lugar de ‘tipo const’ — orden canónico de calificadores.