Aprendendo C do zero, na prática

Um caminho passo a passo para quem nunca programou e quer entender de verdade o que acontece dentro do computador. Cada conceito vem com uma explicação simples, um exemplo que você pode rodar agora e um exercício para fixar.

Como usar este guia

Leia os capítulos na ordem: cada um usa o que foi aprendido no anterior. Em cada capítulo você vai encontrar sempre a mesma estrutura: uma ideia central explicada com analogias, programas completos para testar, um resumo e exercícios com dicas.

  1. Digite os exemplos. Digitar treina o olho e a mão, e os erros de digitação ensinam mais do que parece. Se estiver com pressa, use o botão Copiar de cada bloco de código e cole no seu ambiente de desenvolvimento.
  2. Mude o código. Troque números, textos, condições. Quebre de propósito e veja a mensagem de erro. Programar é experimentar.
  3. Erre sem medo. O computador não se ofende. Todo programador passa o dia corrigindo erros.
  4. Faça os exercícios antes de olhar as dicas. A dica só existe para desemperrar.

Como ler os blocos

O código aparece em janelas escuras com número de linha e um botão para copiar. As linhas mais claras são as que estão sendo explicadas naquele momento. A saída do programa aparece em uma janela preta, parecida com a do Windows, e o que a pessoa digita fica destacado.

Os avisos coloridos em cinza seguem um padrão fácil de reconhecer:

Mapa do curso

Clique em qualquer capítulo para começar por ele. Você também pode marcar capítulos como concluídos para acompanhar seu progresso.

O computador por dentro

Antes da primeira linha de código, vale conhecer a máquina que vai obedecer a você: como ela guarda informação, como executa instruções e por que precisamos de uma linguagem como o C.

O que você vai aprender
  • O que é um programa e um algoritmo
  • Para que servem CPU, RAM e disco
  • O que são bits e bytes
  • Como a memória é organizada
  • O que faz um compilador
  • Por que aprender C

O que é um programa?

Um programa é uma lista de instruções que o computador executa uma por uma, na ordem. O computador é rapidíssimo, mas totalmente literal: ele não tem bom senso e não adivinha o que você quis dizer. Só faz exatamente o que está escrito.

Veja um algoritmo para preparar macarrão instantâneo. Repare que cada passo é simples e que a ordem importa:

As peças principais

Todo computador, do celular ao servidor de um jogo online, tem as mesmas peças básicas trabalhando juntas:

Processador (CPU)Executa as instruções, bilhões por segundo.O cozinheiro que lê a receita e faz cada passo.
Memória RAMGuarda o que está sendo usado agora. Rápida, mas esquece tudo quando desliga.A bancada da cozinha, onde ficam os ingredientes em uso.
Armazenamento (SSD/HD)Guarda arquivos e programas de forma permanente. Mais lento que a RAM.A despensa, onde tudo fica guardado.
Entrada e saídaTeclado, mouse, microfone, tela, caixas de som.Os sentidos e a voz da máquina.

Quando você abre um jogo, ele é copiado do SSD para a RAM, e a CPU passa a executá-lo lendo instruções da RAM:

SSD / HDprograma guardado
▸
RAMprograma em uso
▸
CPUexecuta instruções
▸
Tela e somresultado

Tudo é 0 e 1

Um computador é feito de bilhões de minúsculos interruptores que só têm dois estados: ligado ou desligado. Cada interruptor é um bit (do inglês binary digit), que vale 0 ou 1. Um grupo de 8 bits forma um byte.

Com 1 bit temos 2 combinações. Com 2 bits, 4. Com 8 bits, 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 = 256 combinações, ou seja, um byte pode guardar valores de 0 a 255. Cada posição vale o dobro da anterior, da direita para a esquerda:

Posição8ª7ª6ª5ª4ª3ª2ª1ª
Valor1286432168421
Exemplo: 0100000101000001

No exemplo, somamos apenas as posições ligadas: 64 + 1 = 65. Experimente você mesmo. Clique nos bits e observe o número mudar:

Laboratório: um byte por dentro

Clique em cada bit para ligar (1) ou desligar (0). O número de baixo é a soma dos valores das posições ligadas.

Decimal0
Binário00000000
Hexadecimal0x00
Caractere ASCIIcontrole

Repare que o valor 65 aparece como a letra A. Isso não é coincidência: o computador guarda letras como números, seguindo uma tabela chamada ASCII. Imagens, músicas e vídeos também são apenas números muito longos. Para o computador, tudo é número.

Memória: uma rua com milhões de casas

A RAM é uma sequência enorme de bytes, e cada byte tem um endereço, como o número de uma casa em uma rua. Quando o programa precisa guardar algo, ele reserva algumas dessas casas e dá um nome a elas. Esse nome é o que chamaremos de variável.

Uma pequena parte da memória. Os endereços aqui são inventados, só para ilustrar.

Você não precisa decorar endereços: escolhe o nome (idade) e o computador cuida do resto. Nos capítulos 4 e 12 você vai ver isso na prática.

Da ideia ao programa: o compilador

A CPU só entende instruções em código de máquina, sequências de 0 e 1 que ninguém quer escrever à mão. Por isso criamos linguagens mais próximas do nosso jeito de pensar, como o C. Mas o processador não entende C, então precisamos de um tradutor: o compilador.

Código-fontearquivo .c que você escreve
▸
Compiladortraduz para código de máquina
▸
Executávelarquivo .exe
▸
CPUexecuta

Existem dois tipos de erro que você vai encontrar:

  • Erros de compilação: o compilador não consegue traduzir (faltou um ponto e vírgula, um nome está errado). O .exe nem chega a ser criado, e o compilador diz onde está o problema.
  • Erros de lógica: o programa compila e roda, mas faz a coisa errada (calculou uma média errada, por exemplo). Esses são os mais traiçoeiros, porque ninguém avisa.

Por que aprender C?

O C foi criado por Dennis Ritchie nos laboratórios Bell, no começo da década de 1970, e continua em uso mais de cinquenta anos depois. Há bons motivos:

  • É rápido e enxuto. Programas em C usam pouca memória e rodam muito depressa.
  • Mostra como o computador funciona. Em C você lida diretamente com memória, tipos e endereços. Quem aprende C entende melhor qualquer outra linguagem.
  • Aparece em todo lugar. O núcleo do Linux, partes dos sistemas operacionais, motores de jogos, o interpretador do Python e o firmware de microcontroladores (como os de Arduino) têm muito C ou C++ por baixo.
  • Abre portas. C++, C#, Java e JavaScript herdaram muita coisa da sintaxe do C.

Resumo do capítulo

  • Um algoritmo é uma sequência de passos claros, em ordem e finita; um programa é um algoritmo escrito para o computador.
  • CPU executa, RAM guarda o que está em uso (e esquece ao desligar), disco guarda para sempre.
  • Um bit vale 0 ou 1; 8 bits formam um byte, que guarda valores de 0 a 255.
  • Cada byte da memória tem um endereço; variáveis são nomes para esses lugares.
  • O compilador traduz o código-fonte em C para um executável que a CPU entende.

Exercícios

Algoritmo do dia a dia

Escreva, em passos numerados, um algoritmo para escovar os dentes ou para trocar a lâmpada de um abajur. Depois peça a um colega para "executar" seu algoritmo ao pé da letra e veja onde ele trava.

Ver dica

Não deixe nada implícito. "Pegue a escova" é um passo; "abra a gaveta" pode ser outro. Onde o colega travou é onde faltou um passo.

Lendo bytes

Sem usar o laboratório, calcule o valor decimal de 00001111 e de 10000000. Depois confira no laboratório.

Ver resposta

00001111 = 8 + 4 + 2 + 1 = 15. 10000000 = 128.

Quantas combinações?

Quantos valores diferentes cabem em 1 byte? E em 2 bytes (16 bits)?

Ver resposta

1 byte: 2⁸ = 256. 2 bytes: 2¹⁶ = 65.536. Cada bit a mais dobra o número de combinações.

Onde fica?

Para cada item, diga se está na RAM ou no armazenamento: (a) suas fotos do celular; (b) o jogo que está aberto agora; (c) um trabalho escolar salvo ontem; (d) o texto que você está digitando e ainda não salvou.

Ver resposta

(a) armazenamento; (b) RAM (o jogo foi copiado para lá ao abrir); (c) armazenamento; (d) RAM.

Conhecendo seu ambiente de desenvolvimento

Sua ferramenta de trabalho: onde escrever o código, compilar e testar. Este guia funciona com qualquer IDE ou compilador de C, então vamos falar de conceitos que valem para todos, com exemplos de como cada passo costuma aparecer nas ferramentas mais comuns.

O que você vai aprender
  • O que é uma IDE
  • Algumas opções gratuitas para instalar
  • Reconhecer as áreas comuns a qualquer IDE
  • Criar e salvar um arquivo .c
  • Compilar e executar seu programa
  • Ler mensagens de erro

O que é uma IDE?

IDE significa Ambiente de Desenvolvimento Integrado. É um programa que reúne, em um só lugar, o editor onde você escreve o código, o compilador que o traduz e, geralmente, um depurador que ajuda a achar erros. Existem várias IDEs para C, todas fazendo essencialmente a mesma coisa por baixo: chamar um compilador (na grande maioria das vezes, o GCC ou algo compatível com ele) para transformar seu código em um programa executável.

Instalação em resumo

Os detalhes mudam de ferramenta para ferramenta, mas o caminho costuma ser parecido:

  1. Baixe o instalador na página oficial da ferramenta escolhida. Baixe apenas de fontes confiáveis.
  2. Execute o instalador. Se o sistema pedir permissão, confirme.
  3. Escolha o idioma, se disponível (muitas têm Português Brasileiro), e avance nas telas com as opções padrão.
  4. Ao abrir pela primeira vez, o programa pode pedir tema e fonte. Escolha o que for mais confortável para os olhos.

A maioria dessas IDEs já traz o compilador embutido ou o instala junto, então normalmente você não precisa instalar mais nada à parte.

Um passeio pela interface

As IDEs têm aparências diferentes, mas quase todas organizam a tela nas mesmas áreas. O desenho abaixo é um resumo genérico; a sua pode ter os nomes em outro lugar ou com outro visual, mas o papel de cada área costuma ser este:

  • Menus: Arquivo, Editar, Executar (ou nomes parecidos, onde estão compilar e rodar)...
  • Barra de ferramentas: atalhos com ícones, geralmente incluindo compilar, executar e compilar+executar.
  • Painel lateral: lista de arquivos do projeto e, em algumas IDEs, classes e depuração.
  • Editor: onde você escreve o código. É aqui que você passa a maior parte do tempo, em qualquer ferramenta.
  • Painel de mensagens: mostra erros e avisos do compilador. Fique de olho nele, seja qual for o nome que sua IDE dá a ele!

Criando e salvando o primeiro arquivo

  1. Crie uma pasta curta para seus programas, por exemplo C:\programacao no Windows (ou ~/programacao no Linux/macOS). Evite espaços e acentos no caminho.
  2. No seu editor, vá em Arquivo > Novo > Arquivo Fonte (ou similar) ou pressione Ctrl+N.
  3. Pressione Ctrl+S para salvar. Na janela, escolha sua pasta e, no tipo de arquivo, selecione algo como C source files (*.c).
  4. Dê um nome sem espaços nem acentos, como ola.c.

Compilar e executar

Toda IDE tem alguma forma de compilar (traduzir o código) e executar (rodar o programa gerado), muitas vezes combinadas em um único botão de "play" ou "executar". O nome e o atalho variam de ferramenta para ferramenta; a tabela abaixo mostra alguns exemplos comuns só para você ter uma referência — procure o equivalente na sua:

FerramentaCompilar e executarSó compilar
Dev-C++F11F9
Code::BlocksF9Ctrl+F9
CLionShift+F10Ctrl+F9
Visual StudioCtrl+F5Ctrl+Shift+B
Compiladores on-lineBotão "Run" na páginaGeralmente não separado

Outros atalhos costumam se repetir em quase toda ferramenta de edição de texto ou código:

AtalhoO que faz
Ctrl+SSalvar
Ctrl+Z / Ctrl+YDesfazer / refazer
Ctrl+FLocalizar texto
Ctrl+EspaçoSugestões de autocompletar (na maioria dos editores)

Ao compilar e executar, sua ferramenta salva o arquivo (se ainda não salvou), traduz o código e abre uma janela de console (às vezes chamada de terminal), onde seu programa roda. Muitas IDEs acrescentam uma mensagem extra ao final, parecida com esta, para a janela não fechar antes de você ler o resultado:

Essa mensagem extra não é do seu programa: quem escreve é a IDE, e o texto exato varia de uma para outra. O código de retorno é o número que o programa devolveu ao sistema operacional com o return 0; que você conhecerá no próximo capítulo. Zero significa "terminei sem problemas".

Lendo as mensagens do compilador

Quando algo está errado, o painel de erros da sua IDE (a área 5 do diagrama acima, ou a saída do terminal, se você estiver compilando por linha de comando) lista os problemas. Uma mensagem típica, para um ; esquecido, parece com isto, praticamente igual em qualquer ferramenta baseada em GCC:

Leia assim: arquivo ola.c, linha 6, coluna 5, error (erro): "esperava um ponto e vírgula antes de return". Regras de ouro para lidar com erros:

  1. Comece pelo primeiro erro da lista. Um único erro costuma provocar vários outros em cascata.
  2. Clique (ou dê duplo clique) na mensagem, se sua IDE permitir. Na maioria delas, o cursor pula direto para a linha indicada.
  3. Olhe também a linha de cima. No exemplo acima, o ponto e vírgula esquecido estava na linha 5, mas o compilador só percebeu na 6.
  4. Avisos (warning) não impedem o programa de rodar, mas geralmente apontam bugs de verdade. Leia todos.

Alternativa: compilando pelo terminal

Se preferir (ou se sua turma trabalhar assim), você não precisa de uma IDE completa: pode escrever o código em qualquer editor de texto simples e compilar diretamente no terminal (o Prompt de Comando ou PowerShell no Windows; o terminal no Linux e no macOS), desde que o compilador esteja instalado e configurado no sistema.

Esse comando compila ola.c e gera um executável chamado ola (ola.exe no Windows). Para rodar:

Esse é exatamente o mesmo processo que uma IDE faz por trás dos botões: ela só poupa você de digitar o comando toda vez.

Hábitos que poupam dor de cabeça

  • Salve com Ctrl+S com frequência (ou lembre de recompilar após salvar, se seu fluxo for pelo terminal).
  • Use um arquivo para cada programa e nomes sem espaços ou acentos: media_notas.c.
  • Indente o código (4 espaços ou Tab dentro das chaves). Código alinhado é código legível.
  • Para projetar em sala, aumente a fonte do editor nas opções da sua ferramenta.

Resumo do capítulo

  • Uma IDE reúne editor, compilador e depurador; existem várias boas opções gratuitas, e todas cumprem esse papel.
  • Salve os arquivos com a extensão .c, em uma pasta curta e sem acentos.
  • Toda ferramenta tem um jeito de compilar e executar; o nome e o atalho mudam, o resultado é o mesmo.
  • Também é possível compilar pelo terminal com gcc arquivo.c -o arquivo, sem IDE nenhuma.
  • Leia o primeiro erro, veja a linha indicada (e a anterior) e corrija um de cada vez.

Exercícios

Prepare o ambiente

Instale (ou abra) a IDE escolhida pela sua turma — ou prepare um terminal com o GCC instalado, se for esse o caminho. Crie uma pasta para seus programas e um arquivo em branco salvo como teste.c.

Erro de propósito

No próximo capítulo você digitará o primeiro programa. Quando o fizer, apague um ; de propósito e compile. Descreva com suas palavras o que a mensagem de erro diz. Depois desfaça e veja o erro sumir.

Conforto visual

Aumente o tamanho da fonte do editor e teste um tema diferente, se sua ferramenta permitir. Programar por horas com uma fonte confortável faz diferença.

Seu primeiro programa

Chegou a hora de fazer o computador dizer alguma coisa. Vamos escrever o clássico "Olá, mundo!", entender cada pedaço dele e resolver o problema dos acentos no Windows.

O que você vai aprender
  • A estrutura mínima de um programa em C
  • Mostrar texto na tela com printf
  • Caracteres especiais como \n e \t
  • Escrever comentários
  • Fazer acentos aparecerem corretamente
  • Reconhecer os erros mais comuns

O "Olá, mundo!"

Todo programador começa por aqui. Abra sua IDE (ou o terminal, se for esse o seu caminho), crie um arquivo novo, salve como ola.c e digite:

Compile e execute (use o atalho ou o botão do seu ambiente, como vimos no capítulo anterior). Se tudo deu certo, a janela preta aparece:

Parabéns, você acabou de criar um programa! Vamos entender o que cada linha faz.

Anatomia de um programa em C

LinhaO que significa
#include <stdio.h>Pede ao compilador para trazer uma biblioteca, um conjunto de ferramentas prontas. stdio vem de standard input/output (entrada e saída padrão) e contém o printf.
int main()Define a função principal. Todo programa em C começa a ser executado por ela. O int indica que ela devolve um número inteiro ao final.
{ ... }As chaves delimitam o bloco de instruções que pertencem à função.
printf("...");Escreve o texto entre aspas na tela. É uma instrução.
;O ponto e vírgula termina cada instrução, como o ponto final de uma frase. Sem ele, o compilador se perde.
return 0;Encerra o programa e informa ao Windows que tudo correu bem (0 = sem erros).

O que acontece quando você compila e executa

Por trás do botão (seja ele qual for) existe uma linha de montagem com três etapas:

1. Pré-processadorlê os #include e junta as bibliotecas ao seu código
▸
2. Compiladortraduz o C para código de máquina
▸
3. Linkadorliga seu código ao printf pronto e gera o .exe

Mais de uma instrução

Vamos criar uma pequena ficha de apresentação. Note como cada printf aparece em uma linha da tela, na mesma ordem em que foi escrito. Esse programa já usa a linha do setlocale, que explicamos logo adiante:

E os acentos?

O console do Windows usa uma tabela de caracteres antiga, diferente da que o editor usa. Sem uma configuração, palavras como informática podem sair como inform├ítica. A solução é avisar ao programa que vamos usar português, com a linha:

Este é o seu modelo padrão: a partir de agora, todo programa do guia começa assim. Guarde uma cópia como modelo.c e comece cada exercício a partir dele.

Caracteres especiais

Dentro das aspas do printf, a barra invertida \ inicia uma sequência de escape: um código que representa algo que não dá para digitar diretamente.

CódigoEfeito
\nNova linha (pula para a linha de baixo)
\tTabulação (um "espaço grande", útil para alinhar)
\\Uma barra invertida de verdade
\"Uma aspa dupla de verdade dentro do texto
%%Um símbolo de porcentagem de verdade (o % é especial no printf)

Comentários: notas para humanos

O compilador ignora completamente os comentários, então eles servem para explicar o código para quem for ler (inclusive você daqui a um mês). Também servem para "desligar" uma linha temporariamente.

Desenhando com texto

Um exemplo divertido que junta tudo: um gatinho em arte ASCII. Como a barra invertida é especial, cada uma precisa ser escrita duas vezes (\\).

Erros comuns de quem está começando

O que aconteceuSintoma
Esqueceu o ; no fim da instruçãoerror: expected ';' before ..., geralmente apontando para a linha seguinte
Esqueceu de fechar as aspasmissing terminating " character
Escreveu Printf ou prinftErro ou aviso de função não declarada. O C diferencia maiúsculas de minúsculas.
Esqueceu o #include <stdio.h>Aviso ou erro de implicit declaration of function 'printf'
Escreveu Main com M maiúsculoErro de undefined reference to 'main'
Esqueceu de fechar uma chave }expected declaration or statement at end of input

Resumo do capítulo

  • Todo programa em C tem uma função main, que é onde a execução começa.
  • #include traz bibliotecas; printf escreve na tela.
  • Cada instrução termina com ; e roda de cima para baixo.
  • \n, \t, \\, \" e %% escrevem caracteres especiais.
  • Use setlocale(LC_CTYPE, "Portuguese"); para os acentos funcionarem.
  • Comentários (// e /* */) são ignorados pelo compilador.

Exercícios

Cartão de visita

Escreva um programa que mostre seu nome, sua escola, seu curso e sua cidade, cada um em uma linha, usando \t para alinhar as informações.

Sua arte ASCII

Desenhe algo com printf: uma casa, um foguete, o logo do seu time. Lembre-se de escrever \\ para cada barra invertida.

Cadê o erro?

O programa abaixo tem quatro erros. Descubra todos sem digitar no computador; depois confira digitando.

Ver resposta

(1) Main deve ser main; (2) faltam as aspas de fechamento: "Ola, mundo!"; (3) falta o ; depois do printf(...) (o parêntese também precisa fechar); (4) falta o ; depois do return 0.

Recibo

Imprima um recibo de lanchonete com três itens e preços, usando \t para alinhar os preços em uma coluna.

Variáveis e tipos de dados

Programas úteis precisam lembrar coisas: a vida do personagem, o preço de um produto, a nota de um estudante. Variáveis são a memória de trabalho do seu programa.

O que você vai aprender
  • O que é uma variável e como ela ocupa a RAM
  • Os tipos int, float, double e char
  • Mostrar variáveis com printf
  • Regras para nomear variáveis
  • Constantes
  • Limites e curiosidades dos números no computador

Variável é uma caixa com nome

Para usar uma variável, primeiro a declaramos, dizendo o tipo e o nome. Depois damos um valor a ela com o sinal =, que em C significa "recebe" e não "é igual a":

Cada int ocupa 4 bytes, por isso os endereços (inventados) pulam de 4 em 4.

Os tipos básicos

TipoGuardaTamanhoFaixa aproximadaNo printf
intNúmeros inteiros4 bytes−2,1 bilhões a +2,1 bilhões%d
floatNúmeros com casas decimais4 bytescerca de 7 dígitos de precisão%f
doubleDecimais com mais precisão8 bytescerca de 15 dígitos de precisão%f ou %lf
charUm único caractere1 byte0 a 255 (uma letra da tabela ASCII)%c

Os números decimais usam ponto no código: 3.14, e não 3,14. Um caractere isolado usa aspas simples: 'G'. Textos com aspas duplas ("Olá") são um assunto para o capítulo 10.

Mostrando variáveis na tela

No printf, cada %d, %f ou %c dentro do texto é um espacinho que será preenchido pelas variáveis listadas depois da vírgula, na mesma ordem. O %.2f mostra o número com 2 casas decimais.

O valor de uma variável pode mudar durante o programa. É por isso que ela se chama "variável":

Aplicação: quantas músicas cabem no pen drive?

Conversões de armazenamento aparecem toda hora. Com variáveis, o programa faz a conta para qualquer tamanho:

Outro exemplo do dia a dia, agora com decimais: o total de uma compra.

Nomes de variáveis

  • Podem ter letras, números e o sublinhado _, mas não podem começar com número.
  • Sem espaços, acentos ou símbolos: nota_final sim, nota final e média não.
  • Maiúsculas importam: Idade e idade são variáveis diferentes.
  • Não podem ser palavras reservadas do C, como int, if ou return.
  • Prefira nomes que expliquem o conteúdo: preco_unitario diz muito mais do que x.

Constantes

Quando um valor nunca deve mudar (como o valor de π ou o número máximo de estudantes), declare uma constante. Por convenção, usamos letras maiúsculas:

O tamanho de cada tipo

O operador sizeof pergunta ao compilador quantos bytes um tipo ocupa. É uma boa forma de conferir o que você aprendeu no capítulo 1:

Quando os números "estouram"

Uma variável tem um número fixo de bits, então existe um valor máximo. Um unsigned char (byte sem sinal) vai de 0 a 255. O que acontece ao somar 1 em 255?

Volte ao laboratório do capítulo 1: 11111111 mais 1 daria 100000000, que precisa de 9 bits. Como só cabem 8, o bit extra se perde e sobra 00000000. É como o hodômetro de um carro que passa de 999.999 e volta a zero. Esse fenômeno se chama overflow.

Variável sem valor: o "lixo" na memória

Ao declarar int x;, o computador reserva a caixa, mas não a limpa. Ela contém o que sobrou de algum programa anterior, o "lixo". Ler uma variável antes de dar valor a ela mostra números imprevisíveis.

Tabela de especificadores

CódigoTipoExemploResultado
%dintprintf("%d", 42);42
%ffloat / doubleprintf("%f", 3.5);3.500000
%.2fdecimal com 2 casasprintf("%.2f", 3.14159);3.14
%ccharprintf("%c", 'A');A
%stexto (cap. 10)printf("%s", "Oi");Oi
%5dint com largura 5printf("[%5d]", 42);[ 42]
%05dint com zeros à esquerdaprintf("%05d", 42);00042

Resumo do capítulo

  • Variável = nome + tipo + valor, guardada na RAM.
  • int para inteiros, float/double para decimais, char para um caractere.
  • = atribui (guarda o valor da direita na variável da esquerda).
  • Use %d, %f e %c no printf para mostrar cada tipo.
  • Tipos têm tamanho fixo: passar do limite causa overflow; decimais podem ter pequenas imprecisões.
  • Inicialize as variáveis antes de usar.

Exercícios

Escolha o tipo

Diga qual tipo você usaria para guardar: (a) o número de estudantes da turma; (b) o preço de um café; (c) a inicial do seu nome; (d) a temperatura de hoje; (e) a distância da Terra ao Sol em metros (cerca de 150 bilhões).

Ver resposta

(a) int; (b) float; (c) char; (d) float; (e) double: 150 bilhões é maior que o limite do int (cerca de 2,1 bilhões).

Seu perfil de jogador

Crie um programa com variáveis para inicial do nome, idade, altura (em metros) e quantidade de jogos na biblioteca. Mostre todas em uma ficha bem formatada.

Qual é a saída?

Sem rodar, diga o que este trecho mostra:

Ver resposta

Passo a passo: a = 5; depois a = 5 + 2 = 7; depois a = 7 × 3 = 21.

Quantos filmes cabem?

Adapte o programa das músicas: quantos filmes de 1500 MB cabem em um HD de 500 GB?

Ver dica

500 × 1024 = 512000 MB; dividido por 1500 dá 341 (divisão de inteiros descarta a parte decimal).

Entrada de dados com scanf

Até agora seu programa só fala. Neste capítulo ele aprende a escutar: vamos pedir informações ao usuário, guardar em variáveis e usar nas contas.

O que você vai aprender
  • Ler números e caracteres com scanf
  • Por que existe o &
  • Ler vários valores de uma vez
  • Fazer programas interativos úteis
  • Evitar os erros clássicos do scanf

O ciclo: perguntar, esperar, guardar

Um programa interativo segue quase sempre a mesma receita: mostra uma pergunta com printf, espera a resposta com scanf e guarda o valor em uma variável.

O scanf tem duas partes: o formato entre aspas, que diz que tipo de dado esperar (%d para int, %f para float, %lf para double, %c para char), e o destino, a variável onde guardar.

Nosso primeiro programa interativo

Vamos perguntar o nome da pessoa. Por enquanto, aceite que char nome[50] cria um espaço para guardar um texto de até 49 letras (o capítulo 10 explica). Repare que, para texto, não usamos o &:

Lendo números e fazendo contas

Aqui vai um pequeno spoiler do próximo capítulo: os símbolos + - * / fazem o que você espera. Veja duas leituras, uma para cada número:

Repare que cada valor tem sua própria pergunta e sua própria leitura. Isso deixa claro para quem está usando o programa o que ele deve digitar a cada momento.

Aplicação: conversor de temperatura

Viajando para o Canadá, 25 °C parece pouco ou muito? Vamos converter com a fórmula F = C × 9 / 5 + 32:

Aplicação: quanto tempo até juntar dinheiro?

Sonhando com um celular novo ou um videogame? O programa calcula quantos meses são necessários, guardando uma quantia fixa por mês:

Lendo um caractere

Para ler uma única letra, coloque um espaço antes do %c. Esse espaço manda o scanf ignorar o Enter que sobrou de uma leitura anterior:

Tabela de formatos do scanf

Tipo da variávelFormato no scanfPrecisa do &?
int%dSim
float%fSim
double%lf (atenção: l minúsculo)Sim
char" %c" (com espaço)Sim
texto (char nome[50])%sNão

Erros clássicos

  • Esquecer o &: scanf("%d", idade); tenta escrever em um endereço inválido e o programa pode fechar de repente.
  • Formato errado: usar %d para ler um float guarda lixo. O formato deve combinar com o tipo da variável.
  • Digitar letra onde se espera número: o scanf desiste da leitura e a variável fica com o valor antigo. Para programas mais robustos, verifique o retorno: scanf devolve quantos valores conseguiu ler.

Resumo do capítulo

  • printf pergunta; scanf espera a resposta e guarda na variável.
  • O & informa o endereço da variável; texto (%s) não usa.
  • Formatos: %d int, %f float, %lf double, " %c" char.
  • O formato precisa combinar com o tipo da variável.

Exercícios

Consumo do carro

Peça a distância percorrida (km) e os litros de combustível gastos. Mostre o consumo médio em km/l.

Ver dica

Use float e a divisão km / litros. Ex.: 300 km com 25 litros dá 12.00 km/l.

Conversor de moeda

Peça um valor em reais e a cotação do dólar do dia. Mostre quanto vale em dólares.

Idade em dias

Peça o ano de nascimento e o ano atual. Mostre a idade aproximada em anos e em dias (365 dias por ano).

Ficha de cadastro

Peça nome (uma palavra), idade, altura e a inicial do sobrenome. Mostre tudo em uma frase.

Operadores e expressões

Calcular é a especialidade do computador. Vamos conhecer os operadores aritméticos, os de comparação e os lógicos, incluindo pegadinhas que surpreendem até quem já programa há tempo.

O que você vai aprender
  • As quatro operações e o resto da divisão
  • Divisão de inteiros e conversão (cast)
  • Precedência: quem calcula primeiro
  • ++, --, += e companhia
  • Comparar e combinar condições
  • Funções matemáticas (math.h)

Operadores aritméticos

OperadorOperaçãoExemploResultado
+Soma5 + 38
-Subtração5 - 32
*Multiplicação5 * 315
/Divisão10 / 42 (inteiros) ou 2.5 (decimais)
%Resto da divisão (módulo)10 % 42

A pegadinha da divisão de inteiros

Se os dois lados da divisão forem inteiros, o resultado também é inteiro: a parte decimal é simplesmente descartada. Isso vale mesmo quando o resultado é guardado em um float! Para obter o resultado correto, ao menos um dos lados precisa ser decimal, e podemos forçar isso com uma conversão (cast): (float)a.

O resto da divisão é muito útil

O operador % parece esquisito no começo, mas resolve muitos problemas: saber se um número é par (n % 2 == 0), fazer ciclos (como os dias da semana) e converter unidades. Veja como transformar segundos em horas, minutos e segundos:

Como funciona: 3725 ÷ 3600 = 1 hora, sobrando 125 segundos (3725 % 3600). Desses 125, 125 ÷ 60 = 2 minutos, sobrando 5 segundos (total % 60).

Precedência: quem calcula primeiro?

Como na matemática, multiplicação, divisão e resto são calculados antes de soma e subtração. Use parênteses para mandar no que acontece primeiro. O erro clássico é calcular uma média sem eles:

Atalhos: incremento e atribuição composta

Somar 1 a uma variável é tão comum que existe um atalho. O mesmo vale para as outras operações:

AtalhoEquivale a
x++x = x + 1
x--x = x - 1
x += 5x = x + 5
x -= 5x = x - 5
x *= 2x = x * 2
x /= 2x = x / 2

Operadores de comparação e lógicos

Comparações respondem verdadeiro ou falso. Em C, o falso é o número 0 e o verdadeiro é 1 (na verdade, qualquer valor diferente de zero conta como verdadeiro).

OperadorSignificadoOperadorSignificado
==igual a&&E (as duas precisam ser verdadeiras)
!=diferente de||OU (basta uma ser verdadeira)
> <maior, menor!NÃO (inverte o valor)
>= <=maior ou igual, menor ou igual

Funções matemáticas: math.h

Para raiz quadrada, potência e outras contas, inclua a biblioteca math.h:

FunçãoO que fazExemplo
sqrt(x)Raiz quadradasqrt(25) → 5
pow(x, y)x elevado a ypow(2, 10) → 1024
fabs(x)Valor absoluto (tira o sinal)fabs(-3.5) → 3.5
floor(x) / ceil(x)Arredonda para baixo / para cimaceil(2.1) → 3

Um uso real: em jogos, é preciso saber a distância entre dois pontos da tela (o jogador e um inimigo, por exemplo). Pelo teorema de Pitágoras, d = √((x₂−x₁)² + (y₂−y₁)²):

Resumo do capítulo

  • + - * / %: com dois inteiros, / descarta os decimais; % dá o resto.
  • Use (float) ou um número decimal (7.0) para obter divisão com casas decimais.
  • Multiplicação, divisão e resto vêm antes de soma e subtração; parênteses mandam em tudo.
  • x++, x += 5 e similares são atalhos.
  • Comparações e &&, ||, ! resultam em 1 (verdadeiro) ou 0 (falso).
  • = atribui; == compara.

Exercícios

Qual é o resultado?

Calcule de cabeça: (a) 2 + 3 * 4; (b) (2 + 3) * 4; (c) 10 / 3; (d) 10 % 3; (e) 10 / 3.0; (f) 5 > 3 && 2 > 4.

Ver resposta

(a) 14; (b) 20; (c) 3; (d) 1; (e) 3.333...; (f) 0 (falso, porque a segunda parte é falsa).

Racha a conta

Peça o valor total da conta, a quantidade de pessoas e some 10% de serviço. Mostre quanto cada um paga, com 2 casas decimais.

Ver dica

Ex.: conta de 187.50 para 4 pessoas: (total * 1.10) / pessoas = 51.56 por pessoa.

Troco em cédulas

Peça um valor inteiro em reais e mostre quantas notas de 100, 50, 20, 10, 5 e 2 e quantas moedas de 1 real são necessárias, usando / e %.

Ver dica

Notas de 100: valor / 100. O que sobrou: valor % 100. Repita com o que sobrou para a próxima nota.

Índice de massa corporal

Peça peso (kg) e altura (m) e calcule peso / (altura * altura). (O IMC é apenas um indicador; não substitui avaliação profissional.)

Promoção

Peça o preço de um produto e o percentual de desconto. Mostre o valor do desconto e o preço final.

Tomando decisões

Até aqui, seu programa executava sempre as mesmas instruções. Com if, else e switch ele passa a escolher caminhos diferentes conforme a situação, e é aí que a programação fica interessante.

O que você vai aprender
  • Executar código só se uma condição for verdadeira
  • if, else e else if
  • Combinar condições com && e ||
  • Escolher entre várias opções com switch
  • Os erros mais comuns ao decidir

A bifurcação

No dia a dia decidimos o tempo todo: se estiver chovendo, levo guarda-chuva; senão, deixo em casa. Em C escrevemos exatamente assim. A condição fica entre parênteses e o que fazer, entre chaves:

          ┌───────────────┐
          │  media >= 7 ? │
          └───────┬───────┘
          sim     │     não
        ┌─────────┴─────────┐
        ▼                   ▼
  "Aprovado!"         "Reprovado."
        └─────────┬─────────┘
                  ▼
           (o programa segue)

Se a condição for verdadeira, o bloco do if executa; se for falsa, executa o bloco do else (que é opcional). Depois disso o programa continua normalmente.

Vários caminhos: else if

Quando há mais de duas possibilidades, encadeamos condições com else if. O computador testa de cima para baixo e executa apenas o primeiro bloco cuja condição for verdadeira:

Combinando condições

Use && (E) e || (OU) para condições mais ricas. Um exemplo clássico: descobrir se um ano é bissexto. A regra é: divisível por 4 e não por 100, ou divisível por 400.

Escolhendo entre opções: switch

Quando você compara uma mesma variável com vários valores fixos (um menu, um operador), o switch fica mais limpo que muitos else if. Cada case é uma possibilidade; o break encerra o caso, e o default cuida de qualquer outra coisa.

Aplicação: cardápio da lanchonete

Um menu numerado é o uso perfeito do switch. Note que dentro do default usamos return 0; para encerrar o programa se a opção for inválida:

Bônus: o operador ternário

Para escolher entre dois valores em uma única linha existe o operador ?:, que lê "condição ? valor se verdadeiro : valor se falso":

Erros comuns em decisões

ErroPor que é um problema
if (x = 5)Um só = atribui 5 a x e a condição vira sempre verdadeira. O certo é x == 5.
if (x > 5);O ; logo após o if "fecha" o comando vazio. O bloco seguinte executa sempre.
if (a < b < c)Não funciona como na matemática. Escreva a < b && b < c.
Comparar decimais com ==Imprecisões de arredondamento (lembra do 0.1 + 0.2?) fazem a igualdade falhar. Compare com margem ou use inteiros.
Esquecer as chaves com várias linhasSem chaves, só a primeira instrução pertence ao if. Use sempre chaves.

Resumo do capítulo

  • if (condição) { ... } else { ... } escolhe entre dois caminhos.
  • else if encadeia mais opções; vale o primeiro bloco verdadeiro.
  • && (E), || (OU) e ! (NÃO) combinam condições.
  • switch compara uma variável com valores fixos; lembre do break.
  • Cuidado com = no lugar de == e com ; depois do if.

Exercícios

Positivo, negativo ou zero

Leia um número e diga se é positivo, negativo ou zero.

O maior de três

Leia três números e mostre o maior deles.

Ver solução

A ideia: assuma que a é o maior e vá trocando se aparecer outro maior.

Desconto progressivo

Em uma loja, compras acima de R$ 100 têm 10% de desconto e acima de R$ 200 têm 20%. Leia o valor e mostre o total a pagar.

Triângulo válido

Três lados formam um triângulo se cada lado for menor que a soma dos outros dois. Leia três lados e diga se formam um triângulo.

Ver dica

Use três comparações combinadas com &&: a < b + c && b < a + c && c < a + b.

Dia da semana

Leia um número de 1 a 7 e mostre o dia correspondente (1 = domingo) usando switch.

Repetições

O computador não se cansa nem reclama. Repetir uma tarefa mil vezes custa a ele o mesmo esforço de repetir uma. Os laços (loops) são o superpoder que faz isso com poucas linhas.

O que você vai aprender
  • Repetir com while, for e do-while
  • Contadores e acumuladores
  • Sair de um laço com break e pular com continue
  • Laços dentro de laços
  • Fazer um teste de mesa
  • Criar um jogo de adivinhação

while: repetir enquanto for verdade

O while testa uma condição; se for verdadeira, executa o bloco e volta a testar. Só para quando a condição fica falsa. Veja uma contagem regressiva:

Teste de mesa: como "ser o computador"

Para entender um laço, faça um teste de mesa: uma tabela onde você acompanha, passo a passo, o valor das variáveis como se fosse o computador executando. É a melhor técnica para achar erros de lógica.

Voltat no iníciot > 0 ?Mostrat depois do t--
1ª5sim5...4
2ª4sim4...3
3ª3sim3...2
4ª2sim2...1
5ª1sim1...0
fim0não(sai do laço)

for: quando você sabe quantas vezes

O for reúne em uma linha as três partes de um laço com contador: início, condição e passo.

for ( int i = 1 ;  i <= 10 ;  i++ ) {
      └───┬───┘    └──┬──┘    └─┬─┘
      começa em     repete    depois de cada
          1        enquanto    volta, soma 1
                  for verdade
}

A tabuada é o exemplo perfeito. Escolha o número e o computador mostra a conta inteira:

Contador e acumulador

Dois padrões aparecem o tempo todo dentro de laços. O contador conta quantas vezes algo acontece (cont++). O acumulador vai somando valores (soma += valor). Os dois começam em zero antes do laço.

do-while: faça primeiro, pergunte depois

O do-while executa o bloco pelo menos uma vez e só então testa a condição. É ideal para validar dados: pede-se o valor e repete-se enquanto ele estiver errado.

Todo programa com menu usa esse padrão: mostra as opções e repete até o usuário escolher "Sair".

break e continue

break sai do laço imediatamente. continue pula o resto da volta atual e vai para a próxima. Veja um sistema de senha com três tentativas, um caso real de uso do break:

E um exemplo curto de continue: mostrar só os números ímpares de 1 a 10.

Laços dentro de laços

Um laço pode conter outro. O de dentro roda completo a cada volta do de fora. É assim que desenhamos figuras e percorremos tabelas:

Qual laço usar?

LaçoUse quandoExemplo
forvocê sabe quantas voltas dartabuada, percorrer 10 notas
whilea repetição depende de uma condição, e pode nem acontecercontagem regressiva, ler até acabar
do-whileprecisa executar pelo menos uma vezmenus e validação de entrada

Projeto: jogo de adivinhação

Hora de juntar tudo: variáveis, scanf, if e um laço. O computador "pensa" em um número aleatório e você tenta adivinhar com dicas de maior ou menor. Para sortear, usamos rand(), e srand(time(NULL)) muda a semente a cada execução (senão o número seria sempre o mesmo).

Resumo do capítulo

  • while repete enquanto a condição for verdadeira; for junta início, condição e passo; do-while executa ao menos uma vez.
  • Contadores (cont++) e acumuladores (soma += x) começam antes do laço.
  • break sai do laço; continue pula para a próxima volta.
  • Teste de mesa: acompanhe as variáveis volta por volta para achar erros.
  • rand() e srand(time(NULL)) geram números aleatórios.

Exercícios

Soma de 1 a 100

Some todos os inteiros de 1 a 100 com um laço. (Resposta esperada: 5050.)

Pares e ímpares

Leia 10 números e mostre quantos são pares e quantos são ímpares.

Ver dica

Use dois contadores e o teste n % 2 == 0 dentro de um for.

Fatorial

Leia um número n e calcule n! (por exemplo, 5! = 5 × 4 × 3 × 2 × 1 = 120).

Ver dica

Comece com fat = 1 e multiplique por i a cada volta, de 1 até n.

Sequência de Fibonacci

Mostre os 10 primeiros termos: 0 1 1 2 3 5 8 13 21 34. Cada termo é a soma dos dois anteriores.

Ver dica

Guarde dois valores (a = 0, b = 1). A cada volta, mostre a, calcule prox = a + b, depois faça a = b e b = prox.

Poupança

Guardando R$ 1000 em uma poupança que rende 1% ao mês, quanto se tem após 12 meses? Mostre o saldo mês a mês. (Resposta final: cerca de R$ 1126,83.)

Vetores e matrizes

E se você precisasse guardar 30 notas? Criar 30 variáveis não faz sentido. Os vetores (arrays) permitem guardar muitos valores do mesmo tipo em fila, sob um único nome.

O que você vai aprender
  • Declarar e usar vetores
  • Por que a contagem começa no zero
  • Percorrer vetores com for
  • Achar maior, menor e média
  • O perigo de sair dos limites
  • Matrizes e ordenação

Uma fileira de caixas numeradas

O vetor v depois de v[2] = 99. Embaixo, o endereço (inventado) de cada posição.

Percorrendo com for

Vetores e laços for nasceram um para o outro: o contador do laço serve de índice. Vamos ler as notas de 5 estudantes e calcular estatísticas. Usamos #define TAM 5 para definir o tamanho em um só lugar:

Vetor como contador: lançando dados

Um uso muito esperto: cada posição do vetor conta quantas vezes um valor apareceu. Vamos "rolar" um dado 60 vezes e desenhar um histograma:

O perigo de sair dos limites

O C não verifica se o índice existe. Se você escrever em v[5] em um vetor de 5 posições, o programa escreve na memória vizinha, que pertence a outra variável (ou a outro pedaço do programa!). O resultado pode ser um valor que muda "do nada", ou o programa fechar de repente.

Ordenando: bubble sort

Ordenar dados é uma das tarefas mais comuns da computação. O bubble sort (ordenação por bolhas) compara pares vizinhos e troca se estiverem fora de ordem. A cada rodada, o maior valor "flutua" para o final:

Matrizes: vetores com linhas e colunas

Uma matriz é um vetor de vetores: uma tabela com dois índices, [linha][coluna]. Serve para tabuleiros, imagens (grades de pixels) e planilhas. Vamos desenhar um tabuleiro de jogo da velha:

Resumo do capítulo

  • tipo nome[N] cria N elementos seguidos na memória; os índices vão de 0 a N−1.
  • Use for (i = 0; i < N; i++) para percorrer.
  • O C não protege contra índices inválidos: cuide dos limites.
  • Um vetor pode servir de contador (dados) ou ser ordenado (bubble sort).
  • Matrizes usam dois índices: m[linha][coluna].

Exercícios

Ao contrário

Leia 10 números em um vetor e mostre-os na ordem inversa.

Ver dica

Para mostrar de trás para frente, faça o laço começar em 9 e diminuir: for (i = 9; i >= 0; i--).

Busca

Leia 8 números e depois peça um valor a procurar. Diga em qual posição ele está, ou "não encontrado".

Top 3

Leia 6 pontuações de um jogo e mostre as três maiores.

Ver dica

Ordene o vetor com bubble sort e mostre os três últimos (ou ordene em ordem decrescente e mostre os três primeiros).

Boletim

Crie uma matriz float notas[3][2] (3 estudantes, 2 provas), leia os valores e mostre a média de cada estudante.

Textos: char e strings

Nomes, senhas, mensagens: quase todo programa trabalha com texto. Em C, um texto é apenas um vetor de caracteres com um marcador especial no final. Entender isso abre muitas portas, inclusive a criptografia.

O que você vai aprender
  • Caracteres e a tabela ASCII
  • Como uma string vive na memória
  • Ler frases inteiras com fgets
  • Usar string.h e ctype.h
  • Fazer login, contar vogais, detectar palíndromos
  • Criar uma cifra secreta

Caracteres são números

Lembra do capítulo 1? O byte 01000001 vale 65, que a tabela ASCII associa à letra A. Em C, um char é exatamente esse pequeno número, e o printf decide como mostrá-lo: com %c como letra ou com %d como número.

Como as letras são números em sequência, dá para fazer contas com elas. A diferença entre uma letra minúscula e sua maiúscula é sempre 32. Digite qualquer texto abaixo e veja o código de cada caractere:

Laboratório: texto em números

Repare que letras acentuadas (como ã ou é) ficam fora da tabela ASCII básica, e por isso dão mais trabalho no console.

Uma string é um vetor de char

Não existe um tipo "texto" em C. Uma string é um vetor de char que termina com o caractere especial '\0' (o "terminador", de valor zero). É ele que avisa às funções onde o texto acaba.

O vetor nome. As posições depois do '\0' existem, mas não fazem parte do texto.

Por ser um vetor, você não pode trocar o texto com nome = "Bia";. Para copiar textos existem funções prontas, que veremos a seguir.

Lendo frases com fgets

O scanf("%s") para no primeiro espaço. Para ler uma linha inteira, use fgets. Ele recebe o vetor, o tamanho máximo e stdin (o teclado). Como o fgets também guarda o Enter digitado ('\n'), removemos ele com uma linha padrão:

Ferramentas de string.h e ctype.h

FunçãoO que fazExemplo
strlen(s)Conta os caracteres (sem o terminador)strlen("Ola") → 3
strcpy(dest, orig)Copia um texto para outro vetorstrcpy(nome, "Bia");
strcat(dest, orig)Cola um texto no final de outrostrcat(frase, "!");
strcmp(a, b)Compara: devolve 0 se forem iguaisstrcmp(s, "sim") == 0
toupper(c) / tolower(c)Converte um caractere para maiúscula / minúsculatoupper('a') → 'A'
isdigit(c) / isalpha(c)Diz se é dígito / letraisdigit('7') → verdadeiro

Aplicação: tela de login

O %29s limita a leitura a 29 caracteres para não estourar o vetor de 30 posições (29 letras + o terminador).

Percorrendo uma string: contando vogais

Como a string termina em '\0', podemos percorrê-la com um laço que roda até encontrá-lo:

Palíndromos

Um palíndromo lê-se igual de trás para a frente: arara, radar, ovo. A ideia é comparar o primeiro com o último, o segundo com o penúltimo, e assim por diante, com dois índices caminhando um em direção ao outro:

Projeto: a cifra de César

Há mais de 2000 anos, Júlio César protegia mensagens militares trocando cada letra por outra, três posições à frente no alfabeto: A vira D, B vira E, e assim por diante. Com o que você sabe sobre char como número, dá para programar isso! O % 26 faz o alfabeto "dar a volta" (depois do Z volta ao A):

Como funciona: c - 'a' converte a letra em uma posição de 0 a 25; somamos a chave; o % 26 mantém no alfabeto; e o + 'a' volta para a letra. Para decifrar, use a chave 26 menos a original (para chave 3, decifra-se com 23).

Resumo do capítulo

  • Um char é um número (código ASCII); 'A' é um caractere, "A" é uma string.
  • String = vetor de char terminado em '\0'; use %s para mostrar.
  • fgets lê linhas inteiras; scanf("%s") lê uma palavra.
  • Compare strings com strcmp(a, b) == 0 e copie com strcpy.
  • Percorra strings com um laço até '\0'.

Exercícios

Iniciais

Leia nome e sobrenome (duas palavras) e mostre as iniciais em maiúsculas, como "A.S.".

Força da senha

Leia uma senha e diga se é forte: pelo menos 8 caracteres e ao menos um dígito e uma letra maiúscula.

Ver dica

Percorra a senha e use isdigit() e isupper() para marcar com variáveis 0/1 se achou cada tipo. Ao final, teste as três condições.

Decifrando

Crie o programa que decifra mensagens da cifra de César, sabendo a chave usada. Teste com "Rod Pxqgr" e chave 3.

Contando palavras

Leia uma frase com fgets e conte quantas palavras ela tem (dica: conte os espaços e some 1).

Funções

Programas grandes não são escritos de uma vez: são montados com pecinhas pequenas, cada uma com uma tarefa. As funções permitem dar nome a um pedaço de código e reutilizá-lo quantas vezes quiser.

O que você vai aprender
  • Criar e chamar suas próprias funções
  • Parâmetros e valor de retorno
  • Protótipos
  • Variáveis locais e globais
  • Recursão, para os corajosos
  • Organizar um programa em partes

Por que usar funções?

  • Reaproveitamento: escreve uma vez, usa muitas.
  • Organização: o main fica curto e legível, como um sumário.
  • Facilidade de testar e corrigir: cada função faz uma coisa só.

Você já usa funções desde o primeiro capítulo: printf, scanf, sqrt e strlen são funções escritas por outras pessoas. Agora você vai escrever as suas.

Anatomia de uma função

  tipo do retorno   nome     parâmetros
        │             │           │
        ▼             ▼           ▼
       int        quadrado(   int n   ) {
           return n * n;    ◄── devolve o resultado
       }

Se a função não devolve nada, o tipo de retorno é void. Se não recebe nada, os parênteses ficam vazios (ou com void). Veja várias funções pequenas em ação:

O protótipo (a linha com ; no topo) apresenta a função ao compilador antes de ela ser usada. Sem ele, o compilador lê o main de cima para baixo e não sabe o que é quadrado. Se você definir as funções antes do main, os protótipos ficam dispensáveis.

Cada função tem a sua "bancada"

As variáveis criadas dentro de uma função são locais: só existem enquanto ela roda e ninguém de fora as enxerga. Quando você passa um valor como parâmetro, a função recebe uma cópia. Modificar a cópia não altera a variável original:

Essa passagem "por cópia" é segura, mas às vezes queremos que a função altere a variável original. Para isso existem os ponteiros, tema do próximo capítulo.

Aplicação: caixa eletrônico

Um programa com menu, laço e funções: o main mostra o fluxo geral, e cada operação bancária fica em uma função. As funções que alteram o saldo devolvem o novo saldo, e o main guarda o resultado.

Vetores como parâmetro

Para receber um vetor, a função declara o parâmetro com colchetes vazios e recebe o tamanho separadamente (ela não tem como descobrir sozinha):

Recursão: a função que chama a si mesma

Uma função pode chamar ela mesma para resolver um problema menor. O exemplo clássico é o fatorial: n! = n × (n−1)!, e 1! = 1. Toda função recursiva precisa de um caso base (a hora de parar), senão chama a si mesma para sempre.

Cada chamada fica "esperando" a próxima terminar, empilhadas como pratos. Quando o caso base é atingido, a pilha vai se desfazendo e as multiplicações acontecem:

fatorial(5) = 5 * fatorial(4)
                    = 4 * fatorial(3)
                          = 3 * fatorial(2)
                                = 2 * fatorial(1)
                                      = 1        ◄── caso base
  volta:  2*1 = 2  →  3*2 = 6  →  4*6 = 24  →  5*24 = 120

Resumo do capítulo

  • Função = tipo nome(parâmetros) { ... return valor; }.
  • void indica que a função não devolve nada.
  • Parâmetros de números são cópias: alterá-los não muda a variável original.
  • Variáveis locais só existem dentro da função.
  • Protótipos avisam ao compilador que a função existe; ou defina-as antes do main.
  • Recursão exige um caso base.

Exercícios

Área do retângulo

Crie a função float area(float base, float altura) e use-a no main com valores lidos do teclado.

Número primo

Crie int ehPrimo(int n), que devolve 1 se n é primo e 0 caso contrário. Depois, mostre todos os primos de 1 a 50.

Ver solução

Só precisamos testar divisores até a raiz quadrada de n, por isso i * i <= n.

Maior do vetor

Crie int maiorDoVetor(int v[], int n) e teste com um vetor de 6 números.

Calculadora com funções

Refaça a calculadora do capítulo 7 com uma função para cada operação (somar, subtrair, multiplicar, dividir).

Ponteiros e memória

É o capítulo que assusta muita gente, e não precisa. Um ponteiro é só uma variável que guarda um endereço. Entendendo isso, você desvenda o & do scanf, vetores, alocação de memória e o funcionamento real do computador.

O que você vai aprender
  • Endereços de memória e o operador &
  • Declarar ponteiros e usar *
  • Funções que alteram variáveis do main
  • A relação entre ponteiros e vetores
  • Alocar memória com malloc
  • Os perigos: ponteiro sem destino, vazamentos

Toda variável mora em um endereço

Lembra da rua com casas numeradas do capítulo 1? Cada variável ocupa uma ou mais "casas" da RAM, e cada casa tem um endereço. O operador & revela onde a variável mora. Para mostrar um endereço, usamos %p:

Ponteiro: uma variável que guarda um endereço

O ponteiro p guarda o número 1000: o endereço de idade. Dizemos que p "aponta para" idade.

SímboloLê-seSignificado
&x"endereço de x"Onde a variável x mora na memória
int *p"p é ponteiro para int"Declara uma variável que guarda endereço de um int
*p"o conteúdo de p"Acessa (ler ou alterar) o que está no endereço guardado em p

Agora o scanf faz sentido

Quando escrevemos scanf("%d", &idade), entregamos ao scanf o endereço da variável. Com o endereço em mãos, ele consegue guardar o valor digitado exatamente lá. Se entregássemos só idade, ele receberia uma cópia e não teria como alterar a original. Por isso o & é obrigatório (e, para textos, o nome do vetor já é um endereço, por isso não usamos &).

Uso 1: funções que alteram variáveis de fora

No capítulo 11, vimos que a função recebe uma cópia. Se passarmos o endereço da variável, a função pode alterar a original. O exemplo clássico é trocar o valor de duas variáveis:

Uso 2: devolver vários resultados

Uma função só pode devolver um valor com return. Com ponteiros, ela pode "devolver" vários pelos parâmetros. Vamos refazer a conversão de segundos do capítulo 6, mas agora com uma função:

Ponteiros e vetores são parentes

O nome de um vetor é, na prática, o endereço do seu primeiro elemento. Por isso um vetor passado a uma função não é copiado: a função recebe o endereço e trabalha no original (foi o que vimos no capítulo 11). Somar 1 a um ponteiro o move para o próximo elemento, e v[i] é o mesmo que *(v + i):

Uso 3: pedir memória quando o programa já está rodando

Até agora, o tamanho de todo vetor era decidido antes de rodar. E se você só souber quantas notas terá depois que o usuário responder? Com malloc o programa pede memória à RAM no momento certo e recebe o endereço do bloco. Depois de usar, devolve com free.

Cuidados com ponteiros

  • Ponteiro sem destino: int *p; aponta para um lugar aleatório. Usar *p assim pode derrubar o programa. Inicialize sempre: com um endereço válido ou com NULL (ninguém).
  • Teste o NULL: depois de malloc, confira se deu certo, como no exemplo acima.
  • Não use memória depois do free. O endereço continua no ponteiro, mas o espaço já não é seu.
  • Respeite os limites do bloco alocado, como nos vetores.

Resumo do capítulo

  • &x é o endereço de x; um ponteiro (int *p) guarda um endereço.
  • *p acessa o valor no endereço; *p = 5 altera a variável original.
  • Ponteiros permitem que funções alterem variáveis de fora e devolvam vários resultados.
  • O nome de um vetor é o endereço do primeiro elemento.
  • malloc reserva memória em tempo de execução; free a devolve.

Exercícios

Qual é a saída?

Sem rodar, diga o que o programa mostra:

Ver resposta

*q vale 10 (é o b); então *p = 11, ou seja, a passa a valer 11. Mostra: 11 10.

Dobrar por ponteiro

Escreva void dobrar(int *n), que multiplica por 2 a variável passada. Compare com a função dobrar do capítulo 11.

Mínimo e máximo

Escreva void minmax(int v[], int n, int *min, int *max), que devolve o menor e o maior valor do vetor pelos ponteiros.

Vetor dinâmico

Leia n, aloque um vetor de n inteiros com malloc, preencha com números aleatórios de 1 a 100 e mostre-os. Não esqueça do free.

Structs: criando seus tipos

Um jogador tem nome, pontos e nível. Um produto tem nome, preço e estoque. Em vez de espalhar esses dados em variáveis soltas, uma struct agrupa tudo em uma "ficha" única.

O que você vai aprender
  • Definir uma struct com typedef
  • Acessar campos com . e ->
  • Criar vetores de structs
  • Passar structs para funções
  • Como uma struct ocupa a memória

Uma ficha de cadastro

O typedef dá um nome curto ao molde, e depois usamos Jogador como qualquer outro tipo (int, float). O ponto (.) escolhe qual campo da ficha acessar.

Aplicação: ranking de jogadores

Combinando struct, vetor e laço, montamos um ranking: lemos os jogadores, mostramos uma tabela e descobrimos o campeão. A formatação %-10s alinha o texto à esquerda em 10 colunas:

Repare em j[i].nome: primeiro escolhemos a ficha do vetor (j[i]) e depois o campo dela (.nome). Como nome já é um vetor de char, o scanf dispensa o &; já pontos precisa dele.

Structs e funções

Passar uma struct por valor copia a ficha inteira. Se a função precisa alterar a original (ou se a struct é grande e queremos evitar a cópia), passamos um ponteiro para ela e usamos a seta -> para acessar os campos: j->pontos é o mesmo que (*j).pontos.

Como uma struct ocupa a memória

Os campos ficam um depois do outro na RAM. Vamos perguntar ao compilador o tamanho de uma ficha de personagem:

Resumo do capítulo

  • typedef struct { ... } Nome; cria um novo tipo com vários campos.
  • Acesse campos com variavel.campo; com ponteiro, use ponteiro->campo.
  • Vetores de structs guardam muitas fichas: j[i].campo.
  • Use ponteiros para que funções alterem a struct original.

Exercícios

Estoque da loja

Crie uma struct Produto (nome, preço, quantidade). Leia 4 produtos e mostre o valor total do estoque (soma de preço × quantidade).

Melhor estudante

Struct Estudante com nome e duas notas. Leia 5 estudantes e mostre quem teve a maior média.

Pontos no plano

Struct Ponto com x e y. Escreva a função float distancia(Ponto a, Ponto b), usando a fórmula do capítulo 6.

Ranking ordenado

Adapte o ranking para mostrar os jogadores em ordem decrescente de pontos, usando bubble sort sobre o vetor de structs.

Ver dica

É o mesmo algoritmo do capítulo 9, só que a variável auxiliar deve ser do tipo Jogador e a comparação usa j[k].pontos.

Trabalhando com arquivos

A RAM esquece tudo quando o programa fecha. Para guardar recordes, cadastros e anotações, o programa precisa escrever no disco. Neste capítulo você faz seus programas terem memória permanente.

O que você vai aprender
  • Abrir, escrever, ler e fechar arquivos de texto
  • Os modos "r", "w" e "a"
  • Verificar se a abertura deu certo
  • Guardar um placar e um diário
  • Salvar um recorde entre uma partida e outra

Arquivo é como uma fita de texto

Trabalhar com arquivos lembra usar um caderno: você abre, escreve ou lê e fecha. Em C, temos um ponteiro do tipo FILE * que representa o arquivo aberto, e funções muito parecidas com as que você já conhece:

Já conhecidaVersão para arquivosO que faz
printffprintf(arq, ...)escreve texto formatado no arquivo
scanffscanf(arq, ...)lê dados formatados do arquivo
fgets (teclado)fgets(v, n, arq)lê uma linha do arquivo
–fopen e fcloseabre e fecha o arquivo
ModoSignificadoCuidado
"r"Ler (read)Se o arquivo não existe, fopen devolve NULL
"w"Escrever (write)Apaga o conteúdo anterior se o arquivo já existir
"a"Acrescentar (append)Escreve no final, sem apagar o que já existe

Salvando um placar

Este programa guarda o nome e a pontuação em um arquivo de texto. Como usamos o modo "a", cada execução acrescenta uma linha nova:

Depois de rodar duas ou três vezes com nomes diferentes, abra a pasta do programa: aparece o arquivo placar.txt. Você pode abri-lo no Bloco de Notas para conferir, porque é texto puro.

Lendo o placar de volta

Para ler, abrimos com "r" e repetimos fscanf enquanto ele conseguir ler dois valores (o nome e os pontos). Lembre que o scanf devolve quantos itens leu, e o fscanf também:

Diário com data e hora

Para ler ou escrever frases com espaços, usamos fgets para digitar e fprintf para gravar. A biblioteca time.h fornece a data e a hora atuais:

Para mostrar o diário inteiro, leia linha por linha com fgets, que devolve NULL quando o arquivo acaba:

Aplicação: recorde que nunca se perde

Todo jogo guarda a maior pontuação. Este programa lê o recorde salvo (se existir), compara com a pontuação da partida e só regrava o arquivo se o recorde for batido. Note o uso do modo "w" para substituir o valor antigo:

Resumo do capítulo

  • FILE *arq = fopen("nome.txt", "modo"); abre; fclose(arq); fecha.
  • Modos: "r" lê, "w" escreve (apaga), "a" acrescenta.
  • fprintf escreve, fscanf e fgets leem.
  • Sempre teste arq == NULL após o fopen.

Exercícios

Lista de números

Leia 5 números e grave-os em numeros.txt, um por linha.

Estatísticas do arquivo

Escreva um programa que leia numeros.txt e mostre a soma, a média e o maior valor.

Contador de linhas

Conte quantas linhas tem o arquivo diario.txt.

Ver dica

Dentro do while (fgets(...) != NULL), some 1 a um contador.

Agenda de contatos

Grave nome e telefone (sem espaços) em agenda.txt no modo "a". Crie um segundo programa que busca um nome digitado e mostra o telefone.

Mini projetos

Chegou a hora de juntar tudo o que você aprendeu. Estes quatro projetos usam variáveis, decisões, laços, vetores, strings, funções, structs e arquivos. Leia, rode, mude e melhore.

O que você vai fazer
  • Jogo de pedra, papel e tesoura
  • Gerador de senhas
  • Lista de tarefas que salva em arquivo
  • Conversor de decimal para binário

Projeto 1: Pedra, papel e tesoura

Você joga contra o computador em cinco rodadas. Usa números aleatórios, funções, switch, um laço com validação e placar. A função nomeJogada devolve um texto fixo (o tipo const char * significa "endereço de um texto que não vamos alterar").

Desafios: permita escolher o número de rodadas; mostre a porcentagem de vitórias; jogue "melhor de 3" e pare assim que alguém ganhar duas rodadas.

Projeto 2: Gerador de senhas

Uma aplicação real de vetores de caracteres e números aleatórios: a cada posição da senha, sorteamos um caractere da lista de símbolos permitidos.

Desafios: peça quantos tipos de caracteres usar (só letras, letras e números, tudo); garanta que a senha tenha ao menos um dígito; gere 5 senhas de uma vez.

Projeto 3: Lista de tarefas

O projeto mais completo: usa struct, vetor, funções, ponteiros e arquivos. As tarefas ficam em tarefas.txt: você fecha o programa, abre de novo e elas continuam lá. Repare que as funções que precisam alterar o total de tarefas recebem um ponteiro (int *total).

Desafios: adicione a opção "Remover tarefa"; mostre quantas tarefas estão pendentes; ordene para mostrar as pendentes primeiro.

Projeto 4: Conversor de decimal para binário

Fechando o ciclo com o capítulo 1: como o computador enxerga o número que você digita? Dividindo sucessivamente por 2, os restos formam os bits, do último para o primeiro:

Confira no laboratório do capítulo 1: 01000001 é o número 65, a letra A.

Ideias para continuar

  • Jogo da forca: uma palavra secreta em uma string, um vetor com as letras já descobertas e um contador de erros.
  • Jogo da velha completo: a matriz do capítulo 9 com jogadas alternadas e verificação de vitória.
  • Quiz de conhecimentos: perguntas em um vetor de structs, com pontuação e recorde salvo em arquivo.
  • Controle de gastos: registre despesas em arquivo e mostre o total por categoria.
  • Batalha por turnos: dois personagens (structs) com vida e ataque aleatório.

E agora?

Você chegou ao fim da introdução. Sabe declarar variáveis, decidir, repetir, organizar em funções, guardar dados em vetores, structs e arquivos, e ainda entende como a memória funciona. Próximos passos naturais: estruturas de dados (listas, pilhas, filas), algoritmos de busca e ordenação, C++ e programação orientada a objetos, e treinar em maratonas de programação, como a Olimpíada Brasileira de Informática (OBI).

Exercícios finais

Escolha e construa

Escolha uma das ideias acima (ou uma sua) e desenvolva em etapas: primeiro escreva em português o que o programa deve fazer, depois quebre em funções, depois programe uma função por vez, testando cada uma.

Revise um colega

Troque um projeto com um colega. Leia o código dele, tente prever o que acontece e sugira uma melhoria de legibilidade (nomes melhores, comentários, funções menores).

Erros comuns e como resolver

Ninguém escreve código sem erros. A diferença entre iniciantes e experientes é que os experientes sabem ler a mensagem e procurar no lugar certo. Use este apêndice como um dicionário de erros.

Mensagens do compilador

Mensagem (aproximada)O que significaComo resolver
expected ';' before ...Faltou um ponto e vírgulaOlhe a linha indicada e a anterior
'x' undeclared (first use in this function)Você usou uma variável que não declarou (ou errou o nome)Declare-a ou corrija a digitação; lembre que maiúsculas importam
implicit declaration of function 'printf'Usou uma função sem incluir a bibliotecaAdicione o #include certo (stdio.h, math.h, string.h...)
expected declaration or statement at end of inputFalta fechar uma chave }Confira se cada { tem seu }; a indentação ajuda
missing terminating " characterFaltam as aspas de fechamento de um textoFeche as aspas na mesma linha
stray '\342' in program (ou outros números)Há um caractere estranho, geralmente aspas "curvas" copiadas de um editor de textoApague as aspas e digite de novo as aspas retas "
assignment to expression with array typeTentou fazer nome = "Ana"; em um vetor de charUse strcpy(nome, "Ana");
lvalue required as left operand of assignmentTentou atribuir a algo que não é uma variávelProvavelmente trocou == por = ou vice-versa
warning: format '%d' expects argument of type 'int', but argument 2 has type 'double'O código do printf/scanf não combina com o tipo da variávelUse %d para int, %f para float, %lf para double no scanf
warning: unused variable 'x'Declarou e nunca usouApague ou use
control reaches end of non-void functionUma função com retorno pode terminar sem returnGaranta um return em todos os caminhos
undefined reference to 'main'Não achou a função mainConfira se escreveu main em minúsculas

Problemas na hora de executar

SintomaCausa provável
O programa mostra um número estranho e enormeVariável usada sem valor inicial, ou formato errado no printf (por exemplo, %d para um float)
O programa nunca terminaLaço infinito: a condição nunca fica falsa. Feche a janela ou use Ctrl+C e revise o laço
O console fecha de repente com return value 3221225477 (0xC0000005)Acesso inválido à memória: scanf sem &, índice fora do vetor, ponteiro sem destino
O resultado da divisão perde as casas decimaisDivisão entre inteiros. Use (float) ou um número decimal
O fgets "pula" a leituraSobrou um Enter de um scanf anterior. Use getchar() ou limparBuffer()
Acentos aparecem como símbolosFalta o setlocale(LC_CTYPE, "Portuguese"); ou a codificação do arquivo está diferente do console
Números decimais não são lidos (digitou 3.5 e leu 3)Foi usado LC_ALL no setlocale. Troque por LC_CTYPE

Método para caçar bugs

  1. Leia a mensagem inteira, começando pelo primeiro erro.
  2. Reproduza: descubra qual entrada faz o problema aparecer.
  3. Faça o teste de mesa no papel para o trecho suspeito.
  4. Imprima para investigar: coloque printf temporários para ver o valor das variáveis em cada ponto.
  5. Isole: comente partes do código até achar qual delas causa o problema.
  6. Explique o código em voz alta para alguém (ou para um objeto). Muitas vezes você acha o erro enquanto explica.

Cola rápida

Um resumo de consulta com a sintaxe de tudo que você viu. Use os botões de copiar para colar os trechos no seu código.

Estrutura e entrada/saída

Modelo de programa

Entrada e saída

Controle de fluxo

if / else if / else

switch

Laços

Vetor e matriz

Funções, ponteiros e structs

Função

Ponteiros

Struct

Arquivos

Bibliotecas mais usadas

BibliotecaPrincipais funções
stdio.hprintf, scanf, fgets, getchar, fopen, fclose, fprintf, fscanf
stdlib.hrand, srand, malloc, free, system
string.hstrlen, strcpy, strcat, strcmp, strcspn
math.hsqrt, pow, fabs, floor, ceil
ctype.htoupper, tolower, isdigit, isalpha, isupper
time.htime, localtime, strftime
locale.hsetlocale

Formatos de printf e scanf

Tipoprintfscanf
int%d%d
float%f / %.2f%f
double%f / %.2f%lf
char%c" %c"
string%s%s (sem &)
endereço%p–

Glossário

Os termos que apareceram ao longo do guia, em ordem alfabética, com explicações curtas.

Algoritmo
Sequência finita e ordenada de passos claros que resolve um problema.
ASCII
Tabela que associa um número a cada caractere básico (A = 65, a = 97, espaço = 32).
Bit
Menor unidade de informação do computador: vale 0 ou 1.
Buffer
Área de memória que guarda temporariamente dados, como o que foi digitado no teclado antes de o programa ler.
Byte
Grupo de 8 bits; guarda valores de 0 a 255.
Compilador
Programa que traduz o código-fonte em código de máquina (executável).
Constante
Valor que não muda durante a execução (const ou #define).
Depuração (debug)
Processo de encontrar e corrigir erros (bugs) em um programa.
Endereço
Número que identifica a posição de um byte na memória.
Escopo
A região do programa em que uma variável existe e pode ser usada.
Executável
Arquivo (.exe no Windows) que o computador consegue rodar diretamente.
Função
Bloco de código com nome, que recebe parâmetros e pode devolver um resultado.
Heap
Área da memória usada para alocação dinâmica com malloc.
IDE
Ambiente de desenvolvimento integrado: editor, compilador e depurador juntos (como Dev-C++, Code::Blocks, CLion ou VS Code com as extensões certas).
Índice
Número que indica a posição de um elemento em um vetor; começa em 0.
Laço (loop)
Estrutura que repete um bloco de código: for, while, do-while.
Overflow
Quando um valor ultrapassa o limite do tipo e "dá a volta".
Parâmetro
Variável de uma função que recebe o valor enviado por quem a chamou.
Ponteiro
Variável que guarda o endereço de outra variável.
Protótipo
Declaração de uma função (sem o corpo) que a apresenta ao compilador.
RAM
Memória de trabalho do computador: rápida e volátil.
Recursão
Técnica em que uma função chama a si mesma.
Retorno
O valor que uma função devolve com return.
String
Texto: vetor de char terminado em '\0'.
Struct
Tipo composto que agrupa campos de tipos diferentes sob um nome.
Teste de mesa
Simulação no papel da execução de um código, acompanhando os valores das variáveis.
Tipo de dado
Define o que uma variável pode guardar e quantos bytes ocupa: int, float, char...
Variável
Espaço nomeado da memória que guarda um valor que pode mudar.
Vetor (array)
Conjunto de elementos do mesmo tipo guardados em sequência na memória.