C: Referencia de Eficiencia Idiomática
Tabla de Contenidos
- Gestión de Memoria
- Punteros y Arrays
- Manejo de Errores
- Cadenas
- Estructuras y Enumeraciones
- Preprocesador y Headers
- Anti-patrones específicos de C
1. Gestión de Memoria {#memoria}
// ❌ malloc sin verificar el valor de retorno
char *buf = malloc(size);
strcpy(buf, src);
// ✅
char *buf = malloc(size);
if (!buf) return -ENOMEM;
memcpy(buf, src, size);
// ❌ Cast del resultado de malloc (innecesario en C, oculta falta de #include)
int *p = (int *)malloc(n * sizeof(int));
// ✅
int *p = malloc(n * sizeof *p);
// ❌ free sin asignar NULL (riesgo de puntero colgante en ámbito de larga vida)
free(ptr);
// ... código posterior podría usar ptr
// ✅
free(ptr);
ptr = NULL;
// ❌ Olvidar liberar en caminos de retorno temprano
char *a = malloc(100);
char *b = malloc(200);
if (!b) return -1; // fuga de a
// ✅ — etiqueta única de limpieza
char *a = NULL, *b = NULL;
a = malloc(100);
if (!a) goto limpieza;
b = malloc(200);
if (!b) goto limpieza;
// ... usar a, b ...
limpieza:
free(b);
free(a);
Usa sizeof *ptr en lugar de sizeof(Tipo) — se mantiene correcto cuando el tipo cambia.
2. Punteros y Arrays {#punteros}
// ❌ Seguimiento manual del tamaño del array
void procesar(int *arr, int len) { ... }
procesar(datos, 10);
// ✅ — pasar tamaño junto al puntero, o usar un struct
typedef struct { int *datos; size_t len; } IntSlice;
// ❌ Aritmética de punteros donde la indexación de arrays es más clara
*(arr + i) = valor;
// ✅
arr[i] = valor;
// ❌ VLA en código de producción (riesgo de desbordamiento de pila, opcional en C11+)
int arr[n];
// ✅
int *arr = malloc(n * sizeof *arr);
if (!arr) return -ENOMEM;
// ... usar arr ...
free(arr);
3. Manejo de Errores {#errores}
// ❌ Usar números mágicos para retornos de error
if (hacer_cosa() == -1) { ... }
// ✅ — definir o usar códigos de error con nombre
#include <errno.h>
if (hacer_cosa() < 0) {
perror("hacer_cosa");
return errno;
}
// ❌ Anidamiento profundo de verificaciones de error
int r1 = paso1();
if (r1 == 0) {
int r2 = paso2();
if (r2 == 0) {
int r3 = paso3();
// ...
}
}
// ✅ — retorno temprano / goto cleanup
if (paso1() < 0) goto fallo;
if (paso2() < 0) goto fallo;
if (paso3() < 0) goto fallo;
return 0;
fallo:
limpiar();
return -1;
goto cleanup es C idiomático para la liberación de recursos — no lo evites por principio.
4. Cadenas {#cadenas}
// ❌ strcpy sin verificar límites
strcpy(dest, src);
// ✅
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
// o mejor: snprintf(dest, sizeof(dest), "%s", src);
// ❌ Uso incorrecto de strcmp
if (str == "hola") { ... } // compara punteros, no contenido
// ✅
if (strcmp(str, "hola") == 0) { ... }
// ❌ Construir cadenas con strcat repetido (O(n²))
char resultado[1024] = "";
for (int i = 0; i < n; i++) {
strcat(resultado, items[i]);
}
// ✅ — rastrear posición de escritura
char resultado[1024];
int pos = 0;
for (int i = 0; i < n && pos < (int)sizeof(resultado); i++) {
pos += snprintf(resultado + pos, sizeof(resultado) - pos, "%s", items[i]);
}
Prefiere snprintf sobre sprintf — siempre.
5. Estructuras y Enumeraciones {#estructuras}
// ❌ Struct simple que requiere la palabra clave `struct` en todas partes
struct punto { int x, y; };
struct punto p = {1, 2};
// ✅
typedef struct { int x, y; } Punto;
Punto p = {1, 2};
// ❌ Struct sin inicializar
Punto p;
usar(p.x); // comportamiento indefinido
// ✅
Punto p = {0};
// ❌ Constantes enteras mágicas
if (estado == 3) { ... }
// ✅
typedef enum { ESTADO_INACTIVO, ESTADO_EJECUTANDO, ESTADO_TERMINADO } Estado;
if (estado == ESTADO_TERMINADO) { ... }
6. Preprocesador y Headers {#preprocesador}
// ❌ Macro donde una función inline funciona (sin seguridad de tipos, doble evaluación)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
MAX(x++, y) // x se incrementa dos veces si x > y
// ✅
static inline int max_int(int a, int b) { return a > b ? a : b; }
// ❌ Sin guarda de inclusión
// mi_header.h
struct Foo { int x; };
// ✅
#ifndef MI_HEADER_H
#define MI_HEADER_H
struct Foo { int x; };
#endif
// o: #pragma once (ampliamente soportado, no estándar)
Reserva las macros para compilación condicional y constantes. Usa static inline para lógica.
7. Anti-patrones específicos de C {#antipatrones}
| Anti-patrón | Preferido |
|---|---|
sprintf | snprintf con tamaño de buffer |
gets | fgets (gets fue eliminado en C11) |
Cast del resultado de malloc | dejar que la conversión implícita de void* funcione |
sizeof(Tipo) en malloc | sizeof *ptr |
| VLA para arrays grandes/de tiempo de ejecución | asignación en heap |
Callbacks void* sin parámetro de contexto | pasar void *ctx junto al puntero de función |
| Estado mutable global | pasar estado a través de punteros a struct |
assert para manejo de errores en tiempo de ejecución | códigos de retorno de error adecuados |
Falta de const en parámetros de solo lectura | const char *str |
| Mezclar signed/unsigned en comparaciones | usar tipos consistentes, convertir explícitamente |
Limitaciones
- Estas son pautas específicas del lenguaje y no cubren decisiones arquitectónicas generales.
- La sobrecompresión podría reducir la legibilidad; aplica tu criterio.