Skip to main content


Introdução ao C


A linguagem C é uma das mais influentes e amplamente utilizadas na história da programação. Criada por Dennis Ritchie no início da década de 1970 nos laboratórios Bell, C foi projetada para desenvolvimento de sistemas operacionais, sendo usada para escrever o UNIX. Sua popularidade se deve à combinação de eficiência, flexibilidade e poder de abstração moderado, tornando-a ideal tanto para programação de baixo nível, próxima ao hardware, quanto para desenvolvimento de aplicações de alto desempenho.


C é uma linguagem compilada, estruturada e fortemente tipada, o que significa que o código-fonte precisa ser traduzido para código de máquina antes da execução e que as variáveis devem ser declaradas com tipos específicos. Sua sintaxe influenciou muitas outras linguagens modernas, como C++, Java, C# e Python. Uma característica importante de C é a manipulação direta de memória por meio de ponteiros, permitindo um controle refinado sobre o hardware, algo essencial para sistemas operacionais, drivers e softwares embarcados.


O modelo de programação em C é baseado em funções, permitindo a modularização do código e a reutilização eficiente de trechos de código. Além disso, a linguagem oferece uma rica biblioteca padrão, que inclui funções para manipulação de strings, entrada e saída, operações matemáticas e gerenciamento de arquivos. No entanto, diferentemente de linguagens mais modernas, C não possui gerenciamento automático de memória, exigindo que o programador aloque e libere memória manualmente para evitar vazamentos.



Orientação a Objeto


A linguagem C não é orientada a objetos. Ela é uma linguagem estruturada e procedural, onde o foco está em funções e no fluxo sequencial do programa. Em C, os dados e as funções são separados, e não há suporte nativo para conceitos como classes, herança e polimorfismo, que são fundamentais na programação orientada a objetos (POO).


No entanto, é possível simular alguns aspectos da POO em C, usando structs para organizar dados e ponteiros para funções para criar um comportamento semelhante a métodos. Essa abordagem é usada, por exemplo, na implementação de bibliotecas e sistemas complexos, como o kernel do Linux. Mas, diferentemente de C++, que possui suporte completo a POO, C exige mais esforço para alcançar um modelo orientado a objetos.



Compilador C


Para compilar programas em C no Linux, o compilador mais utilizado é o GCC (GNU Compiler Collection). A instalação pode ser feita facilmente pelo gerenciador de pacotes da sua distribuição. No Ubuntu, Debian e derivados, use o seguinte comando no terminal:

Terminal
sudo apt update && sudo apt install gcc -y

O comando acima instalará o GCC e todas as dependências necessárias para compilar programas em C.


Após a instalação, verifique se o GCC está instalado corretamente com:

Terminal
gcc --version

Se o comando retornar a versão do compilador, significa que a instalação foi concluída com sucesso. Agora você já pode compilar seus programas em C usando o comando abaixo:

Terminal
# Para compilar o código faça:
gcc nome_do_arquivo.c -o nome_do_executavel

# Para executar o código, faça:
./nome_do_executavel


Primeiro programa em C


Como de costume vamos criar um Hello Word como nosso primeiro programa.

helloword.c
#include <stdio.h>

int main() {
printf("Hello, World!\n");
return 0;
}

Terminal
# Para compilar o código faça:
gcc helloword.c -o helloword

# Para executar o código, faça:
./helloword

O código acima inclui a biblioteca stdio.h, que contém a função printf, usada para exibir a mensagem na tela. O main() é a função principal do programa, e return 0 indica que o programa terminou com sucesso.


Resultado do código acima

Hello, World!


Biblioteca stdio.h

A biblioteca stdio.h (Standard Input/Output Header) é um cabeçalho da linguagem C que contém funções para manipulação de entrada e saída padrão, como leitura e escrita no terminal e em arquivos.

Ela fornece funções essenciais, como:

  • printf() para exibir texto na tela.
  • scanf() para ler entrada do usuário.
  • putchar() e puts() para imprimir caracteres e strings.
  • getchar() e gets() (embora gets() seja insegura e obsoleta) para capturar entrada.
  • Funções de manipulação de arquivos, como fopen(), fclose(), fgets(), fprintf(), entre outras.

Sempre que um programa em C precisar lidar com entrada ou saída de dados, a biblioteca stdio.h será necessária. Ela é incluída no código usando #include <stdio.h>, permitindo o uso dessas funções.



Variáveis


As variáveis são espaços na memória usados para armazenar valores que podem ser modificados durante a execução do programa. Cada variável tem um tipo, um nome e pode armazenar diferentes tipos de dados.


Para declarar uma variável, especificamos o tipo seguido do nome:

int idade;   // Declaração
idade = 25; // Atribuição

Também podemos declarar e inicializar ao mesmo tempo:

int idade = 25;

Tipos de Variáveis


Em C, devemos declarar o tipo da variável porque a linguagem é fortemente e estaticamente tipada. Isso permite ao compilador reservar memória corretamente, detectar erros antes da execução, gerar código mais eficiente e garantir controle preciso sobre a memória, algo essencial em sistemas embarcados e no kernel Linux. Isso torna o C mais rápido e seguro, evitando verificações desnecessárias em tempo de execução.


Os tipos básicos de variáveis são:

  • int: Números inteiros (ex: 10, -3, 1000)
  • float: Números de ponto flutuante (ex: 3.14, -0.5)
  • double: Similar ao float, mas com mais precisão
  • char: Armazena um único caractere (ex: 'A', 'z')
  • void: Usado para indicar ausência de retorno em funções ou ausência de valor (para deixar mais simples).

O que significa Fortemente Tipada e Estaticamente Tipada?
Fortemente Tipada

Fortemente Tipada significa que C não converte automaticamente tipos incompatíveis. Se você tentar atribuir um número com casas decimais para uma variável do tipo int sem conversão explícita, pode perder dados. Exemplo:

int x = 3.5;  // Perde a parte decimal, pois 3.5 vira 3

Se quiser armazenar um float corretamente em um int, precisa converter explicitamente:

int x = (int)3.5;  // Conversão explícita (type casting)
Estaticamente Tipada

Estaticamente Tipada significa que o tipo das variáveis é definido em tempo de compilação e não pode mudar durante a execução. Uma variável declarada como int sempre será um int. Por exemplo:

int x = 10;
x = "teste"; // Erro: Não pode atribuir string a um int

Tipo VOID

O tipo void em C representa a ausência de valor. Ele é usado principalmente em três contextos:


  • Funções que não retornam valor
    Se uma função não precisa retornar nada, ela é declarada com void.
    void mensagem() {
    printf("Olá, mundo!\n");
    }
    Aqui, mensagem() não retorna nada, então usamos void no lugar de um tipo de retorno como int ou float.

  • Ponteiros genéricos (void *)
    Um void * pode apontar para qualquer tipo de dado. Isso é útil para alocação dinâmica de memória:
    void *ptr;
    int x = 10;
    ptr = &x; // O ponteiro void pode armazenar qualquer endereço
    Como um void * não tem um tipo específico, ao acessá-lo precisamos fazer um type casting:
    printf("%d\n", *(int *)ptr);  // Convertendo para int* antes de acessar o valor

  • Parâmetros vazios em funções
    Em versões antigas do C, int funcao(); significava que a função poderia receber qualquer número de argumentos. Para indicar que uma função não aceita parâmetros, usamos void:
    int funcao(void);  // Garante que a função não aceita argumentos

Modificadores de Tipo


Podemos modificar os tipos de variáveis para alterar sua capacidade e comportamento:

  • short: Reduz o tamanho de um inteiro (short int x;)
  • long: Aumenta o tamanho do inteiro (long int y;)
  • unsigned: Apenas valores positivos (unsigned int z;)

Exemplo de Uso:

#include <stdio.h>

int main() {
int numero = 10;
float pi = 3.14;
char letra = 'A';

printf("Numero: %d\n", numero);
printf("Valor de Pi: %.2f\n", pi);
printf("Letra: %c\n", letra);

return 0;
}

Resultado do código acima

Numero: 10 Valor de Pi: 3.14 Letra: A



Tipo bool


O tipo bool não existe nativamente no C padrão (C89/C90). No entanto, a partir do C99, foi introduzido na biblioteca stdbool.h.


Para usá-lo, basta incluir:

#include <stdbool.h>

Isso define bool como um alias para int, onde:

  • true é representado por 1
  • false é representado por 0

Como C trata qualquer valor diferente de 0 como verdadeiro (true), o uso de bool ajuda a tornar o código mais legível.



Especificadores de Formato


Os especificadores de formato são usados para exibir e ler valores formatados em C. Eles servem principalmente para exibir valores na tela, ler valores do usuário e trabalhar com formatação de números.


Os principais especificadores de formato são:

EspecificadorTipo de dadoExemplo
%dInteiro decimal (int)printf("Número: %d", 10);
%iInteiro (equivalente a %d)printf("Número: %i", 10);
%fNúmero de ponto flutuante (float/double)printf("Valor: %f", 3.14);
%.xfDefine casas decimais (x indica quantas)printf("%.2f", 3.14159); // Exibe: 3.14
%cCaractere (char)printf("Letra: %c", 'A');
%sString (char array)printf("Nome: %s", "Testando");
%uInteiro sem sinal (unsigned int)printf("%u", 3000000000);
%pPonteiro (endereço de memória)printf("Endereço: %p", ptr);
%x / %XInteiro em hexadecimal (minúsculo/maiúsculo)printf("%x", 255); // Saída: ff
%oInteiro em octalprintf("%o", 255); // Saída: 377


Estruturas de decisão


No C, existem três principais estruturas de decisão, que permitem executar diferentes blocos de código com base em condições. São elas:

  • IF
    Usado para avaliar condições booleanas e executar diferentes blocos de código com base no resultado.

  • ? :
    Forma reduzida de um if-else quando há apenas uma operação a ser realizada.

  • switch
    Alternativa ao if-else quando há múltiplas possibilidades baseadas em um único valor.



IF, ELSE e ELSE IF


No C, as estruturas condicionais if, else if e else são usadas para tomar decisões com base em condições booleanas. A sintaxe básica é:

if (condição) {
// Bloco de código executado se a condição for verdadeira
} else if (outra_condição) {
// Bloco de código executado se a primeira condição for falsa e esta for verdadeira
} else {
// Bloco de código executado se nenhuma das condições anteriores for verdadeira
}

Abaixo podemos conferir um exemplo prático.

#include <stdio.h>

int main() {
int num = 10;

if (num > 10) {
printf("O número é maior que 10.\n");
} else if (num == 10) {
printf("O número é igual a 10.\n");
} else {
printf("O número é menor que 10.\n");
}

return 0;
}


if-else reduzido


#include <stdio.h>

int main() {
int numero = 5;
printf("%s\n", (numero % 2 == 0) ? "Par" : "Ímpar");
return 0;
}


Switch


A estrutura switch é usada quando há múltiplas condições a serem avaliadas para um mesmo valor. É uma alternativa ao uso excessivo de if-else if-else.

switch (expressão) {
case valor1:
// Código a ser executado se expressão == valor1
break;
case valor2:
// Código a ser executado se expressão == valor2
break;
default:
// Código executado se nenhum dos casos acima for verdadeiro
}

Abaixo podemos conferir um exemplo prático.

#include <stdio.h>

int main() {
int opcao = 2;

switch (opcao) {
case 1:
printf("Opção 1 selecionada.\n");
break;
case 2:
printf("Opção 2 selecionada.\n");
break;
case 3:
printf("Opção 3 selecionada.\n");
break;
default:
printf("Opção inválida.\n");
}

return 0;
}

O break evita que os casos seguintes sejam executados em efeito "cascata". O default funciona como um "else", executando caso nenhum case seja atendido.



Cuidado com a queda (fall-through)


Se um case não tiver um break, a execução continuará nos próximos case, mesmo que a condição não seja verdadeira. Exemplo:

switch (2) {
case 1:
printf("Caso 1\n");
case 2:
printf("Caso 2\n");
case 3:
printf("Caso 3\n");
}

Resultado do código acima

Caso 2 Caso 3


Para evitar isso, sempre use break, a menos que queira o efeito intencionalmente.



Fontes


https://www.inf.ufpr.br/roberto/ci067/03_edicao.html