Convenciones de formato: que el código “se lea solo”
El estilo no cambia lo que hace el programa, pero sí cambia cuánto tardas en entenderlo, corregirlo y ampliarlo. La meta es reducir la carga mental: que la estructura sea visible a simple vista y que los detalles importantes destaquen.
Indentación consistente
Elige un tamaño (por ejemplo, 4 espacios) y úsalo siempre. Evita mezclar tabuladores y espacios en el mismo proyecto, porque se ven distinto según el editor.
/* Bien: indentación uniforme y bloques claros */
if (condicion) {
accion();
} else {
otra_accion();
}Llaves: siempre explícitas en bloques
En C es legal omitir llaves si el bloque tiene una sola sentencia, pero es una fuente común de errores al modificar el código. Una práctica segura es usar llaves siempre en if, else, for y while.
/* Riesgoso: al agregar una línea, se rompe la intención */
if (x > 0)
y++;
z++; /* Parece dentro del if, pero no lo está *//* Mejor: intención inequívoca */
if (x > 0) {
y++;
z++;
}Espacios: separa ideas, no caracteres
- Un espacio después de comas:
f(a, b, c). - Espacios alrededor de operadores binarios:
a + b,x == y. - No pegues operadores a paréntesis:
if (x > 0), noif(x>0). - Evita líneas excesivamente largas; si una expresión crece, considera extraer variables intermedias con nombres.
/* Difícil de escanear */
if((a+b*2>c)&&(d!=0||e<f)){
g=h+i*j;
}
/* Más legible */
int umbral = a + b * 2;
int condicion_1 = (umbral > c);
int condicion_2 = (d != 0) || (e < f);
if (condicion_1 && condicion_2) {
g = h + i * j;
}Nombres: la primera capa de documentación
Un buen nombre reduce la necesidad de comentarios y evita malentendidos. Debe contestar: “¿qué representa?” y, si aplica, “¿en qué unidad o formato?”
Reglas prácticas para variables
- Evita abreviaturas crípticas:
cntpuede ser “count”, perocountes más claro. - Incluye unidad o contexto cuando ayude:
timeout_ms,size_bytes,index,last_pos. - Prefiere nombres concretos:
total¿total de qué? Mejortotal_items. - Evita nombres que se confundan con funciones:
printcomo variable es mala idea.
Reglas prácticas para funciones
- Verbo + objeto:
parse_line,compute_checksum,find_max. - Describe efectos: si modifica un buffer, que se note:
trim_in_place. - Evita funciones “multiusos” con nombres vagos:
process,handle,do_stuff.
Convención de estilo de nombres
En C son comunes snake_case para funciones y variables, y UPPER_SNAKE_CASE para constantes simbólicas. Lo importante es la consistencia dentro del proyecto.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
/* Ejemplo de consistencia */
int max_retries = 3;
int retry_count = 0;
int compute_total_items(const int *items, int n);
#define BUFFER_SIZE 256Comentarios útiles: explica el “por qué”, no el “qué”
El código ya dice “qué hace” (si está bien escrito). Los comentarios valiosos explican decisiones, supuestos, restricciones, casos borde y razones de diseño.
Malos comentarios (redundantes)
i++; /* incrementa i */
if (x == 0) { /* si x es 0 */
...
}Buenos comentarios (contexto y decisiones)
/* Usamos >= para aceptar el límite exacto: el protocolo permite tamaño máximo inclusive. */
if (payload_size >= MAX_PAYLOAD) {
return -1;
}
/* Evitamos división por cero: en este módulo, rate puede ser 0 cuando el sensor está desconectado. */
if (rate == 0) {
return 0;
}Comentario de cabecera de función (contrato)
Una plantilla simple que ayuda mucho es documentar: propósito, entradas, salidas, efectos secundarios, errores y supuestos.
/*
* compute_average
* Calcula el promedio entero de un arreglo.
* - items: puntero válido a n enteros.
* - n: cantidad de elementos (n >= 0).
* Devuelve: promedio (0 si n == 0).
* Nota: usa división entera (trunca).
*/
int compute_average(const int *items, int n) {
...
}Patrones para leer código en C de forma efectiva
Leer código es una habilidad activa: haces hipótesis y las verificas. Estos patrones te ayudan a orientarte rápido, incluso en código ajeno.
1) Identifica entradas, salidas y efectos secundarios
Haz un mapa mental de:
- Entradas: parámetros, variables globales usadas, lectura de archivos, lectura de
stdin. - Salidas: valor de retorno, parámetros por puntero, escritura en buffers, escritura a archivos, impresión.
- Efectos secundarios: modifica memoria apuntada, cambia estado global, reserva/libera memoria, altera un contador.
Guía paso a paso:
- Lee la firma de la función y marca parámetros que son punteros (
*) como posibles salidas. - Busca
returnpara entender qué significa el valor devuelto. - Escanea asignaciones a
*ptr,array[i]y variables globales.
int parse_int(const char *s, int *out_value) {
/* Entrada: s; Salida: *out_value; Retorno: 0 ok, -1 error */
...
}2) Sigue variables clave (las que “transportan” la lógica)
En muchos algoritmos hay 2–4 variables que explican casi todo: índices, acumuladores, punteros de recorrido, estados. Identifícalas y síguelas.
Guía paso a paso:
- Localiza inicializaciones (dónde nacen).
- Localiza actualizaciones (dónde cambian).
- Localiza usos críticos (en condiciones, en accesos a arrays, en retornos).
int i = 0; /* índice clave */
int sum = 0; /* acumulador clave */
while (i < n) {
sum += a[i];
i++;
}3) Localiza condiciones de borde
Los bugs suelen vivir en: cero elementos, uno solo, límites máximos, valores negativos, overflow, punteros nulos, cadenas vacías, fin de archivo.
Guía paso a paso:
- Busca comparaciones con 0:
n == 0,len == 0,ptr == NULL. - Busca límites:
i < nvsi <= n,size - 1,MAX_*. - Busca retornos tempranos (
return) y qué casos cubren.
if (n == 0) {
return 0; /* borde: arreglo vacío */
}
for (i = 0; i < n; i++) {
...
}4) Verifica invariantes del bucle
Un invariante es algo que debe ser cierto en cada iteración. Pensar en invariantes te permite detectar errores de lógica sin ejecutar el programa.
Ejemplo: recorrer un arreglo sin salirte.
/* Invariante: 0 <= i && i <= n */
for (i = 0; i < n; i++) {
/* Antes de usar a[i], verifica mentalmente que i < n */
sum += a[i];
}Guía paso a paso para revisar un bucle:
- Identifica el rango esperado de la variable de control (por ejemplo,
i). - Comprueba que la condición del bucle mantiene ese rango.
- Comprueba que la actualización acerca el bucle al fin (evita bucles infinitos).
- Comprueba que los accesos a memoria usan índices/punteros dentro del rango.
Cómo pequeñas mejoras cambian la comprensión (antes/después)
Ejemplo 1: nombres y variables intermedias
Antes: se entiende, pero cuesta escanear y es fácil equivocarse al modificar.
int f(int *a, int n) {
int i, s = 0;
for (i = 0; i < n; i++) {
if (a[i] > 0 && a[i] % 2 == 0) s += a[i];
}
return s;
}Después: el propósito aparece en los nombres, y la condición se lee como una frase.
int sum_positive_even(const int *values, int count) {
int i;
int sum = 0;
for (i = 0; i < count; i++) {
int v = values[i];
int is_positive = (v > 0);
int is_even = (v % 2 == 0);
if (is_positive && is_even) {
sum += v;
}
}
return sum;
}Ejemplo 2: funciones cortas y responsabilidad única
Antes: una función hace demasiadas cosas; para entenderla hay que retener mucho estado mental.
int process(const char *s, int *out) {
int i = 0;
int sign = 1;
int value = 0;
if (!s || !out) return -1;
while (s[i] == ' ' || s[i] == '\t') i++;
if (s[i] == '-') { sign = -1; i++; }
if (s[i] < '0' || s[i] > '9') return -1;
while (s[i] >= '0' && s[i] <= '9') {
value = value * 10 + (s[i] - '0');
i++;
}
*out = sign * value;
return 0;
}Después: se separan pasos, se nombran decisiones y se facilita probar cada parte mentalmente.
static int is_space(char c) {
return (c == ' ' || c == '\t');
}
static int is_digit(char c) {
return (c >= '0' && c <= '9');
}
static int skip_spaces(const char *s, int i) {
while (s[i] && is_space(s[i])) {
i++;
}
return i;
}
int parse_signed_int(const char *s, int *out_value) {
int i;
int sign = 1;
int value = 0;
if (s == NULL || out_value == NULL) {
return -1;
}
i = skip_spaces(s, 0);
if (s[i] == '-') {
sign = -1;
i++;
}
if (!is_digit(s[i])) {
return -1;
}
while (is_digit(s[i])) {
value = value * 10 + (s[i] - '0');
i++;
}
*out_value = sign * value;
return 0;
}Observa el cambio al leer: ahora puedes entender el flujo como una secuencia de pasos con nombres. Además, si aparece un bug, sabes dónde mirar: espacios, signo, dígitos, acumulación.
Lista de verificación de claridad (sin herramientas externas)
Usa esta lista como revisión manual antes de dar por “terminado” un archivo o una función.
Formato y estructura
- ¿La indentación es consistente en todo el archivo?
- ¿Todas las estructuras de control usan llaves?
- ¿Hay espacios alrededor de operadores y después de comas?
- ¿Las líneas largas se pueden simplificar con variables intermedias o funciones?
Nombres
- ¿Los nombres de funciones describen una acción concreta (verbo + objeto)?
- ¿Los nombres de variables indican significado y, si aplica, unidad/formato?
- ¿Evitas nombres genéricos como
tmp,data,valcuando hay alternativas mejores? - ¿Las constantes simbólicas tienen nombres claros y consistentes?
Comentarios
- ¿Los comentarios explican decisiones y supuestos (el “por qué”)?
- ¿Evitas comentar lo obvio (el “qué” literal)?
- ¿Las funciones no triviales tienen un mini-contrato (entradas, salidas, errores)?
Lectura y razonamiento
- ¿Puedes identificar rápidamente entradas, salidas y efectos secundarios?
- ¿Las variables clave están claras y su actualización es fácil de seguir?
- ¿Los casos borde están tratados de forma visible (retornos tempranos, validaciones)?
- ¿En cada bucle puedes enunciar un invariante simple y verificar que se mantiene?
- ¿La función hace una sola cosa? Si no, ¿puedes dividirla en funciones más pequeñas con nombres?