Pular para conteúdo

Lista 02 · Funções: valor, ponteiro e referência

Exercícios da Semana 2 — Funções: valor, ponteiro e referência. Muitos exercícios aqui pedem uma função que altera o argumento do chamador — é assim que o próprio harness de teste consegue checar se sua função usou referência corretamente: ele chama sua função e depois lê a variável que passou.

Como resolver

Copie o bloco de código de cada exercício, cole no seu compilador online, preencha a parte marcada como ESCREVA SUA SOLUCAO AQUI e rode. A saída mostra quais testes passaram.

ex01

Escreva dobra_valor, que recebe um int por referência e dobra o valor da variável do chamador (a função não retorna nada — void).

int n = 5;
dobra_valor(n);
// agora n vale 10
// ex01 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX01_H
#define EX01_H

// Dobra o valor de x, alterando a variavel do chamador (passagem por referencia).
void dobra_valor(int& x);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void dobra_valor(int& x) {
    // TODO: dobre o valor de x (ex: x = x * 2;)
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    int a = 5;
    dobra_valor(a);
    assert(a == 10);

    int b = 0;
    dobra_valor(b);
    assert(b == 0);

    int c = -3;
    dobra_valor(c);
    assert(c == -6);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex02

Escreva troca, que recebe dois int por referência e troca os valores entre si (o clássico swap).

int a = 1, b = 2;
troca(a, b);
// a vale 2, b vale 1
// ex02 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX02_H
#define EX02_H

// Troca os valores de a e b entre si.
void troca(int& a, int& b);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void troca(int& a, int& b) {
    // TODO: troque os valores de a e b (voce vai precisar de uma variavel auxiliar).
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    int a = 1, b = 2;
    troca(a, b);
    assert(a == 2);
    assert(b == 1);

    int c = -5, d = 5;
    troca(c, d);
    assert(c == 5);
    assert(d == -5);

    int e = 7, f = 7;
    troca(e, f);
    assert(e == 7);
    assert(f == 7);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex03

Escreva potencia, que recebe uma base (double) e um expoente (int), e devolve a base elevada ao expoente. O expoente deve ter valor padrão 2 — ou seja, potencia(3.0) calcula o quadrado de 3.

Escreva a multiplicação na mão, com um for: comece o resultado em 1.0 e multiplique pela base tantas vezes quantas o expoente mandar. Nada de std::pow; o assunto aqui é o parâmetro com valor padrão, e a conta precisa ser sua para você ver os dois casos de borda.

E são três casos, não um:

  • expoente 0 — o laço não roda nenhuma vez e o resultado fica em 1.0, que é a resposta certa. Se você começar o resultado na base em vez de em 1.0, este caso quebra.
  • expoente negativo — potencia(2.0, -2) é 1 / potencia(2.0, 2), ou seja, 0.25. Calcule com o expoente positivo e inverta no fim.
  • base negativa — sai de graça, desde que você multiplique de verdade: potencia(-3.0, 3) é -27.

Lembre que o valor padrão vai no .h, na declaração, e não na definição em .cpp — repetir o = 2 nos dois lugares é erro de compilação.

potencia(3.0)        // -> 9.0    (usa o expoente padrao 2)
potencia(2.0, 10)    // -> 1024.0
potencia(5.0, 0)     // -> 1.0
potencia(2.0, -2)    // -> 0.25
// ex03 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX03_H
#define EX03_H

// Retorna base elevada a expoente. Se o expoente nao for informado, usa 2.
// Expoente negativo devolve o inverso: potencia(2.0, -2) == 0.25.
double potencia(double base, int expoente = 2);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
double potencia(double base, int expoente) {   // sem o "= 2" aqui: ele fica no .h
    // TODO: guarde se o expoente e negativo e trabalhe com ele positivo.
    // Comece resultado em 1.0 e multiplique por base uma vez para cada
    // unidade do expoente. Se o expoente era negativo, devolva 1.0 / resultado.
    return 0.0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
static bool perto(double a, double b) {
    return std::fabs(a - b) < 0.0001;
}

int main() {
    // sem informar o expoente: usa o padrao 2
    assert(perto(potencia(3.0), 9.0));
    assert(perto(potencia(1.5), 2.25));

    // expoente informado
    assert(perto(potencia(2.0, 10), 1024.0));
    assert(perto(potencia(10.0, 3), 1000.0));

    // expoente 1 e expoente 0
    assert(perto(potencia(7.0, 1), 7.0));
    assert(perto(potencia(5.0, 0), 1.0));
    assert(perto(potencia(0.0, 0), 1.0));

    // expoente negativo
    assert(perto(potencia(2.0, -2), 0.25));
    assert(perto(potencia(4.0, -1), 0.25));

    // base negativa: o sinal sai da propria multiplicacao
    assert(perto(potencia(-3.0, 3), -27.0));
    assert(perto(potencia(-3.0), 9.0));

    // base zero com expoente positivo
    assert(perto(potencia(0.0, 5), 0.0));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex04

Implemente duas versões sobrecarregadas de dobro: uma para int e outra para double, cada uma devolvendo o dobro do valor recebido, mantendo o tipo.

dobro(5)     // -> 10        (chama a versao int)
dobro(2.5)   // -> 5.0       (chama a versao double)
// ex04 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX04_H
#define EX04_H

// Duas versoes sobrecarregadas: cada uma retorna o dobro do valor recebido.
int dobro(int x);
double dobro(double x);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
int dobro(int x) {
    // TODO: retorne o dobro de x (versao int).
    return 0;
}

double dobro(double x) {
    // TODO: retorne o dobro de x (versao double).
    return 0.0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(dobro(5) == 10);
    assert(dobro(0) == 0);
    assert(dobro(-3) == -6);
    assert(std::abs((dobro(2.5)) - (5.0)) <= (0.0001));
    assert(std::abs((dobro(0.0)) - (0.0)) <= (0.0001));
    assert(std::abs((dobro(-1.5)) - (-3.0)) <= (0.0001));
    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex05

Escreva maiusculo, que recebe um std::string por referência e converte todas as letras para maiúsculas, alterando a própria string do chamador (void, sem retorno).

std::string texto = "ola mundo";
maiusculo(texto);
// texto agora vale "OLA MUNDO"
// ex05 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX05_H
#define EX05_H

// Converte texto para maiusculas, alterando a propria string do chamador.
void maiusculo(std::string& texto);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void maiusculo(std::string& texto) {
    // TODO: percorra "texto" convertendo cada caractere para maiusculo
    // (dica: <cctype> tem a funcao std::toupper).
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    std::string a = "ola mundo";
    maiusculo(a);
    assert(a == std::string("OLA MUNDO"));

    std::string b = "Ja Maiusculo";
    maiusculo(b);
    assert(b == std::string("JA MAIUSCULO"));

    std::string c = "";
    maiusculo(c);
    assert(c == std::string(""));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex06

Escreva repete, que recebe um texto (const std::string& — sem copiar, sem alterar) e um número de vezes, e devolve o texto repetido essa quantidade de vezes.

repete("ab", 3)   // -> "ababab"
repete("x", 0)    // -> ""
repete("oi", 1)   // -> "oi"
// ex06 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX06_H
#define EX06_H

// Retorna "texto" repetido "vezes" vezes.
std::string repete(const std::string& texto, int vezes);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string repete(const std::string& texto, int vezes) {
    // TODO: monte uma string com "texto" concatenado "vezes" vezes.
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(repete("ab", 3) == std::string("ababab"));
    assert(repete("x", 0) == std::string(""));
    assert(repete("oi", 1) == std::string("oi"));
    assert(repete("", 5) == std::string(""));
    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex07

Escreva ajusta_valor, que recebe dois ponteiros para int: ela deve incrementar em 1 o valor apontado pelo primeiro e decrementar em 1 o valor apontado pelo segundo. Não retorna nada.

Repare que aqui o exercício pede ponteiro (int*), e não referência (int&): quem chama precisa escrever ajusta_valor(&a, &b), e dentro da função você usa *a para chegar ao valor.

int a = 10, b = 10;
ajusta_valor(&a, &b);
// a vale 11, b vale 9
// ex07 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX07_H
#define EX07_H

// Incrementa o valor apontado por sobe e decrementa o valor apontado por desce.
void ajusta_valor(int* sobe, int* desce);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void ajusta_valor(int* sobe, int* desce) {
    // TODO: some 1 ao valor apontado por sobe e subtraia 1 do valor
    // apontado por desce (lembre do * antes do nome do ponteiro).
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    int a = 10, b = 10;
    ajusta_valor(&a, &b);
    assert(a == 11);
    assert(b == 9);

    int c = 0, d = 0;
    ajusta_valor(&c, &d);
    assert(c == 1);
    assert(d == -1);

    int e = -5, f = -5;
    ajusta_valor(&e, &f);
    assert(e == -4);
    assert(f == -6);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex08

Escreva raiz_quadrada, que recebe um double e um ponteiro para double onde o resultado deve ser guardado. A função calcula a raiz quadrada do número (std::sqrt, de <cmath>) e escreve o valor no endereço apontado.

Se o número for negativo, não existe raiz quadrada real: guarde -1.0 no resultado para sinalizar isso.

Este é o padrão clássico de "devolver resultado pelo parâmetro", e ele volta várias vezes no curso: sempre que uma função pode não ter resposta, ela devolve bool (ou um valor especial como -1) e entrega o resultado por um parâmetro.

double r;
raiz_quadrada(9.0, &r);    // r vale 3.0
raiz_quadrada(-4.0, &r);   // r vale -1.0
// ex08 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX08_H
#define EX08_H

// Calcula a raiz quadrada de numero e guarda no endereco apontado por resultado.
// Se numero for negativo, guarda -1.0 (nao ha raiz quadrada real).
void raiz_quadrada(double numero, double* resultado);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void raiz_quadrada(double numero, double* resultado) {
    // TODO: se numero for negativo, guarde -1.0 em *resultado.
    // Caso contrario, guarde std::sqrt(numero).
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    double r = 0.0;

    raiz_quadrada(9.0, &r);
    assert(std::abs((r) - (3.0)) <= (0.0001));

    raiz_quadrada(0.0, &r);
    assert(std::abs((r) - (0.0)) <= (0.0001));

    raiz_quadrada(2.0, &r);
    assert(std::abs((r) - (1.41421356)) <= (0.0001));

    raiz_quadrada(-4.0, &r);
    assert(std::abs((r) - (-1.0)) <= (0.0001));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex09

Escreva divide, que recebe dois inteiros e devolve os dois resultados da divisão inteira — quociente e resto — pelos parâmetros de referência. A função não retorna nada.

É a resposta desta semana para "e se eu precisar devolver mais de uma coisa?". Na Lista 03 você vai ver a outra, com std::pair.

int q, r;
divide(17, 5, q, r);
// q vale 3, r vale 2
// ex09 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX09_H
#define EX09_H

// Calcula a divisao inteira de a por b, devolvendo os DOIS resultados
// pelos parametros de referencia. Assume b diferente de zero.
void divide(int a, int b, int& quociente, int& resto);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void divide(int a, int b, int& quociente, int& resto) {
    // TODO: quociente recebe a / b, resto recebe a % b.
    // Como sao referencias, atribuir aqui altera as variaveis do chamador.
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    int q = 0, r = 0;

    divide(17, 5, q, r);
    assert(q == 3);
    assert(r == 2);

    divide(20, 4, q, r);
    assert(q == 5);
    assert(r == 0);

    divide(3, 10, q, r);
    assert(q == 0);
    assert(r == 3);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}