Aula 02 – Variáveis e Tipos de Dados

int, float, double, char, bool — tipagem estática, memória e comparação com Java

O que é uma Variável?

Uma variável é um espaço reservado na memória do computador que recebe um nome e armazena um valor que pode ser alterado durante a execução do programa. Em C, antes de usar qualquer variável, é obrigatório declará-la indicando seu tipo de dado.

Em C, toda variável deve ser declarada com um tipo antes de ser usada. O tipo determina quanto espaço de memória será reservado e quais operações são válidas sobre aquele dado.

A sintaxe básica de declaração é:

tipo nomeDaVariavel;                   // declaração
tipo nomeDaVariavel = valorInicial;    // declaração com inicialização

Exemplos práticos:

int idade = 25;          // inteiro
float altura = 1.75;     // ponto flutuante simples
double massa = 70.345;   // ponto flutuante duplo
char inicial = 'A';      // caractere único
Tipagem estática: C é uma linguagem estaticamente tipada — o tipo de cada variável é definido em tempo de compilação e não muda durante a execução. Isso permite ao compilador verificar erros de tipo antes mesmo do programa rodar, além de otimizar o uso de memória.

Tipos Inteiros

Os tipos inteiros armazenam números sem parte fracionária. C oferece quatro variações, com diferentes tamanhos e faixas de valores:

Tipo Tamanho típico Faixa de valores (signed) Uso típico
short 2 bytes (16 bits) −32.768 a 32.767 Contadores pequenos, economia de memória
int 4 bytes (32 bits) −2.147.483.648 a 2.147.483.647 Uso geral — o tipo inteiro padrão
long 4 ou 8 bytes Depende da plataforma Inteiros grandes em sistemas 32-bit
long long 8 bytes (64 bits) −9,2 × 10¹⁸ a 9,2 × 10¹⁸ Valores muito grandes (IDs, timestamps)
Variantes unsigned: Todos os tipos inteiros aceitam o modificador unsigned, que elimina os valores negativos e dobra o limite superior. Exemplo: unsigned int vai de 0 a 4.294.967.295.
#include <stdio.h>

int main() {
    short   s = 30000;
    int     i = 2000000;
    long    l = 2000000000L;      // sufixo L para long
    long long ll = 9000000000LL;  // sufixo LL para long long

    unsigned int ui = 4000000000U; // sufixo U para unsigned

    printf("short:     %d\n",   s);
    printf("int:       %d\n",   i);
    printf("long:      %ld\n",  l);   // %ld para long
    printf("long long: %lld\n", ll);  // %lld para long long
    printf("unsigned:  %u\n",   ui);  // %u para unsigned int

    return 0;
}

short: 30000

int: 2000000

long: 2000000000

long long: 9000000000

unsigned: 4000000000

Overflow — o perigo de estourar o limite: Se você atribuir a um int um valor maior que 2.147.483.647, o resultado é indefinido (ou "estoura" para um número negativo). C não lança exceção nem avisa — o erro fica silencioso. Use long long quando houver risco de valores grandes.

Tipos de Ponto Flutuante

Os tipos de ponto flutuante armazenam números com parte fracionária. C oferece três variações com diferentes precisões:

Tipo Tamanho Dígitos de precisão Especificador
float 4 bytes ~6–7 dígitos significativos %f
double 8 bytes ~15–16 dígitos significativos %lf (scanf) / %f (printf)
long double 10–16 bytes ~18–19 dígitos significativos %Lf
#include <stdio.h>

int main() {
    float  f = 3.14159f;       // sufixo f para float literal
    double d = 3.141592653589793;
    float  altura = 1.75;

    printf("float:  %.5f\n", f);   // %.5f = 5 casas decimais
    printf("double: %.15f\n", d);  // mostra a maior precisão do double
    printf("altura: %.2f m\n", altura);

    return 0;
}

float: 3.14159

double: 3.141592653589793

altura: 1.75 m

Quando usar float vs double? Para a maioria dos cálculos científicos e de engenharia, prefira double — ele é o padrão em C para literais de ponto flutuante (ex: 3.14 é double por padrão). Use float apenas quando memória for crítica (ex: sistemas embarcados com muitos dados).

Tipo Caractere: char

O tipo char armazena um único caractere usando a codificação ASCII. Ocupa exatamente 1 byte (8 bits) de memória. Na prática, um char é um número inteiro pequeno — cada caractere tem um código numérico correspondente.

#include <stdio.h>

int main() {
    char letra = 'A';
    char digito = '7';

    printf("Caractere: %c\n", letra);    // imprime: A
    printf("Código ASCII: %d\n", letra); // imprime: 65 (%d lê o char como inteiro)
    printf("Próxima letra: %c\n", letra + 1); // imprime: B (65+1 = 66)

    return 0;
}

Caractere: A

Código ASCII: 65

Próxima letra: B

char é um inteiro disfarçado: Em C, char e int são compatíveis. É válido fazer aritmética com char, como letra + 1. Isso é muito útil para percorrer alfabetos, comparar caracteres e processar texto.

Tabela ASCII — caracteres mais comuns

CódigoCaractere CódigoCaractere CódigoCaractere
48–57'0'–'9' 65–90'A'–'Z' 97–122'a'–'z'
32espaço 10\n (newline) 0\0 (nulo)

Tipo Booleano em C

Ao contrário do Java, que possui o tipo primitivo boolean com os valores true e false, o C originalmente não tinha um tipo booleano. A lógica booleana era implementada com inteiros:

Regra histórica do C (antes de C99):
// C antes do C99 — usando int para lógica booleana
int ativo = 1;   // verdadeiro
int inativo = 0; // falso

if (ativo) {
    printf("Sistema ativo!\n");  // este bloco executa
}

A partir do padrão C99, foi introduzido o header <stdbool.h>, que define o tipo bool e as constantes true e false formalmente:

#include <stdio.h>
#include <stdbool.h>   // necessário para usar bool em C99+

int main() {
    bool aprovado = true;
    bool reprovado = false;

    if (aprovado) {
        printf("Aluno aprovado!\n");
    }

    printf("Valor de true:  %d\n", true);   // imprime 1
    printf("Valor de false: %d\n", false);  // imprime 0

    return 0;
}

Aluno aprovado!

Valor de true: 1

Valor de false: 0

Atenção: Mesmo com <stdbool.h>, bool em C continua sendo implementado como inteiro por baixo dos panos. Ao contrário do Java, C não impede o uso de inteiros em expressões booleanas — o que pode causar bugs sutis se o programador não tiver cuidado.

Verificando o Tamanho dos Tipos: sizeof

O operador sizeof retorna o número de bytes que um tipo ou variável ocupa na memória. É muito útil para entender como o C aloca espaço e para escrever código portável entre diferentes plataformas e sistemas operacionais.

#include <stdio.h>

int main() {
    printf("char:        %zu byte(s)\n",  sizeof(char));
    printf("short:       %zu byte(s)\n",  sizeof(short));
    printf("int:         %zu byte(s)\n",  sizeof(int));
    printf("long:        %zu byte(s)\n",  sizeof(long));
    printf("long long:   %zu byte(s)\n",  sizeof(long long));
    printf("float:       %zu byte(s)\n",  sizeof(float));
    printf("double:      %zu byte(s)\n",  sizeof(double));
    printf("long double: %zu byte(s)\n",  sizeof(long double));
    return 0;
}

char: 1 byte(s)

short: 2 byte(s)

int: 4 byte(s)

long: 8 byte(s)

long long: 8 byte(s)

float: 4 byte(s)

double: 8 byte(s)

long double: 16 byte(s)

DICA: O especificador correto para imprimir o resultado de sizeof é %zu (z = tamanho, u = unsigned). Em sistemas antigos, pode-se usar %lu como alternativa.

Exemplo Completo: Lendo Diferentes Tipos

O programa abaixo, baseado no material de referência do curso, demonstra como declarar, inicializar e ler variáveis dos três tipos principais. Observe o uso de <locale.h> para suporte a acentos:

#include <stdio.h>
#include <locale.h>   // necessário para exibir caracteres acentuados

void main() {
    setlocale(LC_ALL, "");  // ativa o suporte à língua local (acentos)

    printf("Olá \n");

    // ── Inteiro ───────────────────────────────────
    int a = 50;
    printf("O valor de a é = %d \n", a);
    printf("Informe o novo valor de a: ");
    scanf("%d", &a);
    printf("O valor de a mudou para %d \n", a);

    // ── Ponto flutuante ───────────────────────────
    float b = 5.5;
    printf("O valor de b é = %f \n", b);
    printf("Informe o novo valor de b: ");
    scanf("%f", &b);
    printf("O valor de b mudou para %f \n", b);

    // ── Caractere ─────────────────────────────────
    char letra = 't';
    printf("O valor de c é = %c \n", letra);
    printf("Informe o novo valor de c (letra): ");
    scanf(" %c", &letra);  // espaço antes de %c descarta o '\n' do buffer
    printf("O valor de c mudou para %c\n", letra);
}
Atenção — int b = 5.5: No material de referência, o exemplo usa int b = 5.5 intencionalmente para ilustrar um erro comum: atribuir um valor com casas decimais a um int. O compilador trunca silenciosamente o valor para 5. Para armazenar 5.5 corretamente, use float ou double.

Conversão de Tipos (Type Casting)

Quando valores de tipos diferentes são combinados em uma expressão, o C realiza conversões de tipo — de forma automática (implícita) ou forçada pelo programador (explícita).

Conversão Implícita (automática)

O C promove automaticamente o tipo "menor" para o tipo "maior" em uma expressão:

int    i = 7;
double d = 2.5;
double resultado = i + d;  // i é promovido para double automaticamente
printf("%.1f\n", resultado);  // 9.5

Conversão Explícita (cast)

Para forçar uma conversão, use o operador de cast (tipo) antes do valor:

#include <stdio.h>

int main() {
    int a = 7, b = 2;

    // sem cast: divisão inteira (descarta o resto)
    printf("Sem cast: %d\n", a / b);         // 3

    // com cast: converte a para double antes de dividir
    printf("Com cast: %.1f\n", (double)a / b);  // 3.5

    return 0;
}

Sem cast: 3

Com cast: 3.5

Conversão de tipos em C vs Java: Em Java, o compilador gera erros ou avisos ao detectar conversões que podem perder dados (ex: double → int) sem um cast explícito. Em C, essas conversões podem ocorrer silenciosamente, produzindo resultados inesperados sem qualquer aviso por padrão. É uma das razões pelas quais C oferece mais controle — mas também mais responsabilidade — ao programador.

Tipos em C versus Java

A tabela abaixo compara os tipos primitivos das duas linguagens. As semelhanças são grandes — a principal diferença está no que não existe em C:

Tipo em C Equivalente em Java Diferença principal
int int Idênticos em comportamento
short short Idênticos
long long long Em C, long pode ter 4 ou 8 bytes; long long garante 8
float float Idênticos
double double Idênticos
char char Em C, char é um inteiro de 1 byte; em Java, char é Unicode de 2 bytes
bool (C99+) boolean Em C, é apenas um inteiro; em Java, é um tipo separado e não se mistura com inteiros
— (não existe) String Em C, strings são arrays de char terminados em \0 — não há tipo String nativo
— (não existe) byte Em C, usa-se unsigned char para o mesmo propósito

Exercícios — Aula 02

Responda às questões abaixo para fixar os tipos de dados em C:

1. Qual tipo de dado deve ser usado para armazenar o valor 3.141592653589793 com máxima precisão em C?

2. O que acontece quando você atribui o valor 5.9 a uma variável do tipo int em C?

3. Qual é o código ASCII do caractere 'A'?

4. Analise o trecho abaixo. Qual será o valor impresso?

int a = 7, b = 2;
printf("%d\n", a / b);

5. Para usar bool, true e false em C (padrão C99), qual header deve ser incluído?

6. Qual especificador de formato é usado no printf para imprimir uma variável do tipo long long?

7. O que o operador sizeof(double) retorna em um sistema típico de 64 bits?

8. Analise o código. Qual será a saída?

char letra = 'C';
printf("%c  %d\n", letra, letra);