A integração entre a linguagem C e sistemas operacionais baseados em Unix, especialmente Linux, não é acidental — é uma relação estrutural. O kernel do Linux é escrito majoritariamente em C, e ferramentas essenciais como o compilador GCC, o depurador GDB e o sistema de construção Make foram desenvolvidos e otimizados nesse ecossistema. Dominar C dentro desse contexto permite compreender não apenas sintaxe, mas também os mecanismos subjacentes de alocação de memória, chamadas de sistema e representação binária de dados.
Representação de Dados e Sistemas Numéricos
Tipos Numéricos Básicos
Na linguagem C, dados numéricos são categorizados principalmente em dois grupos: inteiros e de ponto flutuante. Essa distinção reflete diretamente as limitações e capacidades do hardware, onde operações inteiras são nativas e eficientes, enquanto cálculos com frações exigem tratamento especializado via FPU ou bibliotecas de software.
- Inteiros:
char(1 byte),short(mínimo 2 bytes),int(tipicamente 4 bytes),long(4 ou 8 bytes conforme arquitetura),long long(C99+, mínimo 8 bytes) - Ponto flutuante:
float(precisão simples, ~7 dígitos decimais),double(precisão dupla, ~15 dígitos),long double(varia por plataforma)
Conversões de Tipo
A conversão entre tipos pode ocorrer de forma implícita ou explícita. A coerção implícita segue regras de promoção aritmética (ex.: int + float → float), enquanto a conversão explícita — ou cast — exige notação entre parênteses e pode ocasionar perda de informação:
int valor_inteiro = 42;
double valor_duplo = valor_inteiro; // Implícito: seguro
int truncado = (int)3.14159; // Explícito: parte fracionária descartada
unsigned char byte = (unsigned char)(-1); // Conversão com interpretação modular
Sistemas de Numeração e Conversões
Computadores operam internamente em base 2 (binário), mas programadores utilizam bases auxiliares para legibilidade: octal (base 8) e hexadecimal (base 16). Cada dígito hexadecimal representa exatamente 4 bits, permitindo compressão visual eficaz de valores binários.
| Base | Símbolos | Notação em C | Exemplo |
|---|---|---|---|
| Binária | 0, 1 | 0b1010 (C11+) |
0b11000011 → 195 |
| Octal | 0–7 | 0173 (prefixo 0) |
0303240 → 100000 |
| Decimal | 0–9 | não requer prefixo | 100000 |
| Hexadecimal | 0–9, A–F | 0x186A0 |
0x186A0 → 100000 |
Conversões práticas:
- Decimal → Binário: divisão sucessiva por 2, coletando restos do fim ao início.
- Binário → Hexadecimal: agrupar bits em blocos de 4 da direita para esquerda, substituindo cada grupo por seu equivalente hexa (ex.:
1001 0110 1100→0x96C). - Hexadecimal → Decimal: multiplicar cada dígito por potências crescentes de 16, começando da direita (
0x2C1= 2×256 + 12×16 + 1 = 705).
Elementos Léxicos da Linguagem
Palavras-Chave Reservadas
O padrão C define um conjunto fixo de identificadores reservados que não podem ser usados como nomes de variáveis ou funções. Exemplos incluem:
- Controle de fluxo:
if,else,while,for,switch,break,continue - Tipos de dados:
int,char,float,void,signed,unsigned - Modificadores:
static,extern,const,volatile,typedef
Identificadores Válidos
Regras para nomear variáveis, funções e tipos:
- Começam com letra ou sublinhado (
_) - Contêm apenas letras, dígitos e
_ - São sensíveis a maiúsculas/minúsculas
- Não coincidem com palavras-chave
Exemplos válidos: contador, _valor_max, buffer_2024. Inválidos: 2nd_try, my-var, int.
Operadores e Suas Prioridades
Aritméticos
Incluem operações básicas e incremento/decremento. Note que / entre inteiros realiza divisão inteira (truncamento em direção a zero), e % exige operandos inteiros.
int x = 17, y = 5;
int quociente = x / y; // 3
int resto = x % y; // 2
x++; // x agora é 18
Lógicos e Relacionais
Operadores relacionais (==, !=, <, >, etc.) retornam 0 (falso) ou 1 (verdadeiro). Os operadores lógicos && e || aplicam avaliação de curto-circuito: se o resultado puder ser determinado pelo primeiro operando, o segundo não é avaliado.
int idade = 25;
int acesso = (idade >= 18) && (idade <= 65); // 1
// Avaliação de curto-circuito:
int *ptr = NULL;
if (ptr != NULL && ptr->ativo == 1) { /* seguro */ }
Bit a Bit
Operações que manipulam diretamente os bits individuais de um valor inteiro. São fundamentais em programação de sistemas, drivers e protocolos.
&: AND bit a bit — útil para mascaramento (extrair bits)|: OR bit a bit — útil para ativar bits^: XOR bit a bit — útil para alternar bits ou troca sem variável auxiliar~: NOT bit a bit — inversão de todos os bits<<,>>: deslocamentos — equivalentes a multiplicações/divisões por potências de 2
Exemplo de troca segura usando XOR:
int a = 12, b = 10;
a ^= b; // a = a ^ b
b ^= a; // b = b ^ (a ^ b) = a
a ^= b; // a = (a ^ b) ^ a = b
// Agora a == 10, b == 12
Operador Condicional (Ternário)
Fornece uma forma concisa de expressar seleção binária:
int maior = (x > y) ? x : y;
// Equivalente a:
// if (x > y) maior = x; else maior = y;
Precedência de Operadores
A ordem de avaliação segue uma hierarquia bem definida. Em caso de dúvida, use parênteses para tornar a intenção explícita. A sequência geral (da mais alta para a mais baixa) é:
- Operadores unários (
++,--,!,~,+,-) - Multiplicativos (
*,/,%) - Aditivos (
+,-) - Deslocamentos (
<<,>>) - Relacionais (
<,<=,>,>=) - Igualdade (
==,!=) - AND bit a bit (
&) - XOR bit a bit (
^) - OR bit a bit (
|) - Lógicos AND (
&&) - Lógicos OR (
||) - Condicional (
?:) - Atribuição (
=,+=,-=, etc.)