C: Referência de Eficiência Idiomática
Sumário
- Gerenciamento de Memória
- Ponteiros e Arrays
- Tratamento de Erros
- Strings
- Structs e Enums
- Pré-processador e Headers
- Antipadrões específicos de C
1. Gerenciamento de Memória {#memoria}
// ❌ malloc sem verificar o valor retornado
char *buf = malloc(size);
strcpy(buf, src);
// ✅
char *buf = malloc(size);
if (!buf) return -ENOMEM;
memcpy(buf, src, size);
// ❌ Cast do resultado de malloc (desnecessário em C, esconde falta de #include)
int *p = (int *)malloc(n * sizeof(int));
// ✅
int *p = malloc(n * sizeof *p);
// ❌ free sem anular o ponteiro (risco de dangling pointer em escopo de longa duração)
free(ptr);
// ... código posterior pode usar ptr
// ✅
free(ptr);
ptr = NULL;
// ❌ Esquecer de liberar memória em caminhos de retorno antecipado
char *a = malloc(100);
char *b = malloc(200);
if (!b) return -1; // vaza a
// ✅ — rótulo único de limpeza
char *a = NULL, *b = NULL;
a = malloc(100);
if (!a) goto cleanup;
b = malloc(200);
if (!b) goto cleanup;
// ... usar a, b ...
cleanup:
free(b);
free(a);
Use sizeof *ptr em vez de sizeof(Tipo) — permanece correto quando o tipo muda.
2. Ponteiros e Arrays {#ponteiros}
// ❌ Rastreamento manual do tamanho do array
void process(int *arr, int len) { ... }
process(data, 10);
// ✅ — passar tamanho junto com o ponteiro, ou usar um struct
typedef struct { int *data; size_t len; } IntSlice;
// ❌ Aritmética de ponteiros onde indexação de array é mais clara
*(arr + i) = value;
// ✅
arr[i] = value;
// ❌ VLA em código de produção (risco de estouro de pilha, opcional no C11+)
int arr[n];
// ✅
int *arr = malloc(n * sizeof *arr);
if (!arr) return -ENOMEM;
// ... usar arr ...
free(arr);
3. Tratamento de Erros {#erros}
// ❌ Usar números mágicos para retornos de erro
if (do_thing() == -1) { ... }
// ✅ — definir ou usar códigos de erro nomeados
#include <errno.h>
if (do_thing() < 0) {
perror("do_thing");
return errno;
}
// ❌ Verificações de erro profundamente aninhadas
int r1 = step1();
if (r1 == 0) {
int r2 = step2();
if (r2 == 0) {
int r3 = step3();
// ...
}
}
// ✅ — retorno antecipado / goto cleanup
if (step1() < 0) goto fail;
if (step2() < 0) goto fail;
if (step3() < 0) goto fail;
return 0;
fail:
cleanup();
return -1;
goto cleanup é C idiomático para liberação de recursos — não o evite por princípio.
4. Strings {#strings}
// ❌ strcpy sem verificação de limites
strcpy(dest, src);
// ✅
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
// ou melhor: snprintf(dest, sizeof(dest), "%s", src);
// ❌ Uso incorreto de strcmp
if (str == "hello") { ... } // compara ponteiros, não conteúdo
// ✅
if (strcmp(str, "hello") == 0) { ... }
// ❌ Construir strings com strcat repetido (O(n²))
char result[1024] = "";
for (int i = 0; i < n; i++) {
strcat(result, items[i]);
}
// ✅ — rastrear posição de escrita
char result[1024];
int pos = 0;
for (int i = 0; i < n && pos < (int)sizeof(result); i++) {
pos += snprintf(result + pos, sizeof(result) - pos, "%s", items[i]);
}
Prefira snprintf a sprintf — sempre.
5. Structs e Enums {#structs}
// ❌ Struct puro exigindo a palavra-chave `struct` em todo lugar
struct point { int x, y; };
struct point p = {1, 2};
// ✅
typedef struct { int x, y; } Point;
Point p = {1, 2};
// ❌ Struct não inicializado
Point p;
use(p.x); // UB
// ✅
Point p = {0};
// ❌ Constantes inteiras mágicas
if (state == 3) { ... }
// ✅
typedef enum { STATE_IDLE, STATE_RUNNING, STATE_DONE } State;
if (state == STATE_DONE) { ... }
6. Pré-processador e Headers {#preprocessador}
// ❌ Macro onde função inline funciona (sem segurança de tipo, dupla avaliação)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
MAX(x++, y) // x incrementado duas vezes se x > y
// ✅
static inline int max_int(int a, int b) { return a > b ? a : b; }
// ❌ Sem guarda de inclusão
// meu_header.h
struct Foo { int x; };
// ✅
#ifndef MEU_HEADER_H
#define MEU_HEADER_H
struct Foo { int x; };
#endif
// ou: #pragma once (amplamente suportado, não padrão)
Mantenha macros para compilação condicional e constantes. Use static inline para lógica.
7. Antipadrões específicos de C {#antipadroes}
| Antipadrão | Preferível |
|---|---|
sprintf | snprintf com tamanho do buffer |
gets | fgets (gets foi removido no C11) |
Cast do resultado de malloc | deixar a conversão implícita de void* funcionar |
sizeof(Tipo) em malloc | sizeof *ptr |
| VLA para arrays grandes/em tempo de execução | alocação no heap |
Callback void* sem parâmetro de contexto | passar void *ctx junto com o ponteiro de função |
| Estado global mutável | passar estado através de ponteiros para struct |
assert para tratamento de erro em tempo de execução | códigos de retorno de erro adequados |
Faltando const em parâmetros de ponteiro somente leitura | const char *str |
| Misturar signed/unsigned em comparações | usar tipos consistentes, fazer cast explícito |
Limitações
- Estas são diretrizes específicas da linguagem e não cobrem decisões arquiteturais gerais.
- A compressão excessiva pode reduzir a legibilidade; use o bom senso.