Mejores Prácticas
## Objetivos de Aprendizaje
- Escribir código C limpio y mantenible
- Seguir convenciones de nomenclatura
- Dominar técnicas de depuración
- Aplicar pruebas efectivas
## Estilo de Código
### Convenciones de Nomenclatura
```c
// Variables: snake_case
int user_age;
char *file_name;
size_t buffer_size;
// Constantes: UPPER_SNAKE_CASE
#define MAX_BUFFER_SIZE 1024
const int MAX_RETRIES = 3;
// Funciones: snake_case
int calculate_total(void);
void process_data(const char *input);
// Tipos/Estructuras: snake_case con sufijo _t
typedef struct {
int x;
int y;
} point_t;
```
### Formato
```c
// Usar 4 espacios para indentación
// Longitud de línea: ~80 caracteres máximo
// Una declaración por línea
int age;
char *name;
size_t count;
// Llaves siempre
if (condition) {
do_something();
} else {
do_other();
}
// Declaraciones de funciones
int main(void) {
return 0;
}
```
## Inicialización
### Siempre Inicializar Variables
```c
// Mal - contiene basura
int count;
// Bien - explícitamente inicializado
int count = 0;
// Bien - inicializado a cero
int *ptr = NULL;
char buffer[100] = {0};
```
### Inicializar Punteros a NULL
```c
// Buena práctica
int *ptr = NULL;
// Verificar antes de desreferenciar
if (ptr != NULL) {
*ptr = 42;
}
```
## Gestión de Memoria
### Siempre Liberar Memoria
```c
int *ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
return 1;
}
// Usar memoria...
free(ptr);
ptr = NULL; // Prevenir puntero colgante
```
### Verificar Asignación
```c
int *ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
// Manejar error
fprintf(stderr, "Allocation failed\n");
return 1;
}
```
### Liberar en Orden Inverso
```c
// Si se asignan nodos enlazados
struct Node *n1 = malloc(sizeof(struct Node));
struct Node *n2 = malloc(sizeof(struct Node));
n1->next = n2;
// Liberar en orden inverso
free(n2);
free(n1);
```
## Funciones
### Usar const para Parámetros de Solo Lectura
```c
// Bien - indica que str no se modifica
size_t stringLength(const char *str) {
size_t len = 0;
while (str[len] != '\0') {
len++;
}
return len;
}
```
### Retornar Códigos de Error
```c
typedef enum {
SUCCESS = 0,
ERROR_NULL_POINTER = 1,
ERROR_OUT_OF_MEMORY = 2,
ERROR_INVALID_INPUT = 3
} error_code_t;
error_code_t processData(const char *input, int *output) {
if (input == NULL || output == NULL) {
return ERROR_NULL_POINTER;
}
// Procesar...
return SUCCESS;
}
```
### Las Funciones Pequeñas Son Buenas
```c
// Bien - responsabilidad única
int isEven(int x) {
return x % 2 == 0;
}
int isOdd(int x) {
return x % 2 != 0;
}
```
## Arreglos y Cadenas
### Cálculo de Tamaño
```c
int arr[10];
size_t size = sizeof(arr) / sizeof(arr[0]);
```
### Verificación de Límites
```c
int getElement(const int *arr, size_t size, size_t index) {
if (index >= size) {
// Manejar error
return 0;
}
return arr[index];
}
```
### Terminar Cadenas en NULL
```c
char buffer[100];
strncpy(buffer, input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
```
## Manejo de Errores
### Verificar Valores de Retorno
```c
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
perror("Failed to open file");
return 1;
}
// Usar archivo...
if (fclose(fp) != 0) {
perror("Failed to close file");
return 1;
}
```
### Usar assert para Depuración
```c
#include
void process(int *data, size_t size) {
assert(data != NULL);
assert(size > 0);
// Procesar...
}
```
### Desactivar en Producción
```c
#define NDEBUG // Antes de #include
#include
```
## Comentarios
### Usar Comentarios con Sabiduría
```c
// Bien: explica POR QUÉ, no QUÉ
// Saltar primera línea (contiene encabezado)
if (line[0] == '#') {
continue;
}
// Mal: redundante
int i = 0; // Inicializar i a 0
```
### Documentar Funciones
```c
/**
* Calcula la suma de dos enteros.
*
* @param a Primer entero
* @param b Segundo entero
* @return Suma de a y b
*/
int add(int a, int b) {
return a + b;
}
```
## Portabilidad
### Tipos de Tamaño Fijo
```c
#include
int32_t i32 = 42; // Exactamente 32 bits
uint64_t u64 = 100; // Exactamente 64 bits sin signo
intptr_t ptr = (intptr_t)&x; // Contiene puntero
```
### Evitar Suposiciones Sobre Tamaño
```c
// Mal - asume 4 bytes
long x = 1000000;
// Bien - usar tamaño fijo o límite
int32_t x = 1000000;
```
### Impresión Portátil
```c
#include
int64_t big = 9000000000000000000LL;
printf("%" PRId64 "\n", big);
```
## Seguridad
### Evitar Desbordamiento de Buffer
```c
// Mal - posible desbordamiento de buffer
char buffer[100];
gets(buffer);
// Bien - entrada segura
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = '\0';
```
### Validar Entrada
```c
if (strlen(input) >= MAX_INPUT) {
// Manejar error
}
```
### Usar Funciones de Cadena Seguras
```c
// Usar strncpy en lugar de strcpy
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
// Usar strncat en lugar de strcat
strncat(dest, src, sizeof(dest) - strlen(dest) - 1);
```
## Pruebas
### Ejemplo de Pruebas Unitarias
```c
#include
int add(int a, int b) {
return a + b;
}
void testAdd(void) {
assert(add(2, 3) == 5);
assert(add(-1, 1) == 0);
assert(add(0, 0) == 0);
assert(add(-5, -5) == -10);
}
int main(void) {
testAdd();
printf("All tests passed!\n");
return 0;
}
```
## Depuración
### valgrind
```bash
valgrind --leak-check=full ./program
```
### Advertencias de gcc
```bash
gcc -Wall -Wextra -Werror -pedantic program.c -o program
```
| Bandera | Descripción |
|---------|-------------|
| `-Wall` | Activar todas las advertencias |
| `-Wextra` | Advertencias adicionales |
| `-Werror` | Tratar advertencias como errores |
| `-pedantic` | Conformidad ISO C |
| `-g` | Incluir símbolos de depuración |
| `-O2` | Optimizar (para pruebas de rendimiento) |
## Rendimiento
### Evitar Optimización Prematura
```c
// Código claro primero
for (int i = 0; i < n; i++) {
sum += arr[i];
}
// Optimizar solo si profiling muestra que es necesario
```
### Usar Funciones Inline (C99)
```c
inline int max(int a, int b) {
return a > b ? a : b;
}
```
## Resumen
- Usar nombres significativos (snake_case para variables)
- Siempre inicializar variables
- Verificar valores de retorno de asignación
- Liberar memoria y establecer a NULL
- Usar const para parámetros de solo lectura
- Activar advertencias del compilador (-Wall -Wextra)
- Usar valgrind para detectar fugas de memoria
- Escribir pruebas para funciones críticas
- Validar toda la entrada
- Usar tipos de tamaño fijo para portabilidad
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →