Sua dose diária de programação

Conceito, 3 exemplos e 3 exercícios: uma pequena conquista de cada vez.

✦   Lição

Variáveis e tipos básicos

Uma variável é um espaço na memória com nome e tipo. Em C, toda variável precisa ser declarada antes do uso, com o tipo na frente: int vidas = 3;. O tipo define quanto espaço é reservado e como os bits são interpretados, e não muda depois da declaração.

C Fundamentos Iniciante 3 exemplos + 3 exercícios
42 lições publicadas 10 linguagens novas lições todo dia útil
C / Fundamentos

Variáveis e tipos básicos

por Huxley Sterling
Iniciante

Entenda o conceito

Uma variável é um espaço na memória com nome e tipo. Em C, toda variável precisa ser declarada antes do uso, com o tipo na frente: int vidas = 3;. O tipo define quanto espaço é reservado e como os bits são interpretados, e não muda depois da declaração.

Os tipos básicos são int para inteiros, double (e o menos preciso float) para números com parte decimal e char para um único caractere, escrito entre aspas simples ('A'). Há variações como long long, para inteiros grandes, e unsigned, que troca os negativos por um limite positivo maior.

O tipo também decide o resultado das contas: entre dois int, a divisão é inteira e descarta a parte decimal (7 / 2 dá 3), e o operador % devolve o resto. Para manter as casas decimais, um dos operandos precisa ser double, seja por uma constante como 2.0 ou por um cast, como (double)total.

Uma variável declarada sem valor contém lixo de memória, então inicialize sempre. Valores que não devem mudar ganham const, e o compilador recusa qualquer tentativa de alterá-los.

↳ Pontos-chave
  • Declare com tipo, nome e valor inicial (int pontos = 0;); ler uma variável não inicializada dá um valor imprevisível.
  • Use int para contagens, double para valores com parte decimal e char para um caractere entre aspas simples.
  • Cada tipo tem seu especificador no printf: %d para int, %f para double, %c para char e %lld para long long.
  • Divisão entre inteiros descarta a parte decimal; converta um operando com (double) antes de dividir para manter as casas.
  • const torna o valor somente leitura, e o compilador acusa erro se o código tentar alterá-lo.

Do conceito à prática

3 exemplos para ver a ideia funcionando.

01Placar do jogo com int, double e char

placar.c
/* placar.c: declarar, atribuir e imprimir variáveis */
#include <stdio.h>

int main(void)
{
    /* Declaração com inicialização: tipo, nome e valor */
    int vidas = 3;
    int pontos = 1200;
    double tempo = 87.5;   /* segundos, com parte decimal */
    char rank = 'B';       /* um único caractere, entre aspas simples */

    printf("Vidas: %d | Pontos: %d\n", vidas, pontos);
    printf("Tempo: %.1f s | Rank: %c\n", tempo, rank);

    /* A variável pode receber um novo valor depois */
    vidas = vidas - 1;
    pontos = pontos + 350;
    rank = 'A';

    puts("Depois da fase 2:");
    printf("Vidas: %d | Pontos: %d | Rank: %c\n", vidas, pontos, rank);
    return 0;
}

O exemplo declara variáveis de três tipos já com valor inicial e imprime cada uma com o especificador correspondente: %d para int, %.1f para double e %c para char.

Depois, as mesmas variáveis recebem novos valores com =. A declaração (com o tipo) acontece uma vez só; nas atribuições seguintes, o tipo não é repetido.

02Ajustando porções: divisão inteira e cast

porcoes.c
/* porcoes.c: divisão inteira, conversão e constantes */
#include <stdio.h>

int main(void)
{
    /* const impede que o valor seja alterado por engano */
    const int PORCOES_RECEITA = 4;
    int ovos = 3;
    double farinha_g = 250.0;

    int porcoes_desejadas = 6;

    /* Só inteiros na conta: o resultado é int e a parte decimal some */
    int ovos_inteiro = ovos * porcoes_desejadas / PORCOES_RECEITA;

    /* Com um double na conta, a divisão mantém as casas decimais */
    double ovos_exato = (double)ovos * porcoes_desejadas / PORCOES_RECEITA;
    double farinha = farinha_g * porcoes_desejadas / PORCOES_RECEITA;

    printf("Receita para %d porções\n", porcoes_desejadas);
    printf("Ovos (conta inteira): %d\n", ovos_inteiro);
    printf("Ovos (conta exata): %.1f\n", ovos_exato);
    printf("Farinha: %.1f g\n", farinha);

    /* O operador % devolve o resto da divisão inteira */
    printf("Resto da conta inteira: %d\n",
           ovos * porcoes_desejadas % PORCOES_RECEITA);
    return 0;
}

Para ajustar a receita de 4 para 6 porções, 3 * 6 / 4 com inteiros dá 4, porque 18 / 4 descarta o .5; o % mostra o resto 2 que ficou de fora. Com (double)ovos na frente, a conta inteira passa a ser feita em ponto flutuante e dá 4.5. A farinha já é double, então a conta dela não perde nada.

A quantidade de porções da receita original está em uma const int: o nome explica o número e protege o valor contra alterações acidentais.

03Inventário: char como número, unsigned e sizeof

inventario.c
/* inventario.c: char é número, unsigned, long long e sizeof */
#include <stdio.h>

int main(void)
{
    /* char guarda um código numérico: %c mostra a letra, %d o número */
    char slot = 'A';
    printf("Slot %c tem código %d\n", slot, slot);
    slot = slot + 2;
    printf("Dois slots depois: %c\n", slot);

    /* unsigned char vai de 0 a 255 e "dá a volta" ao passar do limite */
    unsigned char flechas = 250;
    flechas = flechas + 10;
    printf("Flechas depois de somar 10: %d\n", flechas);

    /* long long comporta valores grandes; LL marca a constante */
    long long ouro = 5000000000LL;
    printf("Ouro acumulado: %lld\n", ouro);

    /* float tem menos precisão que double */
    float peso_f = 0.1f;
    double peso_d = 0.1;
    printf("float:  %.10f\n", peso_f);
    printf("double: %.10f\n", peso_d);

    /* sizeof devolve o tamanho em bytes (um size_t, impresso com %zu) */
    printf("char: %zu, int: %zu, double: %zu bytes\n",
           sizeof(char), sizeof(int), sizeof(double));
    return 0;
}

Um char é um inteiro pequeno: 'A' guarda o código 65, e somar 2 leva a 'C'. Já o unsigned char vai de 0 a 255, então 250 + 10 dá a volta e vira 4; tipos unsigned fazem essa conta módulo o limite em vez de ficar negativos. Para valores acima do limite do int, como 5 bilhões, use long long com %lld.

As duas linhas de peso mostram que 0.1 não tem representação exata em binário e que o float erra antes do double (algo como 0.1000000015 contra 0.1000000000). Por fim, sizeof informa quantos bytes cada tipo ocupa; o valor de int depende da plataforma, por isso ele é perguntado ao compilador em vez de suposto.


Agora é com você

Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.

01Ficha da receita de pão de queijoFácil

Guarde os dados de uma receita em variáveis e imprima a ficha exatamente como na saída esperada. Nenhum número ou letra da ficha pode ser escrito direto no texto do printf.

↳ Requisitos
  • Usar int para porções, tempo e temperatura, double para o polvilho e char para a dificuldade.
  • Inicializar todas as variáveis na declaração.
  • Imprimir cada valor com o especificador do seu tipo (%d, %.2f e %c).
código inicial
#include <stdio.h>

int main(void)
{
    /* Declare aqui as variáveis da receita */

    puts("Receita: Pão de queijo");
    /* Imprima as demais linhas usando as variáveis */

    return 0;
}
saída esperada
Receita: Pão de queijo
Porções: 6
Polvilho (kg): 0.50
Tempo de forno (min): 25
Temperatura: 180 graus
Dificuldade: B
Ver dica

Pergunte a cada dado: ele tem parte decimal? É uma letra só? A resposta escolhe o tipo, e o tipo escolhe o especificador.

Ver resposta
#include <stdio.h>

int main(void)
{
    /* Cada informação fica numa variável do tipo adequado */
    int porcoes = 6;
    double polvilho_kg = 0.5;
    int tempo_min = 25;
    int temperatura = 180;
    char dificuldade = 'B';

    puts("Receita: Pão de queijo");
    printf("Porções: %d\n", porcoes);
    printf("Polvilho (kg): %.2f\n", polvilho_kg);      /* duas casas */
    printf("Tempo de forno (min): %d\n", tempo_min);
    printf("Temperatura: %d graus\n", temperatura);
    printf("Dificuldade: %c\n", dificuldade);          /* %c para char */
    return 0;
}

Passo a passo

  1. As cinco variáveis são declaradas no início de main, cada uma com tipo, nome e valor inicial; declarar e inicializar na mesma linha evita usar uma variável com lixo de memória.
  2. Porções, tempo e temperatura são contagens sem parte decimal, por isso usam int.
  3. A quantidade de polvilho tem parte decimal, então fica em double, o tipo de ponto flutuante padrão em C.
  4. A dificuldade é uma única letra: char com o valor entre aspas simples ('B').
  5. O título não depende de variável, então puts basta; as demais linhas usam printf com o especificador que combina com o tipo: %d para int, %.2f para double e %c para char.
  6. %.2f mostra 0.5 como 0.50, com as duas casas pedidas na saída.

Por que funciona

Uma variável é um espaço na memória com nome e tipo. O tipo diz ao compilador quanto espaço reservar e como interpretar os bits, e diz a você qual especificador usar no printf. Escolhendo o tipo certo para cada dado, os valores saem exatamente como foram guardados.

Erros comuns

  • Aspas duplas no char: char dificuldade = "B"; tenta guardar o endereço de uma string num char; o GCC avisa e a saída sai errada. Use 'B'.
  • Especificador trocado: printf("Polvilho (kg): %d\n", polvilho_kg); passa um double para %d, comportamento indefinido que costuma imprimir lixo. Use %f (aqui %.2f).
  • Guardar 0.5 num int: int polvilho_kg = 0.5; descarta a parte decimal e a variável vale 0. Use double.
  • Usar a variável sem inicializar: int porcoes; printf("%d", porcoes); imprime um valor imprevisível. Sempre dê um valor antes de ler.

Outra forma de resolver

Dá para declarar várias variáveis do mesmo tipo numa linha só:

int porcoes = 6, tempo_min = 25, temperatura = 180;

É compacto e útil para valores relacionados, mas uma declaração por linha facilita comentar e encontrar cada variável.

Saída esperada

Receita: Pão de queijo
Porções: 6
Polvilho (kg): 0.50
Tempo de forno (min): 25
Temperatura: 180 graus
Dificuldade: B

02Média do boletim com e sem castMédio

Calcule a média das notas 7, 8 e 10 de um aluno de duas formas: com divisão inteira e com cast para double, e imprima as duas para comparar.

↳ Requisitos
  • Guardar as três notas e a soma em variáveis int.
  • Calcular a média inteira com soma / 3 e guardá-la num int.
  • Calcular a média real com (double)soma / 3 e mostrá-la com %.2f.
código inicial
#include <stdio.h>

int main(void)
{
    int nota1 = 7;
    int nota2 = 8;
    int nota3 = 10;
    /* Calcule a soma e as duas médias */

    puts("Boletim de Rafa");
    /* Imprima soma, média inteira e média real */

    return 0;
}
saída esperada
Boletim de Rafa
Soma das notas: 25
Média (divisão inteira): 8
Média (com cast): 8.33
Ver dica

O tipo da divisão é decidido pelos operandos, não pela variável que recebe o resultado.

Ver resposta
#include <stdio.h>

int main(void)
{
    int nota1 = 7;
    int nota2 = 8;
    int nota3 = 10;
    int soma = nota1 + nota2 + nota3;

    /* int / int: a parte decimal é descartada */
    int media_inteira = soma / 3;

    /* O cast converte soma para double ANTES da divisão */
    double media_real = (double)soma / 3;

    puts("Boletim de Rafa");
    printf("Soma das notas: %d\n", soma);
    printf("Média (divisão inteira): %d\n", media_inteira);
    printf("Média (com cast): %.2f\n", media_real);
    return 0;
}

Passo a passo

  1. As três notas são inteiras, então ficam em variáveis int; a soma de inteiros também é int e vale 25.
  2. soma / 3 divide dois inteiros: o C faz divisão inteira e descarta a parte decimal, resultando em 8.
  3. (double)soma é um cast: converte o valor de soma para double só nessa expressão, sem mudar a variável.
  4. Como um dos operandos agora é double, o 3 também é convertido e a divisão dá 8.333....
  5. %d imprime a média inteira e %.2f arredonda a média real para 8.33.
  6. O título não tem valores, então usa puts.

Por que funciona

O tipo do resultado de uma operação depende dos tipos dos operandos. Entre dois int, a divisão é inteira; se pelo menos um for double, o outro é promovido e a conta é feita em ponto flutuante. O cast é a forma explícita de dizer ao compilador "trate este valor como double aqui".

Erros comuns

  • Guardar em double sem converter: double media_real = soma / 3; ainda dá 8.00, porque a divisão inteira acontece antes da atribuição. Converta um operando: (double)soma / 3.
  • Cast no lugar errado: (double)(soma / 3) converte o resultado já truncado e também dá 8.00. O cast precisa vir antes da divisão.
  • %d com double: printf("%d\n", media_real); é comportamento indefinido e imprime lixo; o -Wall avisa. Use %f.

Outra forma de resolver

Em vez do cast, use uma constante double no divisor:

double media_real = soma / 3.0;

Funciona porque 3.0 já é double. O cast é preferível quando o divisor também é uma variável int, como (double)soma / quantidade.

Saída esperada

Boletim de Rafa
Soma das notas: 25
Média (divisão inteira): 8
Média (com cast): 8.33

03Relógio da partida em horas, minutos e segundosDesafio

Uma partida durou 9876 segundos. Converta esse total para o formato HH:MM:SS, mostre quantos minutos completos ele tem e quanto vale em horas com duas casas decimais.

↳ Requisitos
  • Declarar SEGUNDOS_POR_MINUTO e SEGUNDOS_POR_HORA como const int e usá-las em todas as contas.
  • Separar horas, minutos e segundos só com os operadores / e % sobre inteiros.
  • Mostrar o tempo formatado com %02d em cada parte.
  • Calcular as horas decimais com cast para double e exibir com %.2f.
código inicial
#include <stdio.h>

int main(void)
{
    const int SEGUNDOS_POR_MINUTO = 60;
    const int SEGUNDOS_POR_HORA = 3600;
    int total = 9876;

    /* Calcule horas, minutos, segundos, minutos completos e horas decimais */

    return 0;
}
saída esperada
Tempo total: 9876 s
Formatado: 02:44:36
Minutos completos: 164
Em horas: 2.74 h
Ver dica

Primeiro tire as horas cheias e guarde o que sobrou com %; os minutos e segundos saem dessa sobra.

Ver resposta
#include <stdio.h>

int main(void)
{
    /* Constantes com nome no lugar de números mágicos */
    const int SEGUNDOS_POR_MINUTO = 60;
    const int SEGUNDOS_POR_HORA = 3600;

    int total = 9876;

    /* Divisão inteira dá as horas cheias; % dá o que sobra */
    int horas = total / SEGUNDOS_POR_HORA;
    int resto = total % SEGUNDOS_POR_HORA;
    int minutos = resto / SEGUNDOS_POR_MINUTO;
    int segundos = resto % SEGUNDOS_POR_MINUTO;

    int minutos_completos = total / SEGUNDOS_POR_MINUTO;

    /* Cast para manter a parte decimal das horas */
    double horas_decimais = (double)total / SEGUNDOS_POR_HORA;

    printf("Tempo total: %d s\n", total);
    printf("Formatado: %02d:%02d:%02d\n", horas, minutos, segundos);
    printf("Minutos completos: %d\n", minutos_completos);
    printf("Em horas: %.2f h\n", horas_decimais);
    return 0;
}

Passo a passo

  1. const int cria valores que não podem ser alterados depois; nomear 60 e 3600 deixa claro o que cada conta faz e o compilador recusa uma atribuição acidental.
  2. total / SEGUNDOS_POR_HORA é divisão inteira: 9876 / 3600 dá 2 horas cheias.
  3. total % SEGUNDOS_POR_HORA devolve o resto, 2676 segundos que não completam uma hora.
  4. O mesmo par / e % aplicado ao resto separa 44 minutos e 36 segundos.
  5. total / SEGUNDOS_POR_MINUTO dá os minutos completos do tempo todo: 164.
  6. (double)total / SEGUNDOS_POR_HORA converte antes de dividir, então o resultado é 2.7433..., mostrado como 2.74 por %.2f.
  7. %02d imprime o inteiro com pelo menos 2 dígitos, completando com zero à esquerda: 2 vira 02.

Por que funciona

Aqui a divisão inteira, que costuma ser armadilha, é exatamente o que se quer: / entre inteiros dá a quantidade de unidades cheias e % dá a sobra. Encadeando os dois, de horas para minutos e de minutos para segundos, o total se decompõe sem perder nenhum segundo (2 × 3600 + 44 × 60 + 36 = 9876).

Quando a parte decimal importa, como nas horas em formato decimal, o cast para double muda o tipo da conta. A mesma variável int serve aos dois casos; o que decide o resultado é o tipo dos operandos em cada expressão.

Erros comuns

  • Calcular os minutos a partir do total: int minutos = total / SEGUNDOS_POR_MINUTO; dá 164, não 44, porque inclui os minutos das horas já contadas. Use o resto: resto / SEGUNDOS_POR_MINUTO.
  • Esquecer o 0 em %02d: %2d completa com espaço e a saída fica 2:44:36. O 0 é o que preenche com zeros.
  • Dividir sem cast: double horas_decimais = total / SEGUNDOS_POR_HORA; faz divisão inteira e mostra 2.00. Converta um operando antes.
  • Tentar alterar a constante: SEGUNDOS_POR_HORA = 60; não compila (assignment of read-only variable). Se o valor muda, ele não deveria ser const.

Outra forma de resolver

As constantes também podem ser macros do pré-processador:

#define SEGUNDOS_POR_MINUTO 60
#define SEGUNDOS_POR_HORA 3600

O #define troca o nome pelo valor antes da compilação e era o estilo clássico em C; ele é preferível quando o valor precisa ser uma constante em tempo de compilação, como o tamanho de um vetor global. Para contas como estas, const int tem tipo e respeita escopo.

Saída esperada

Tempo total: 9876 s
Formatado: 02:44:36
Minutos completos: 164
Em horas: 2.74 h

Últimas lições

Ver o arquivo completo →