Fundamentos Essenciais da Linguagem C e Estruturas de Dados

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;
}

Tags: c-language data-structures algorithms memory-management flow-control

Publicado em 8-31 23:52