Aula 1 - Introdução à Programação, Variáveis e Expressões
Nesta primeira aula veremos o que são algoritmos e programas, um pouco sobre a linguagem C e o pensamento computacional, e os primeiros elementos de um programa em C: variáveis, tipos de dados, memória, entrada e saída, expressões aritméticas e atribuição. Todo o restante da disciplina será construído em cima desses conceitos básicos.
1. O que é um algoritmo?
Um algoritmo é uma sequência finita e bem definida de passos para resolver um problema ou realizar uma tarefa. Não precisa envolver computadores: uma receita de bolo, um manual de montagem de móveis, ou as instruções para trocar um pneu são todos algoritmos.
Um bom algoritmo precisa ser:
- Finito: deve terminar após um número finito de passos;
- Preciso: cada passo deve ser claro, sem ambiguidade;
- Executável: cada passo deve poder ser realizado por quem (ou o que) for executar o algoritmo.
Por exemplo, um algoritmo para "somar dois números e mostrar o resultado" poderia ser descrito, em português estruturado, como:
1. Ler o primeiro número (A) 2. Ler o segundo número (B) 3. Calcular a soma: S = A + B 4. Mostrar o valor de S
2. Um algoritmo se torna um programa
Um programa é a implementação de um algoritmo em uma linguagem de programação — uma linguagem formal, com regras rígidas de sintaxe, que pode ser traduzida para instruções que o computador executa diretamente.
Nesta disciplina utilizaremos a linguagem C. Um programa em C passa por duas etapas até ser executado:
| Etapa | O que acontece |
|---|---|
| Compilação | O código-fonte (arquivo .c) é traduzido para um arquivo executável, através de um programa chamado compilador (usaremos o gcc). |
| Execução | O sistema operacional roda o arquivo executável gerado na compilação, seguindo as instruções do programa. |
Em linha de comando, isso é feito assim:
gcc -Wall main.c -o programa # compila main.c e gera o executável "programa" ./programa # executa o programa
Erros de compilação x erros de execução. Se o programa tiver um erro de sintaxe (por exemplo, esquecer um ;), o compilador se recusa a gerar o executável, e o erro aparece antes de qualquer execução. Já um erro de lógica (o programa compila, mas calcula algo errado) só é percebido durante a execução, observando a saída do programa.
|
3. Pensamento computacional
Resolver um problema com o auxílio de um computador envolve muito mais do que simplesmente escrever código. O desenvolvimento de um programa costuma seguir um processo com várias etapas:
Problema ↓ Análise ↓ Algoritmo ↓ Programa em C ↓ Compilação ↓ Execução ↓ Testes ↓ Correções
Um erro comum de quem está começando é tentar escrever código diretamente, sem antes pensar no algoritmo. Ao longo desta disciplina, o foco não estará apenas em "aprender a sintaxe de C", mas em desenvolver a capacidade de analisar problemas e escolher a lógica e as representações mais adequadas para resolvê-los.
4. A linguagem C
A linguagem C foi desenvolvida no início da década de 1970 por Dennis Ritchie, e permanece, até hoje, uma das linguagens mais importantes da computação. Entre suas principais características, destacam-se:
- Linguagem compilada (o código-fonte é totalmente traduzido para linguagem de máquina antes da execução);
- Linguagem imperativa (o programa é descrito como uma sequência de instruções que alteram o estado da memória);
- Alto desempenho;
- Controle explícito da memória (o programador decide, em muitos casos, como e quando a memória é utilizada);
- Grande portabilidade entre sistemas operacionais.
Essas características tornam C uma excelente linguagem para compreender como os dados são, de fato, representados e manipulados na memória do computador — algo que ficará mais claro já na próxima seção.
5. Nosso primeiro programa em C
Todo programa em C precisa de uma função main(), que é o ponto onde a execução começa:
#include <stdio.h> int main() { printf("Ola, mundo!\n"); return 0; }
| Elemento | Significado |
|---|---|
#include <stdio.h> |
Inclui a biblioteca padrão de entrada e saída (standard input/output), necessária para usar printf e scanf. |
int main() |
Declara a função principal do programa, que retorna um int (o código de encerramento do programa). |
printf(...) |
Imprime um texto na tela. O \n ao final representa uma quebra de linha. |
return 0; |
Encerra a função main(), informando ao sistema operacional que o programa terminou sem erros (por convenção, 0 significa sucesso). |
6. Variáveis, tipos de dados e memória
Uma variável representa uma posição da memória do computador, identificada por um nome, usada para armazenar um valor que pode mudar ao longo da execução do programa. Sempre que uma variável é declarada, o compilador reserva um espaço na memória suficiente para armazenar um valor daquele tipo.
Toda variável possui quatro características principais:
| Atributo | Descrição |
|---|---|
| Identificador | Nome utilizado para acessar a variável no programa. |
| Tipo | Define quais valores podem ser armazenados e quanto espaço será reservado na memória. |
| Endereço | Posição da memória onde a variável está armazenada. |
| Valor | Conteúdo atualmente armazenado na variável. |
O tipo de uma variável determina que espécie de valor ela pode guardar, e quanto espaço ela ocupa na memória:
| Tipo | Armazena | Tamanho típico | Exemplo de valor |
|---|---|---|---|
char |
Um único caractere | 1 byte | 'A', '7', '\n' |
int |
Números inteiros | 4 bytes | 10, -3, 0 |
float |
Números reais (ponto flutuante, precisão simples) | 4 bytes | 3.14, -0.5 |
double |
Números reais (ponto flutuante, precisão dupla — mais preciso que float) |
8 bytes | 3.14159265 |
A sintaxe para declarar uma variável é tipo nome;, opcionalmente já atribuindo um valor inicial:
int idade; // declaração, sem valor inicial int ano = 2026; // declaração com valor inicial float altura = 1.75; char inicial = 'D';
Nomes de variáveis. Um nome de variável em C pode conter letras, dígitos e _ (nunca começando por dígito), e é sensível a maiúsculas/minúsculas (idade e Idade são variáveis diferentes). Prefira nomes descritivos (media, numeroAlunos) em vez de nomes genéricos (x, a1), exceto em contadores simples de laços (que veremos na Aula 3).
|
Como as variáveis ficam na memória
Considere o seguinte programa:
#include <stdio.h> int main() { int idade = 20; float nota = 8.5; char sexo = 'F'; double media = 7.35; return 0; }
Durante a execução, o compilador reserva espaço para cada variável, uma após a outra. A tabela abaixo apresenta uma representação simplificada da memória (os endereços são fictícios e usados apenas para fins didáticos):
| Endereço | Identificador | Tipo | Tamanho | Valor |
|---|---|---|---|---|
E1000 |
idade |
int |
4 bytes | 20 |
E1004 |
nota |
float |
4 bytes | 8.5 |
E1008 |
sexo |
char |
1 byte | 'F' |
E1009 |
media |
double |
8 bytes | 7.35 |
Podemos representar essa mesma situação através de um mapa simplificado da memória:
Endereço Conteúdo E1000 ───► idade = 20 E1004 ───► nota = 8.5 E1008 ───► sexo = 'F' E1009 ───► media = 7.35
Note que variáveis de tipos diferentes ocupam quantidades diferentes de memória, e o endereço de cada variável depende de quantos bytes a variável anterior ocupou. Como idade (int) ocupa 4 bytes, nota começa 4 bytes depois. Como sexo (char) ocupa apenas 1 byte, a variável seguinte já começa logo em seguida.
Observação sobre a notação de endereços. Os endereços apresentados aqui são apenas exemplos ilustrativos — em um programa real, o sistema operacional e o compilador escolhem automaticamente onde cada variável será armazenada. Na prática, endereços de memória são normalmente representados em hexadecimal (com o prefixo 0x, como 0x1000). Para não misturar essa conversão de base com os conceitos de memória que estamos aprendendo agora, vamos usar aqui uma notação simplificada, em decimal: a letra E seguida de um número, como E1000 — lendo-se "endereço 1000". Assim, as contas de "quanto cada variável ocupa" podem ser feitas em decimal, sem a etapa extra de converter para hexadecimal.
|
Além do valor armazenado, também é possível manipular diretamente o endereço de uma variável, através de ponteiros. Esse conceito será estudado com detalhes mais adiante na disciplina, e é fundamental para entender listas encadeadas, árvores e diversas outras estruturas de dados.
7. Entrada e saída
Para que um programa seja útil, ele geralmente precisa ler dados fornecidos por quem o executa, e mostrar resultados na tela. Em C, isso é feito com scanf() (entrada) e printf() (saída), ambas definidas em <stdio.h>.
printf: escrevendo na tela
Sintaxe genérica:
printf("texto e especificadores de formato", valor1, valor2, ...);
Ou seja, printf recebe sempre uma string de formatação — um texto entre aspas, podendo conter especificadores de formato (que começam com %) — seguida de uma variável (ou valor) para cada especificador, na mesma ordem em que aparecem no texto:
| Especificador | Usado para |
|---|---|
%d |
int |
%f |
float ou double |
%c |
char |
%s |
string (veremos com detalhe na Aula 5) |
Exemplo:
int idade = 20; float altura = 1.75; printf("Idade: %d anos\n", idade); printf("Altura: %.2f m\n", altura); // %.2f: duas casas decimais
O texto e os especificadores "casam" na ordem. O primeiro % encontrado no texto corresponde ao primeiro valor depois da vírgula, o segundo % corresponde ao segundo valor, e assim por diante. Se um printf tiver dois especificadores, ele precisa vir acompanhado de exatamente duas variáveis (ou valores), na ordem certa — como em printf("%d anos e %.2f m\n", idade, altura);.
|
scanf: lendo do teclado
Sintaxe genérica:
scanf("especificadores de formato", &variavel1, &variavel2, ...);
A ideia é parecida com a do printf — um especificador de formato para cada valor a ser lido — mas com duas diferenças importantes: a string de formatação normalmente não traz texto comum (só os especificadores, como "%d" ou "%d %f"), e cada variável precisa vir acompanhada do operador & (endereço de) antes do seu nome:
#include <stdio.h> int main() { int idade; printf("Digite sua idade: "); scanf("%d", &idade); printf("Voce tem %d anos.\n", idade); return 0; }
Por que o &? O & antes do nome da variável em scanf informa o endereço de memória daquela variável, e não seu valor. É assim que scanf consegue "escrever" o valor lido diretamente na variável do programa. Esse conceito de endereço de memória será aprofundado mais adiante na disciplina, quando estudarmos ponteiros.
|
Também é possível ler vários valores em uma única chamada, um especificador para cada variável (na mesma ordem):
int dia, mes, ano; printf("Digite a data (dia mes ano): "); scanf("%d %d %d", &dia, &mes, &ano);
8. Expressões aritméticas
C oferece os operadores aritméticos usuais, que podem ser combinados em expressões:
| Operador | Significado | Exemplo |
|---|---|---|
+ |
Adição | 5 + 2 (resultado: 7) |
- |
Subtração | 5 - 2 (resultado: 3) |
* |
Multiplicação | 5 * 2 (resultado: 10) |
/ |
Divisão | 5 / 2 (resultado: 2, veja abaixo) |
% |
Resto da divisão inteira (módulo) | 5 % 2 (resultado: 1) |
Cuidado com a divisão inteira! Quando ambos os operandos de / são int, o resultado também é um int, e a parte decimal é descartada (não arredondada): 5 / 2 vale 2, não 2.5. Para obter um resultado com casas decimais, pelo menos um dos operandos precisa ser float ou double, como em 5.0 / 2 (que vale 2.5).
|
Escrever 5.0 em vez de 5, como no exemplo acima, é uma forma de conversão de tipo: em vez de deixar o 5 como int, nós o escrevemos diretamente como um valor double. Também é possível converter o conteúdo de uma variável (e não apenas um número escrito na mão) de um tipo para outro, no meio de uma expressão, através do operador de cast (algo como "trocar o tipo" só durante aquele cálculo, sem alterar a variável original):
int a = 5, b = 2; float resultado; resultado = a / b; // 2.0 -> divisão inteira acontece antes da atribuição! resultado = (float) a / b; // 2.5 -> "a" é tratado como float só nesta conta
Cast: uma conversão temporária, só para aquela expressão. A sintaxe (tipo) expressao — o nome do tipo desejado entre parênteses, logo antes do valor — pega o resultado daquela expressão e o trata como se fosse do tipo indicado, sem alterar a variável original. Em (float) a / b, a variável a continua sendo int depois dessa linha; apenas o valor usado naquela divisão foi convertido para float — o suficiente para que a divisão inteira (Aula 1) deixe de acontecer, já que agora um dos operandos não é mais int.
|
Assim como na matemática, expressões seguem uma ordem de precedência: multiplicação, divisão e módulo são calculados antes de soma e subtração, e parênteses () podem ser usados para forçar uma ordem diferente:
int resultado1 = 2 + 3 * 4; // 2 + 12 = 14 (multiplicação primeiro) int resultado2 = (2 + 3) * 4; // 5 * 4 = 20 (parênteses primeiro)
9. Atribuição
O operador = em C é o operador de atribuição: ele calcula o valor da expressão à direita, e o guarda na variável à esquerda. Não deve ser confundido com o "igual" da matemática — em C, x = x + 1; é uma instrução perfeitamente válida, que significa "calcule x + 1 e guarde o resultado de volta em x", e não uma equação sem solução.
Por que não usar um símbolo diferente do "igual" da matemática? Alguns livros e pseudocódigos, para evitar exatamente essa confusão, representam a atribuição com uma seta (x ← x + 1), deixando mais claro que é um valor "fluindo" da direita para a variável à esquerda, e não uma igualdade nos dois sentidos. C (assim como a maioria das linguagens de programação) optou por reutilizar o = por simplicidade de teclado, mas é útil, sempre que tiver dúvida, ler x = x + 1; mentalmente como x ← x + 1 — "o novo x recebe o valor de x + 1" — e nunca como uma equação a ser resolvida.
|
int x = 5; x = x + 1; // agora x vale 6
Um outro exemplo comum é a atribuição entre duas variáveis já existentes — nesse caso, o valor da variável à direita é copiado para a variável à esquerda; as duas passam a ter o mesmo valor, mas continuam sendo variáveis (e espaços de memória) independentes:
int a = 10; int b = 20; a = b; // copia o valor de b para a printf("%d\n", a); // imprime 20
| Variável | Valor inicial | Valor final |
|---|---|---|
a |
10 | 20 |
b |
20 | 20 |
Atribuição (=) x comparação (==). Não confunda o operador de atribuição com o operador de comparação, que veremos na próxima aula. O primeiro altera o valor de uma variável; o segundo apenas verifica se dois valores são iguais, sem alterar nada.
|
Como esse padrão de "atualizar uma variável com base em seu próprio valor" é muito comum, C oferece operadores de atribuição compostos, que combinam uma operação aritmética com a atribuição:
| Operador composto | Equivale a |
|---|---|
x += 1; |
x = x + 1; |
x -= 2; |
x = x - 2; |
x *= 3; |
x = x * 3; |
x /= 2; |
x = x / 2; |
Um caso particularmente comum é somar ou subtrair exatamente 1, para o qual existem os operadores de incremento (++) e decremento (--):
int x = 5; x++; // equivale a x = x + 1; agora x vale 6 x--; // equivale a x = x - 1; agora x vale 5 de novo
Esses operadores serão especialmente úteis na Aula 3, ao estudarmos estruturas de repetição.
10. Programa completo de exemplo
Juntando tudo o que vimos, um programa que lê dois números inteiros e mostra sua soma, média e resto da divisão:
#include <stdio.h> int main() { int a, b; int soma, resto; float media; printf("Digite o primeiro numero: "); scanf("%d", &a); printf("Digite o segundo numero: "); scanf("%d", &b); soma = a + b; media = (a + b) / 2.0; // 2.0 força divisão com casas decimais resto = a % b; printf("Soma: %d\n", soma); printf("Media: %.2f\n", media); printf("Resto de %d dividido por %d: %d\n", a, b, resto); return 0; }
Resumo
|
Exercícios
Nível fácil
-
Escreva um programa que declare uma variável
intcom o valor25e imprima essa idade na tela, no formato"Idade: 25 anos". - Escreva um programa que leia dois números inteiros do teclado e imprima a soma, a subtração e a multiplicação entre eles.
-
Qual é o resultado de cada expressão abaixo considerando as conversões da linguagem C?
7 / 27.0 / 27 % 22 + 3 * 2
Nível médio
-
Escreva um programa que leia o preço de um produto (
float) e a quantidade comprada (int), e imprima o valor total da compra, com duas casas decimais. -
Escreva um programa que leia um valor em minutos (
int) e converta para horas e minutos (por exemplo, 125 minutos = 2 horas e 5 minutos), utilizando divisão inteira e o operador%. - Escreva um programa que leia três números inteiros e calcule sua média aritmética, imprimindo o resultado com duas casas decimais. Tome cuidado para o resultado não ser truncado por uma divisão inteira.
Nível difícil
-
Escreva um programa que leia um valor em segundos (
int) e o converta para o formatoHH:MM:SS(horas, minutos e segundos), imprimindo, por exemplo,"01:05:23"para uma entrada de 3923 segundos. Use apenas divisão inteira e módulo. -
Escreva um programa que leia um valor em reais (
float) e simule um "trocador de moedas": calcule quantas notas de 100, 50, 20, 10, 5 e 1 real são necessárias para compor esse valor, utilizando o menor número possível de notas em cada etapa (dica: utilize divisão inteira e módulo repetidamente, e converta a parte decimal para inteiro no início).
Sugestões de Respostas dos Exercícios
Exercício 1
#include <stdio.h> int main() { int idade = 25; printf("Idade: %d anos\n", idade); return 0; }
Exercício 2
#include <stdio.h> int main() { int a, b; printf("Digite o primeiro numero: "); scanf("%d", &a); printf("Digite o segundo numero: "); scanf("%d", &b); printf("Soma: %d\n", a + b); printf("Subtracao: %d\n", a - b); printf("Multiplicacao: %d\n", a * b); return 0; }
Exercício 3
7 / 2vale3(divisão inteira: descarta a parte decimal de3.5).7.0 / 2vale3.5(um dos operandos éfloat, então o resultado tem casas decimais).7 % 2vale1(resto da divisão de 7 por 2).2 + 3 * 2vale8(a multiplicação3 * 2 = 6é feita antes da soma:2 + 6 = 8).
Exercício 4
#include <stdio.h> int main() { float preco; int quantidade; printf("Preco do produto: "); scanf("%f", &preco); printf("Quantidade: "); scanf("%d", &quantidade); printf("Total: %.2f\n", preco * quantidade); return 0; }
Note que preco * quantidade é uma multiplicação entre float e int; C converte automaticamente o int para float antes de multiplicar, então o resultado já vem com casas decimais.
Exercício 5
#include <stdio.h> int main() { int minutos, horas, minutosRestantes; printf("Digite os minutos: "); scanf("%d", &minutos); horas = minutos / 60; // divisão inteira: quantas horas cabem minutosRestantes = minutos % 60; // o que sobra depois das horas printf("%d minutos = %d hora(s) e %d minuto(s)\n", minutos, horas, minutosRestantes); return 0; }
Exercício 6
#include <stdio.h> int main() { int n1, n2, n3; float media; printf("Digite tres numeros: "); scanf("%d %d %d", &n1, &n2, &n3); media = (n1 + n2 + n3) / 3.0; // 3.0 evita a divisão inteira printf("Media: %.2f\n", media); return 0; }
Se tivéssemos escrito (n1 + n2 + n3) / 3 (com 3 em vez de 3.0), como todos os operandos seriam int, o resultado sofreria divisão inteira antes de ser guardado em media — perdendo a parte decimal mesmo a variável de destino sendo float.
Exercício 7
#include <stdio.h> int main() { int totalSegundos, horas, minutos, segundos; printf("Digite o total de segundos: "); scanf("%d", &totalSegundos); horas = totalSegundos / 3600; minutos = (totalSegundos % 3600) / 60; segundos = totalSegundos % 60; printf("%02d:%02d:%02d\n", horas, minutos, segundos); return 0; }
A ideia é isolar cada unidade de tempo em etapas: totalSegundos / 3600 dá as horas completas; totalSegundos % 3600 descarta as horas já contadas, sobrando os segundos "dentro da hora atual", que divididos por 60 dão os minutos; e totalSegundos % 60 dá os segundos restantes dentro do minuto atual. O especificador %02d imprime o número sempre com 2 dígitos, completando com zero à esquerda se necessário (por exemplo, 05 em vez de 5).
Exercício 8
#include <stdio.h> int main() { float valor; int centavos; int notas100, notas50, notas20, notas10, notas5, notas1; printf("Digite o valor em reais: "); scanf("%f", &valor); centavos = (int) valor; // considera apenas a parte inteira dos reais notas100 = centavos / 100; centavos = centavos % 100; notas50 = centavos / 50; centavos = centavos % 50; notas20 = centavos / 20; centavos = centavos % 20; notas10 = centavos / 10; centavos = centavos % 10; notas5 = centavos / 5; centavos = centavos % 5; notas1 = centavos; printf("Notas de 100: %d\n", notas100); printf("Notas de 50: %d\n", notas50); printf("Notas de 20: %d\n", notas20); printf("Notas de 10: %d\n", notas10); printf("Notas de 5: %d\n", notas5); printf("Notas de 1: %d\n", notas1); return 0; }
A conversão (int) valor descarta a parte decimal de valor, garantindo que trabalhemos apenas com reais inteiros (o exercício não pede o troco em centavos). A partir daí, cada etapa repete o mesmo padrão de divisão inteira (para saber quantas notas daquele valor cabem) seguida de módulo (para saber o que ainda falta compor), sempre da nota de maior valor para a de menor valor.