Estrutura Básica de um Programa C
Um programa em C é escrito em arquivos com a extensão .c. O sistema operacional identifica o ponto de entrada do programa através da função main. Todo código executável deve estar contido dentro desta função.
#include <stdio.h>
int main(void) {
printf("Bem-vindo ao mundo C!\n");
return 0;
}
A diretiva #include <stdio.h> é responsável por incluir a biblioteca padrão de entrada e saída, permitindo o uso de funções como printf. Note que todas as instruções devem ser finalizadas com ponto e vírgula (;) e os símbolos devem ser estritamente em inglês.
Tipos de Dados e Representação Binária
No nível do hardware, os dados são armazenados em bits (0 ou 1). Oito bits formam um byte (B). Para representar números negativos, a arquitetura moderna utiliza o sistema de complemento de dois.
- Inteiros:
short(2 bytes),int(4 bytes),long(4 ou 8 bytes dependendo da arquitetura). - Pontos Fluutantes:
float(4 bytes, precisão simples),double(8 bytes, precisão dupla). - Caracteres:
char(1 byte), armazena valores da tabela ASCII.
Variáveis e Formatação de Saída
Variáveis são espaços na memória reservados para armazenar dados. A nomenclatura deve iniciar com letra ou underscore, não pode conter espaços e não pode ser uma palavra-chave reservada.
#include <stdio.h>
int main(void) {
int idade_usuario = 25;
float altura = 1.75f;
char inicial_nome = 'A';
printf("Idade: %d, Altura: %.2f, Inicial: %c\n", idade_usuario, altura, inicial_nome);
return 0;
}
Especificadores de Formato
| Especificador | Descrição |
|---|---|
| %c | Caractere único |
| %d, %i | Inteiro com sinal (decimal) |
| %u | Inteiro sem sinal |
| %f | Ponto flutuante (float/double) |
| %e, %E | Notação científica |
| %x, %X | Hexadecimal sem sinal |
| %o | Octal sem sinal |
| %s | String (array de caracteres) |
| %p | Ponteiro |
Conversão de Tipos
A conversão pode ser implícita (promovida automaticamante pelo compilador, ex: int para double) ou explícita (cast forçado pelo programador).
#include <stdio.h>
int main(void) {
double pi = 3.14159;
int pi_inteiro = (int)pi; // Conversão explícita, perde a parte fracionária
printf("Valor truncado: %d\n", pi_inteiro);
return 0;
}
Operadores e Precedência
A linguagem C possui uma vasta gama de operadores: aritméticos (+, -, *, /, %), lógicos (&&, ||, !), bit a bit (&, |, ^, ~, <<, >>) e de atribuição.
Precedência de Opeardores (da maior para a menor)
| Operadores | Associatividade |
|---|---|
| () [] . | Esquerda para Direita |
| ! ~ ++ -- + - (tipo) * | Direita para Esquerda |
| * / % | Esquerda para Direita |
| + - | Esquerda para Direita |
| << >> | Esquerda para Direita |
| < <= > >= | Esquerda para Direita |
| == != | Esquerda para Direita |
| & | Esquerda para Direita |
| ^ | Esquerda para Direita |
| | | Esquerda para Direita |
| && | Esquerda para Direita |
| || | Esquerda para Direita |
| ? : | Direita para Esquerda |
| = += -= *= /= etc. | Direita para Esquerda |
Estruturas de Controle de Fluxo
Condicionais
#include <stdio.h>
int main(void) {
int nota = 85;
if (nota >= 90) {
printf("Conceito A\n");
} else if (nota >= 70) {
printf("Conceito B\n");
} else {
printf("Conceito C\n");
}
return 0;
}
Switch
#include <stdio.h>
int main(void) {
char opcao = 'B';
switch (opcao) {
case 'A': printf("Opção A selecionada\n"); break;
case 'B': printf("Opção B selecionada\n"); break;
default: printf("Opção inválida\n");
}
return 0;
}
Laços de Repetição
O for é ideal quando o número de iterações é conhecido. O while e do-while são usados quando a condição de parada depende de uma expressão dinâmica.
#include <stdio.h>
int main(void) {
for (int i = 0; i < 5; i++) {
if (i == 3) continue; // Pula a iteração quando i for 3
printf("%d ", i);
}
return 0;
}
Algoritmos Clássicos e Arrays
Números de Armstrong (Narcissistic)
Um número de Armstrong de 3 dígitos é aquele onde a soma dos cubos de seus dígitos é igual ao próprio número.
#include <stdio.h>
#include <math.h>
int main(void) {
for (int num = 100; num < 1000; num++) {
int dig1 = num / 100;
int dig2 = (num / 10) % 10;
int dig3 = num % 10;
if (pow(dig1, 3) + pow(dig2, 3) + pow(dig3, 3) == num) {
printf("%d\n", num);
}
}
return 0;
}
Ordenação Bolha (Bubble Sort)
#include <stdio.h>
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main(void) {
int dados[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(dados) / sizeof(dados[0]);
bubbleSort(dados, n);
for (int i = 0; i < n; i++) {
printf("%d ", dados[i]);
}
return 0;
}
Programação Dinâmica
Problema do Roubo de Casas (House Robber)
Dado um array de inteiros representando o dinheiro em cada casa, encontre o máximo que pode ser roubado sem roubar duas casas adjacentes.
#include <stdio.h>
int max(int a, int b) {
return (a > b) ? a : b;
}
int roubar(int casas[], int n) {
if (n == 0) return 0;
if (n == 1) return casas[0];
int dp[n];
dp[0] = casas[0];
dp[1] = max(casas[0], casas[1]);
for (int i = 2; i < n; i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + casas[i]);
}
return dp[n - 1];
}
int main(void) {
int casas[] = {2, 7, 9, 3, 1};
int n = sizeof(casas) / sizeof(casas[0]);
printf("Maximo roubado: %d\n", roubar(casas, n));
return 0;
}
Strings e Manipulação de Texto
Em C, strings são arrays de caracteres terminados pelo caractere nulo \0. Funções como scanf, fgets e puts são utilizadas para manipulação de entrada e saída.
Verificação de Palíndromo
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool ehPalindromo(char str[]) {
int esq = 0;
int dir = strlen(str) - 1;
while (esq < dir) {
if (str[esq] != str[dir]) return false;
esq++;
dir--;
}
return true;
}
int main(void) {
char texto[100];
fgets(texto, sizeof(texto), stdin);
texto[strcspn(texto, "\n")] = 0; // Remove a quebra de linha
printf(ehPalindromo(texto) ? "Eh palindromo\n" : "Nao eh palindromo\n");
return 0;
}
Algoritmo de Busca KMP
O algoritmo Knuth-Morris-Pratt (KMP) otimiza a busca de padrões em strings, evitando reavaliações de caracteres já comparados através de uma tabela de falhas.
#include <stdio.h>
#include <string.h>
void construirTabelaFalha(char *padrao, int tamPadrao, int *falha) {
falha[0] = 0;
int j = 0;
for (int i = 1; i < tamPadrao; i++) {
while (j > 0 && padrao[i] != padrao[j]) {
j = falha[j - 1];
}
if (padrao[i] == padrao[j]) {
j++;
}
falha[i] = j;
}
}
int main(void) {
char texto[] = "ababcabababc";
char padrao[] = "abababc";
int tamTxt = strlen(texto);
int tamPad = strlen(padrao);
int falha[tamPad];
construirTabelaFalha(padrao, tamPad, falha);
int i = 0, j = 0;
while (i < tamTxt) {
if (padrao[j] == texto[i]) {
i++;
j++;
}
if (j == tamPad) {
printf("Padrao encontrado no indice %d\n", i - j);
j = falha[j - 1];
} else if (i < tamTxt && padrao[j] != texto[i]) {
if (j != 0) {
j = falha[j - 1];
} else {
i++;
}
}
}
return 0;
}