Introdução à linguagem C (cont.)

MCTA028-15 - PROGRAMAÇÃO ESTRUTURADA

Victor S. Portella

UFABC

2026-09-18

Questões Logísticas

Agenda de aulas e provas

Agenda prevista de aulas está no site. Datas das provas:

  • P1: 27 de outubro (Laboratório)
  • P2: 4 de dezembro (Teoria)

Objeções?

Usando Linux nos Laboratórios

Nos laboratórios, a partição Linux tem um único usuário…

COM MEMÓRIA PERSISTENTE


  • Use navegadores em abas anônimas
  • Tente fazer todo seu trabalho em uma pasta e apagar essa pasta depois de terminar o laboratório

UFABC para Todos e CO’DA

Próxima terça-feira não haverá aula. Venha participar do UFABC para todos e do CO’DA

Sexta-feira é aula normal!!

Minha palestra no CO’DA

Horário: Terça-feira – 13hs

Competição de programação

Entrada, saída, e condicionais

Um programa de exemplo: máximo de dois números

Queremos implementar o seguinte programa:

O programa le dois números e imprime o maio deles.


Solução em Python (sem usar a função max)

```python
Print("Digite dois números (1 por linha):")
a = int(input())
b = int(input())
if a > b:
    print("Maior:", a)
else:
    print("Maior:", b)
```

Entrada com scanf

Para ler valores, usamos a função scanf.

int scanf(const char *format, ...);

Diferença importante entre scanf() e printf()

  • printf recebe os valores a imprimir;
  • scanf recebe os endereços das variáveis onde deve escrever.

Exemplo de scanf

Continuando o nosso exemplo do máximo de dois números:

#include <stdio.h>
int main() {
    int a,b;

    printf("Digite um numero inteiro (1 por linha):\n");
    scanf("%d", &a);
    scanf("%d", &b);
    // (Continua em breve...)

O operador & obtém o endereço da variável.

  • Quando estudarmos ponteiros, isso ficará muito mais claro
  • Por enquanto, não esquecer de usar & no scanf

Imprimindo o endereço de uma variável

#include <stdio.h>

int main() {
    int a = 1;
    printf("%d\n", a);  // 1
    printf("%p\n", &a); // 0x7fff6d661ad4
    return 0;
}
  • &a é o endereço na memória onde o valor de a está armazenado
  • O valor de &a é diferente em diferentes execuções

A string de formato no scanf

O primeiro argumento do scanf nos diz como interpretar a linha que está sendo dada como entrada


    // Interpretar entrada como float
    printf("Digite um numero fracionario:\n");
    scanf("%f", &n_float);

    // Interpretar entrada como dois int
    //   (separados por espaço)
    printf("Digite dois numeros:\n");
    scanf("%d %d", &a, &b);

Condicionais

if e else

Sintaxe:

if (condicao) {
    // bloco executado se condicao for verdadeira
}


if (condicao) {
    // caso verdadeiro
} else {
    // caso falso
}

Também existe o operador ternário (usado só em casos muito simples):

condicao ? valor_se_verdadeiro : valor_se_falso

Operadores relacionais


Operador Significado
< menor que
> maior que
<= menor ou igual a
>= maior ou igual a
== igual a
!= diferente de

Operadores lógicos

Operador Significado
&& e
|| ou
! negação

Tipo do resultado de operadores lógicos

Por padrão, operadores lógicos/relacionais são avaliado como um int, sempre ou 0 (false) ou 1 (true)

#include <stdio.h>

int main() {
    printf("%d\n", 1 >= 2); // 0
    printf("%d\n", 1 <= 2); // 1
    printf("%d\n", 1 == 2); // 0
}

Atenção: = versus ==

Qual é a saída?

#include <stdio.h>

int main() {
    int n = 507;

    if (n = 5)
        printf("n eh 5.\n");
    else
        printf("n nao eh 5.\n");

    return 0;
}

= versus ==: resposta

O código compila, e imprime “o que esperamos”, mas está errado.

  • O código n = 5 faz uma atribuição de valor
  • Em C, atribuições retornam o valor da variável
  • Qualquer valor \(\neq 0\) é considerado true

O teste de igualdade seria:

if (n == 5)

Com warnings ativados, o compilador pode ajudar a detectar esse tipo de erro.

Concluindo o exemplo do máximo

Concluindo

#include <stdio.h>
int main() {
    int a,b;

    printf("Digite um numero inteiro (1 por linha):\n");
    scanf("%d", &a);
    scanf("%d", &b);
    
    if(a > b){
        printf("%d\n", a);
    } else {
        printf("%d\n", b);
    }

    return 0; 
}

Concluindo o exemplo do máximo

C

#include <stdio.h>

int main( ) {
    int a, b;

    printf("Digite dois numeros:\n");

    scanf("%d", &a);
    scanf("%d", &b);

    if (a > b) {
        printf("Maior: %d\n", a);
    } else {
        printf("Maior: %d\n", b);
    }

    return 0;
}

Python




print("Digite dois números:")

a = int(input())
b = int(input())

if a > b:
    print("Maior:", a)
else:
    print("Maior:", b)

Laços

while

while (condicao) {
    // repete enquanto condicao for verdadeira
}

Exemplo:

int i = 0;
while (i < 3) {
    i = i + 1;
    printf("%d\n", i);
}
1
2
3

Interlúdio - sintaxe para incrementos

Operador Significado
a += b a = a + b
a -= b a = a - b
a *= b a = a * b
Operador Significado
a /= b a = a / b
a %=b a = a % b

Também temos notação de pós/pré-incremento unitário

Pós-incremento Pré-incremento Significado
a++ ++a a += 1
a-- --a a -= 1
  • Pós-incremento é executado por último na linha
  • Pré-incremento é executado primeiro na linha

Pós-incremento

Qual é a saída?

int i = 0;
while (i < 3) {
    printf("%d\n", i++);
}
0
1
2

Pré-incremento

Qual é a saída?

int i = 0;
while (i < 3) {
    printf("%d\n", ++i);
}
1
2
3

do while

do {
    // executa pelo menos uma vez
} while (condicao);

Semelhante ao while(), mas a condição de saída é avaliada ao final do laço

  • A diferença principal é que o bloco de código é executado pelo menos uma vez

Exemplo:

int i = 0;
do {
    printf("%d ", i);
    i++;
} while (i < 3);
//0 1 2

for

for (inicializacao; condicao; passo) {
    // corpo do laço
}


  • inicialização: Executado apenas antes da primeira iteração
  • condição: Caso falhe, o laço para
    • Na primeira iteração é executado depois da inicialização
  • passo: Executado após cada execução do corpo, mas antes de testar condição

for

for (inicializacao; condicao; passo) {
    // corpo do laço
}

Exemplo:

for (int i = 0; i < 3; i++) {
    printf("%d\n", i);
}
0
1
2

Um exemplo: exponenciação

Tarefa: um programa que recebe de entrada números \(a\) e \(b\), e imprime \(a^b\).

  • Queremos implementar isso sem usar funções de exponenciação pré-prontas

Versão em Python:

a = int(input())
b = int(input())

produto = 1
for i in range(b):
    produto *= a # produto = produto * a

print(produto)

Exponenciação em C

#include <stdio.h>

int main() {
    int a, b, i, produto = 1;
    scanf("%d", &a);
    scanf("%d", &b);
    
    for (i = 0; i < b; i++) {
        produto = a * produto;
    }

    return produto;
}

Conversões e expressões

Tipos de dados

Tipo Tamanho típico Faixa típica
char 1 byte -128 a 127
short int 2 bytes \(-2^{15}\) a \(2^{15}-1\)
int 4 bytes \(-2^{31}\) a \(2^{31}-1\)
long int 8 bytes \(-2^{63}\) a \(2^{63}-1\)
long long int 8 bytes \(-2^{63}\) a \(2^{63}-1\)
float 4 bytes ~7 casas de precisão
double 8 bytes ~15–16 casas precisão

Tamanhos/faixas exatapas dependem do compilador/sistema. Valores acima são típicos para gcc em sistemas Linux de 64 bits.

Conversão de tipo: primeiro exemplo

Qual é a saída?

#include <stdio.h>

int main() {
    float num;

    num = 4 / 2;
    // %.2f imprime só 2 casas decimais
    printf("%.2f\n", num); 

    num = 10 / 2.5;
    printf("%.2f\n", num);

    return 0;
}
2.00
4.00

Inteiros versus ponto flutuante

Qual é a saída?

#include <stdio.h>

int main() {
    float num;

    num = 4 / 2;
    printf("%.2f\n", num);

    num = 5 / 2;
    printf("%.2f\n", num);

    return 0;
}
2.00
2.00

Divisão inteira

Em C:

5 / 2

é uma divisão entre inteiros, cujo resultado inteiro é 2.

Para obter um resultado fracionário:

5 / 2.0
5 / ((float) 2)
((float) 5) / 2

Cast explícito

Em C podemos converter tipos explicitamente usando cast:

(tipo) expressao

Por exemplo:

int a = 5, b = 2;
float x = ((float) a) / b;

O cast converte o valor de a para float nesta expressão:

(float) a / b = 5.0 / 2 = 2.5

Nem todo cast é necessário:

float n = 507;          // conversão implícita
float m = (float) 507;  // conversão explícita

Precedência do cast

Qual é a saída?

    float r1 = 7 / 2;
    float r2 = ((float) 7) / 2;
    float r3 = 7 / ((float) 2);
    float r4 = ((float) 7) / ((float) 2);
    float r5 = (float) 7 / 2;
    float r6 = (float) (7 / 2);

    printf("%.1f %.1f %.1f\n", r1, r2, r3);
    printf("%.1f %.1f %.1f\n", r4, r5, r6);
3.0 3.5 3.5
3.5 3.5 3.0

O cast tem precedência alta. Sem parênteses, vai normalmente ser executado primeiro.

Conversões perigosas - tipos de tamanhos diferentes

Conversões de um tipo com maior faixa/precisão para um tipo com menor faixa/precisão podem perder informação.

int n1 = 50000;
short int n2 = n1;

O resultado depende dos limites do tipo short int na implementação.


No meu Ubuntu, 50000 não cabe em short int, e o programa imprime

-15536

Conversão de reais para inteiros

#include <stdio.h>

int main() {
    float f1 = 2.782;
    int n2 = f1;

    printf("%d\n", n2);
    return 0;
}
2


A parte fracionária é descartada!.

Tipos unsigned

Os tipos inteiros também possuem versões sem sinal (unsigned) que aceitam uma faia maior de valores não negativos.

Tipo Tamanho típico Faixa típica
unsigned char 1 byte \(0\) a \(2^8 - 1\)
unsigned short int 2 bytes \(0\) a \(2^{16} - 1\)
unsigned int 4 bytes \(0\) a \(2^{32} - 1\)
unsigned long int 8 bytes \(0\) a \(2^{64} - 1\)
unsigned long long int 8 bytes \(0\) a \(2^{64} - 1\)

Conversão unsigned para a versão com sinal

int main( ) {
    int a = -1;
    unsigned int b = a;

    printf("a = %d\n", a);
    printf("b = %u\n", b);

    return 0;
}

Saída:

a = -1
b = 4294967295

Conversão implicita de int para unsigned

Cuidado: em expressões, C pode converter implicitamente um valor int para unsigned int, produzindo resultados inesperados.

int main() {
    int a = -1;
    unsigned int b = 1;

    if (a < b) {
        printf("menor\n");
    } else {
        printf("maior ou igual\n");
    }
}

Saída: maior ou igual.

  • Na conversão para unsigned int, o valor -1 virou um positivo grande.

Inicialização e erros comuns

Sempre inicialize variáveis

Quando uma variável local é declarada, o programa reserva espaço de memória para ela.

Esse espaço não é necessariamente inicializado.

#include <stdio.h>

int main() {
    int num;
    printf("%d\n", num);
    return 0;
}

O programa pode imprimir um valor arbitrário.

Compilador como aliado

Compilando com warnings:

gcc -Wall -Wextra teste.c -o teste

O compilador pode avisar sobre problemas como uso de variável não inicializada.

teste.c: In function 'main':
teste.c: warning: 'num' is used uninitialized

Durante o curso, tratem warnings como sinais importantes. Código que compila com warnings pode estar errado.

Exemplos finais

Leitura de n elementos

#include <stdio.h>

int main() {
    int qtd;
    scanf("%d", &qtd);

    for (int i = 0; i < qtd; i++) {
        int numero;
        scanf("%d", &numero);

        // Faz alguma coisa com numero
        printf("Leu numero!\n");
    }

    printf("Saiu!\n");
    return 0;
}

Leitura até sentinela

#include <stdio.h>

int main() {
    int numero;
    scanf("%d", &numero);

    while (numero >= 0) {
        // Faz alguma coisa com numero
        printf("Leu numero!\n");
        scanf("%d", &numero);
    }

    printf("Saiu!\n");
    return 0;
}

Aqui, um número negativo encerra a leitura.

Exercício 1

Faça um programa que leia um inteiro e imprima todos os divisores desse número.

Neste exercício, um divisor é um número que divide o outro com resto zero.

Exercício 2 – Lista de números 1

Faça um programa que leia um número n.

Depois, o programa deve ler n números.

Ao final, considerando os n números, o programa deve imprimir:

  • maior número;
  • menor número;
  • soma de todos os números.

Não use vetor neste exercício.

Exercício 3 – Lista de números 2

Modifique o programa anterior para não solicitar a quantidade n no início.

Agora, o programa deve ler números até que o usuário digite um número negativo.

Ao final, imprima:

  • maior número;
  • menor número;
  • soma de todos os números.

Exercício 4

Faça um programa que leia um número n.

Depois, o programa deve ler n números.

Ao final, considerando esses números, imprima:

  • quantos são pares;
  • quantos são ímpares.

Exercício 5 – Menu de operações

Faça um programa com um menu de operações.

O programa deve ler um código:

  • 1: soma dois números;
  • 2: soma três números;
  • 3: multiplica dois números;
  • 0: sair.

Se o usuário escolher sair, o programa imprime o menor resultado obtido e encerra.

Caso contrário, lê os números necessários, imprime o resultado da operação e volta ao menu.

Exemplo de interação

Operacao? 1
-2
1
-1
Operacao? 3
2
90
180
Operacao? 2
80
2
5
87
Operacao? 0
-1