C / C++
/*
Aprenda X em Y Minutos
Onde X = C (https://learnxinyminutes.com/pt-br/c/)
Use Clang ou GCC.
Como compilar (recomendado):
clang -std=c11 -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion \
-Wshadow -Wpointer-arith -Wstrict-prototypes -Wundef -Werror \
-fsanitize=address,undefined -fno-omit-frame-pointer -O2 learnc.c -o learnc
Ou:
gcc -std=c11 -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion \
-Wshadow -Wpointer-arith -Wstrict-prototypes -Wundef -Werror \
-fsanitize=address,undefined -fno-omit-frame-pointer -O2 learnc.c -o learnc
Depuração:
ASAN_OPTIONS=detect_leaks=1 ./learnc
gdb ./learnc (ou lldb ./learnc)
Filosofia (K&R aplicada):
1) Faça o programa funcionar antes de otimizá-lo.
2) Torne-o claro antes de torná-lo esperto.
3) Media o que importa (tempo, memória, I/O).
4) Escreva interfaces pequenas e bem definidas; esconda detalhes em .c.
5) Teste casos pequenos e extremos; fuja do comportamento indefinido (UB).
*/
// -------------------------------------------------------------
// Comentários e constantes
// -------------------------------------------------------------
// Comentários de uma linha (C99+)
#include <stddef.h> // size_t
/*
Comentários de múltiplas linhas funcionam desde o C89.
*/
// Constantes por macro:
#define DAY_IN_YEAR 365
// Enumerações também criam inteiros-constantes legíveis:
enum day { DOM = 1, SEG, TER, QUA, QUI, SEX, SAB };
// SEG recebe 2 automaticamente, TER 3, etc.
// -------------------------------------------------------------
// Includes usuais da biblioteca padrão
// -------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h> // tipos de largura fixa (uint32_t etc.)
#include <stdbool.h> // bool, true, false (C99)
#include <limits.h> // limites de tipos inteiros
#include <errno.h> // códigos de erro para I/O, strtoul, etc.
// Para cabeçalhos próprios, use aspas:
#include "minha_biblioteca.h"
// -------------------------------------------------------------
// Protótipos (declare antes de usar, ou em um .h)
// -------------------------------------------------------------
void funcao_1(void);
int funcao_2(void);
int soma_dois_ints(int x1, int x2); // protótipo de função
void str_reverse(char *str_in);
// -------------------------------------------------------------
// Ponto de entrada
// -------------------------------------------------------------
int main(void) {
// Saída formatada: %d = inteiro, \n = nova linha
printf("%d\n", 0); // => 0
// ---------------------------------------------------------
// Tipos
// ---------------------------------------------------------
int x_int = 0; // normalmente 4 bytes
short x_short = 0; // normalmente 2 bytes
char x_char = 0; // sempre 1 byte (8 bits na prática)
char y_char = 'y'; // literais de caractere usam aspas simples
long x_long = 0; // 4 ou 8 bytes
long long x_long_long = 0; // >= 64 bits
float x_float = 0.0f; // 32 bits
double x_double = 0.0; // 64 bits
unsigned short ux_short = 0;
unsigned int ux_int = 0u;
unsigned long long ux_long_long = 0ull;
// Caracteres são inteiros da tabela de caracteres da máquina (ASCII/UTF-8 no byte):
(void)'0'; // 48 em ASCII
(void)'A'; // 65 em ASCII
// sizeof(T) e sizeof(expr) retornam o tamanho em bytes
printf("sizeof(int) = %zu\n", sizeof(int));
{
int a = 1;
size_t sz = sizeof(a++); // a++ NÃO é avaliada
printf("sizeof(a++) = %zu; a = %d\n", sz, a);
}
// ---------------------------------------------------------
// Arrays
// ---------------------------------------------------------
char meu_char_array[20]; // 20 bytes
int meu_int_array[20]; // 20 * sizeof(int)
// Inicialização com zeros:
char meu_array[20] = {0};
meu_array[1] = 2;
printf("%d\n", meu_array[1]); // => 2
// VLA (C99; opção em C11): tamanho decidido em tempo de execução
{
char buf[0x100];
fputs("Entre o tamanho do array: ", stdout);
if (fgets(buf, sizeof buf, stdin)) {
errno = 0;
char *end = NULL;
unsigned long n = strtoul(buf, &end, 10);
if (errno == 0 && end != buf && n > 0 && n < 100000) {
size_t size = (size_t)n;
int var_length_array[size]; // VLA
printf("sizeof array = %zu\n", sizeof var_length_array);
// Ex.: entrada 10 -> sizeof = 40 (se int=4 bytes)
(void)var_length_array; // evitar warning
} else {
fputs("tamanho inválido\n", stderr);
}
}
}
// Strings são arrays de char terminados por '\0'
char uma_string[20] = "Isto e uma string"; // evite acentos em ASCII puro
printf("%s\n", uma_string);
printf("%d\n", uma_string[17]); // => 0 (byte nulo)
// Literais de char têm tipo 'int' por história:
int cha = 'a';
char chb = 'a';
(void)cha; (void)chb;
// Arrays multidimensionais
int multi_array[2][5] = {
{1, 2, 3, 4, 5},
{6, 7, 8, 9, 0}
};
int array_int = multi_array[0][2]; // 3
(void)array_int;
// ---------------------------------------------------------
// Operadores
// ---------------------------------------------------------
{
int i1 = 1, i2 = 2;
float f1 = 1.0f, f2 = 2.0f;
(void)(i1 + i2);
(void)(i2 - i1);
(void)(i2 * i1);
(void)(i1 / i2); // 0
(void)(f1 / f2); // ~0.5 (ponto flutuante não é exato)
(void)(11 % 3); // 2
// Comparações retornam 0/1; em C99 há _Bool/bool
(void)(3 == 2);
(void)(3 != 2);
(void)(3 > 2);
(void)(3 < 2);
(void)(2 <= 2);
(void)(2 >= 2);
// Não encadeie comparações como em Python:
{
int a = 1;
int errado = 0 < a < 2; // errado
int certo = 0 < a && a < 2; // certo
(void)errado; (void)certo;
}
// Lógica
(void)(!3);
(void)(!0);
(void)(1 && 1);
(void)(0 || 1);
// Ternário
{
int a = 5, b = 10;
int z = (a > b) ? a : b; // 10
(void)z;
}
// Pré/pós-incremento
{
const char *s = "iLoveC";
int j = 0;
(void)s[j++]; // 'i'
j = 0;
(void)s[++j]; // 'L'
}
// Bit a bit
(void)(~0x0F); // 0xF0
(void)(0x0F & 0xF0); // 0x00
(void)(0x0F | 0xF0); // 0xFF
(void)(0x04 ^ 0x0F); // 0x0B
(void)(0x01 << 1); // 0x02
(void)(0x02 >> 1); // 0x01
// Cuidado com shifts em signed e largura do tipo: pode ser UB
}
// ---------------------------------------------------------
// Estruturas de Controle
// ---------------------------------------------------------
if (0) {
puts("Nunca roda");
} else if (0) {
puts("Também não");
} else {
puts("Eu serei impresso");
}
{
int ii = 0;
while (ii < 10) {
printf("%d, ", ii++);
}
printf("\n");
}
{
int kk = 0;
do {
printf("%d, ", kk);
} while (++kk < 10);
printf("\n");
}
{
for (int jj = 0; jj < 10; jj++) {
printf("%d, ", jj);
}
printf("\n");
}
// Corpo vazio:
for (int i = 0; i <= 5; i++) {
; // declaração nula
}
for (int i = 0; i <= 5; i++); // também compila, mas cuidado: é fácil errar
// switch
{
int alguma_expressao_integral = 1;
switch (alguma_expressao_integral) {
case 0:
puts("zero");
break;
case 1:
puts("um");
break;
default:
fputs("erro!\n", stderr);
exit(EXIT_FAILURE);
}
}
// ---------------------------------------------------------
// Casts
// ---------------------------------------------------------
{
int x_hex = 0x01;
printf("%d %d %d\n", x_hex, (short)x_hex, (char)x_hex);
// Overflow silencioso:
printf("%u\n", (unsigned)(unsigned char)257u); // 1 (se char=8 bits)
printf("%f\n", (float)100);
printf("%lf\n", (double)100);
printf("%d\n", (int)(char)100.0);
}
// ---------------------------------------------------------
// Ponteiros
// ---------------------------------------------------------
{
int x = 0;
printf("%p\n", (void *)&x);
int *px = &x, nao_eh_um_ponteiro = 0;
printf("%p\n", (void *)px);
printf("sizeof(px)=%zu sizeof(nao_eh_um_ponteiro)=%zu\n",
sizeof px, sizeof nao_eh_um_ponteiro);
printf("%d\n", *px); // 0
(*px)++; // incrementa x via ponteiro
printf("%d %d\n", *px, x); // 1 1
// Arrays e aritmética de ponteiros
int x_array[20];
for (int xx = 0; xx < 20; xx++) x_array[xx] = 20 - xx;
int *x_ptr = x_array; // decaimento para ponteiro
printf("%d %d\n", *(x_ptr + 1), x_array[1]); // 19 19
// Pega o endereço do array (tipo é "ponteiro para array")
int arr[10];
int (*ptr_to_arr)[10] = &arr;
(void)ptr_to_arr;
// sizeof em arrays NÃO decai:
printf("sizeof arr=%zu sizeof ptr=%zu\n", sizeof arr, sizeof x_ptr);
// Alocação dinâmica (heap)
int *my_ptr = (int *)malloc(sizeof *my_ptr * 20);
if (!my_ptr) { perror("malloc"); exit(EXIT_FAILURE); }
for (int xx = 0; xx < 20; xx++) my_ptr[xx] = 20 - xx;
// NÃO acesse fora dos limites: comportamento indefinido
// printf("%d\n", my_ptr[21]); // exemplo propositalmente comentado
free(my_ptr);
// Literais de string são imutáveis -> use const char *
const char *my_str = "Esta e a minha literal string";
printf("%c\n", *my_str); // 'E'
// String em memória mutável:
char foo[] = "foo";
foo[0] = 'a'; // ok -> "aoo"
}
funcao_1(); // demo
return 0;
}
// -------------------------------------------------------------
// Funções
// -------------------------------------------------------------
int soma_dois_ints(int x1, int x2) {
return x1 + x2;
}
/*
Funções são chamadas por valor. Array/strings "decaem" para ponteiro no call.
Para modificar o argumento do chamador, receba um ponteiro.
*/
void str_reverse(char *str_in) {
if (!str_in) return;
size_t len = strlen(str_in);
for (size_t i = 0; i < len / 2; i++) {
char tmp = str_in[i];
str_in[i] = str_in[len - i - 1];
str_in[len - i - 1] = tmp;
}
}
/*
Exemplo de uso:
char c[] = "Isto e um teste.";
str_reverse(c);
printf("%s\n", c); // ".etset mu e otsI"
*/
// Variáveis externas e linkage
int g_contador_publico = 0; // ligação externa por padrão
void testFunc(void) {
extern int g_contador_publico; // refere-se à global
g_contador_publico++;
}
// Torne símbolos "privados" ao arquivo com static
static int s_privado = 0; // não visível fora deste .c
static void helper_interno(void) { s_privado++; }
// -------------------------------------------------------------
// Tipos definidos pelo usuário e structs
// -------------------------------------------------------------
typedef int meu_tipo;
meu_tipo var_meu_tipo = 0;
struct retangulo {
int altura;
int largura;
};
void funcao_1(void) {
struct retangulo meu_retan = { .altura = 10, .largura = 20 };
// Acesso por .
meu_retan.altura = 30;
// Ponteiro para struct e ->
struct retangulo *p = &meu_retan;
p->largura = 10;
printf("area=%d\n", p->largura * p->altura);
}
// typedef para struct por conveniência
typedef struct retangulo retan;
// Passagem por valor (copia):
int area_val(retan r) { return r.largura * r.altura; }
// Passagem por ponteiro (sem cópia):
int area_ptr(const retan *r) { return r->largura * r->altura; }
// -------------------------------------------------------------
// Ponteiros para funções
// -------------------------------------------------------------
typedef void (*minha_funcao_type)(char *);
static void minusculo_exemplo(char *s) {
if (!s) return;
for (char *p = s; *p; ++p) if (*p >= 'A' && *p <= 'Z') *p += 32;
}
static void reverso_exemplo(char *s) { str_reverse(s); }
static void usar_fp(minha_funcao_type f, char *s) { if (f) f(s); }
// -------------------------------------------------------------
// Caractere especiais e printf
// -------------------------------------------------------------
/*
'\a' '\n' '\t' '\v' '\f' '\r' '\b' '\0' '\\' '\?' '\'' '\"' '\xhh' '\ooo'
Formatos printf:
%d %u %ld %zu %f %lf %s %c %p %x %o %%
*/
// -------------------------------------------------------------
// Ordem de avaliação e precedência (resumo)
// -------------------------------------------------------------
/*
() [] -> . esquerda->direita
! ~ ++ -- + - * (type) sizeof direita->esquerda
* / % esquerda->direita
+ - esquerda->direita
<< >> esquerda->direita
< <= > >= esquerda->direita
== != esquerda->direita
& esquerda->direita
^ esquerda->direita
| esquerda->direita
&& esquerda->direita
|| esquerda->direita
?: direita->esquerda
= += -= *= /= %= &= ^= |= <<= >>= direita->esquerda
, esquerda->direita
*/
// -------------------------------------------------------------
// PRÉ-PROCESSADOR, MACROS e BOAS PRÁTICAS
// -------------------------------------------------------------
/*
Dicas cruciais:
- Prefira 'const', 'static inline' e funções a macros sempre que possível.
- Macros precisam de parênteses extras para evitar bugs:
#define SQ(x) ((x) * (x))
- Use #if/#ifdef para seleção de plataforma; evite lógica complexa no pré-processador.
- Use header guards em .h:
// em arquivo.h
#ifndef ARQUIVO_H
#define ARQUIVO_H
// declarações
#endif
- Em headers, exponha apenas o necessário (protótipos e tipos opacos).
*/
// -------------------------------------------------------------
// ARMAZENAMENTO, VIDA ÚTIL e LIGAÇÃO (storage duration & linkage)
// -------------------------------------------------------------
/*
- auto (padrão): variável local, vida no bloco.
- static local: vida do programa, visível só no bloco/arquivo.
- extern: declara símbolo definido em outro lugar.
- const: somente leitura (o compilador pode otimizar).
- volatile: diz ao compilador que o valor pode mudar fora do controle
(memória mapeada, registradores, sinal).
*/
// -------------------------------------------------------------
// MEMÓRIA e ERROS COMUNS (segredos para dominar mais rápido)
// -------------------------------------------------------------
/*
1) Sempre inicialize ponteiros; cheque retorno de malloc/calloc/realloc.
2) Emparelhe alocação/liberação no mesmo nível de abstração.
3) Prefira 'calloc' para zero-inicializar buffers:
int *v = calloc(n, sizeof *v);
4) Evite 'realloc' sem temporário:
int *tmp = realloc(p, novo * sizeof *p);
if (tmp) p = tmp; else trate erro.
5) Nunca use memória após 'free' (use-after-free) e nunca leia além dos limites.
6) Evite UB: ponteiros inválidos, overflow de inteiro assinado, shift inválido,
desreferenciar NULL, múltiplos frees, violar strict-aliasing.
7) Ative sanitizers e warnings agressivos (linha de compilação no topo).
8) Teste com Valgrind (Linux) e com Address/UB Sanitizer.
9) Em APIs, documente ownership: quem aloca, quem libera.
10) Regra prática: se a função retorna um ponteiro "novo", o chamador libera;
se retorna ponteiro para "dentro" de algo recebido, quem recebeu é dono.
*/
// -------------------------------------------------------------
// I/O ROBUSTA e PARSE SEGURO
// -------------------------------------------------------------
/*
- Use fgets + strtoul/strtol/strtod com checagem de errno para ler números.
- printf/scanf: prefira scanf com especificadores limitados (%99s) e verifique
o valor de retorno (quantidade de itens lidos).
- fprintf(stderr, ...) para erros; retorne códigos consistentes.
*/
// -------------------------------------------------------------
// MÓDULOS, BUILD e ORGANIZAÇÃO
// -------------------------------------------------------------
/*
Arquitetura básica de projeto C:
src/
modulo.c
outro.c
include/
modulo.h
outro.h
tests/
test_modulo.c
Makefile
Exemplo mínimo de Makefile:
CC ?= clang
CFLAGS := -std=c11 -O2 -Wall -Wextra -Wpedantic
SAN := -fsanitize=address,undefined -fno-omit-frame-pointer
INC := -Iinclude
SRC := $(wildcard src/*.c)
OBJ := $(SRC:.c=.o)
all: app
app: $(OBJ)
$(CC) $(CFLAGS) $(SAN) $^ -o $@
%.o: %.c
$(CC) $(CFLAGS) $(SAN) $(INC) -c $< -o $@
clean:
rm -f src/*.o app
.PHONY: all clean
*/
// -------------------------------------------------------------
// CONJUNTOS DE BITS, UNIONS, BIT-FIELDS
// -------------------------------------------------------------
/*
- Use enums + máscaras para 'flags':
enum perms { P_READ=1<<0, P_WRITE=1<<1, P_EXEC=1<<2 };
unsigned p = P_READ | P_WRITE;
if (p & P_WRITE) { ... }
- unions compartilham a mesma área de memória entre membros; use com cuidado.
- bit-fields economizam bits, mas têm portabilidade limitada (ordem/empacotamento).
*/
// -------------------------------------------------------------
// CONST-CORRECTNESS, INLINE, RESTRICTION (restrict) e ALIASING
// -------------------------------------------------------------
/*
- Marque ponteiros que não serão modificados como 'const T *p'.
- 'restrict' (C99) indica que ponteiros não se sobrepõem -> permite otimização:
void saxpy(size_t n, float a, const float *restrict x,
float *restrict y) { ... }
- Strict aliasing: compilar com -fno-strict-aliasing evita alguns UB, mas o ideal
é respeitar as regras (não reinterpretar objetos via ponteiros de tipos
incompatíveis; use memcpy).
- 'static inline' em headers para funções pequenas evita macros perigosas.
*/
// -------------------------------------------------------------
// ERROS ESPECÍFICOS DE C QUE UM DEV SÊNIOR EVITA (atalhos de maestria)
// -------------------------------------------------------------
/*
- Esquecer 'break' em switch (a menos que deseje 'fallthrough' e documente).
- Comparar ponteiro com inteiro; converter ponteiros sem motivos.
- Retornar endereço de variável local (dangling pointer).
- Esquecer 'const' em literais de string e tentar modificá-las.
- Misturar API que aloca em um módulo e libera em outro sem convenção clara.
- Não verificar retorno de funções de I/O e conversão (errno).
- Usar %d para size_t/ptrdiff_t: prefira %zu/%td.
- Depender do tamanho de tipos (int, long) sem usar <stdint.h>.
*/
// -------------------------------------------------------------
// LEITURA ADICIONAL
// -------------------------------------------------------------
/*
- K&R: "The C Programming Language" — clássico; contextualize com práticas modernas.
- comp.lang.c FAQ — histórico de armadilhas e respostas.
- Linux kernel coding style — clareza e consistência.
- C11/C17 padrão (n1570 é rascunho público útil para consulta).
Referência sobre padding de struct e sizeof:
Por que sizeof(struct) != soma dos seus membros?
Resposta curta: alinhamento e preenchimento (padding) para performance/ABI.
[Pesquise por "struct padding alignment C" para detalhes.]
*/
// -------------------------------------------------------------
// EXEMPLOS RÁPIDOS DE TESTES (micro-unidade sem framework)
// -------------------------------------------------------------
#ifdef DEMO_TESTS
static int tests_passed = 0;
static void assert_int_eq(int got, int want, const char *msg) {
if (got != want) {
fprintf(stderr, "FAIL: %s (got=%d want=%d)\n", msg, got, want);
exit(EXIT_FAILURE);
}
tests_passed++;
}
static void run_tests(void) {
assert_int_eq(soma_dois_ints(2,3), 5, "soma 2+3");
char s[] = "abc";
str_reverse(s);
assert_int_eq(strcmp(s,"cba")==0, 1, "reverse");
printf("OK: %d tests\n", tests_passed);
}
#endif
Atualizado