Alvaro: Resolução dos exercícios da aula 2 #11

Open
opened 2025-05-23 19:37:55 -03:00 by alvaro · 0 comments

Exercícios da aula 2: Dados e instruções

1. Pesquise e responda

Quais são as finalidades da função printf?

A função printf (print formatted) serve principalmente para exibir informações na tela. Ela é usada para imprimir texto, valores de variáveis, resultados de operações, mensagens de erro ou de status, ajudando na interação do programa com o usuário ou na depuração do código.

Para que mais serve o printf?

  • Depuração: Você pode usar o printf para mostrar o valor de variáveis em diferentes pontos do programa e entender o que está acontecendo internamente.
  • Interação: Mostra mensagens para o usuário (pedindo dados, mostrando resultados, avisando de erros, etc).
  • Formatação de saída: Você pode exibir dados de forma alinhada, ajustando o número de casas decimais, espaçamento, bases numéricas (decimal, hexadecimal, octal), etc.

Comparação com puts

A função puts() é mais limitada, pois só imprime strings e adiciona uma quebra de linha automaticamente.
O printf é mais flexível, pois permite imprimir números, caracteres, valores em hexadecimal, octal, float, etc., tudo em uma mesma chamada.

Em que cabeçalho da glibc ela é declarada?

A função printf é declarada no arquivo de cabeçalho chamado stdio.h (standard input output header) da glibc (GNU C Library - Biblioteca Padrão C do GNU) no ambiente GNU/Linux.

O arquivo stdio.h contém as declarações (os "protótipos") das funções, mas o código de implementação delas está em arquivos compilados da biblioteca.

Quantos argumentos ela recebe?

A função printf é uma função chamada de "variadic" (função variádica), isso significa que aceita um número variável de argumentos, enquanto algumas funções, como puts, sempre recebem só um argumento.

O primeiro argumento da função printf é sempre uma string de formato (por exemplo, "Nome: %s, Idade: %d"), e os demais argumentos (opcionais) são os valores que vão preencher os espaços marcados pelos especificadores de formato na string.

Pode-se passar apenas a string inicial ou a string mais vários valores, dependendo da necessidade dos especificadores na string de formato.

O compilador não impede que sejam colocados mais ou menos argumentos, mas se a quantidade de valores não combinar com o que está na string de formato, pode acontecer comportamento inesperado.

Como fazer para que a saída imprima quebras de linha?

Para que a função printf imprima uma quebra de linha na saída, basta incluir o caractere especial \n dentro da string passada como argumento. Em C, toda vez que o caractere \n é colocado dentro da string do primeiro argumento da função printf o sistema operacional entende que ali deve-se pular para a linha de baixo.

O símbolo \ indica "escape", fugir, e n vem de "newline", nova linha, mas \n representa um único caractere que tem um único código na tabela ASCII, 10 em decimal, 0x0A em hexadecimal e 00001010 em binário.

Para haver quebra de linha em sistemas Unix/Linux, o \n já resolve, porém em Windows, o padrão de quebra de linha é \r\n, mas o C traduz o \n automaticamente para a quebra correta no sistema, então deve-se usar somente \n no código C.

Para que servem os especificadores de formato %d, %s, %c e %f?

Os especificadores de formato %d, %s, %c e %f servem para informar ao printf e funções similares como interpretar e imprimir os valores das variáveis passadas. Cada um corresponde a um tipo diferente de dado.

  • %d — % formato, d decimal integer - serve para imprimir valores inteiros (int), como 10, -5, 203.
  • %s — % formato, s string - serve para imprimir textos (strings), ou seja, sequências de caracteres terminadas por \0.
  • %c — % formato, c character - serve para imprimir um único caractere.
  • %f — % formato, f floating-point - serve para imprimir números com parte decimal (float ou double), como 3.14, -0.5, 100.0.

Outros especificadores de formato:

  • %i - % formato, i decimal integer - análogo ao %d.
  • %u - % formato, u unsigned int - inteiros decimais sem sinal.
  • %x - % formato, x hexadecimal - inteiros em hexadecimal (letras minúsculas).
  • %X - % formato, X hexadecimal - inteiros em hexadecimal (letras maiúsculas).
  • %o - % formato, o octal - inteiros em octal.
  • %e - % formato, e exponential - números reais em notação científica com e minúsculo.
  • %E - % formato, e exponential - números reais em notação científica com E maiúsculo.
  • %g - % formato, g general - mais compacto entre o formato decimal comum (%f) e a notação científica (%e).
  • %G - % formato, G general - mais compacto entre o formato decimal comum (%f) e a notação científica (%E).
  • %p - % formato, p pointer - endereço de memória de um ponteiro.
  • %% - % formato, % caractere % - imprime o símbolo de porcentagem.

2. Operações aritméticas

Escreva um programa que, dado um valor inteiro associado à variável num, calcule e imprima os resultados das seguintes operações:

  • num multiplicado por 15;
  • num somado a 42;
  • 123 menos num;
  • Divisão de num por 5;
  • Resto da divisão de num por 5.

Esquema geral da saída do programa:

NUM x 15 = VALOR
NUM + 42 = VALOR
123 - NUM = VALOR
NUM / 5 = VALOR
RESTO DE NUM / 5 = VALOR

Solução

#include <stdio.h> 
int main(void){
    int num = 13;
    printf(
        "%d x 15 = %d\n"
        "%d + 42 = %d\n"
        "123 - %d = %d\n"
        "%d / 5 = %d\n"
        "Resto de %d / 5 = %d\n",
        num, num * 15,
        num, num + 42,
        num, 123 - num,
        num, num / 5,
        num, num % 5
        ); /* end printf */
    return 0;
} /* end main */

3. Números mágicos

O uso de valores literais sem um significado óbvio (números mágicos) é uma prática indesejável, mas pode ser evitada de várias formas. Sendo assim, analise as situações abaixo e proponha uma solução.

Caso 1: Propriedades de círculos

Meu programa tem funções para calcular a área e o perímetro de um círculo cujo raio (um inteiro) é recebido como argumento:

#include ...

float circ_perim(int raio) {
    /* Circunferência = 2πr */
    return 2 * 3.14 * raio;
}

double circ_area(int raio) {
    /* Área = πr² */
    return 3.14 * raio * raio;
}

int main(void) { ... }

Solução 1. Diretiva do pré-processador

1º passo. Incluir no cabeçalho a diretiva de pré-processador correspondente à definição da constante simbólica PI (#define PI 3.14).

2º passo. Substituir no interior de cada função todas as aparições do número mágico 3.14 pela constante simbólica PI.

#include ...
#define PI 3.14

float circ_perim(int raio) {
    /* Circunferência = 2πr */
    return 2 * PI * raio;
}

double circ_area(int raio) {
    /* Área = πr² */
    return PI * raio * raio;
}

int main(void) { ... }

Vantagens:

  • fácil de usar e entender,
  • funciona em C e C++,
  • pode ser usada em qualquer lugar do código.

Desvantagens:

  • não tem tipo: PI vira apenas um "find and replace" no pré-processamento,
  • não respeita escopo (vale para todo o código),
  • erros de digitação não são detectados (por exemplo, PII passa batido).

Solução 2. Constante tipada

1º passo. Incluir logo abaixo do cabeçalho e fora de qualquer função a definição da constante pi com tipo float (const float pi = 3.14).

2º passo. Substituir no interior de cada função todas as aparições do número mágico 3.14 pela constante pi.

#include ...

const float pi = 3.14;

float circ_perim(int raio) {
    /* Circunferência = 2πr */
    return 2 * pi * raio;
}

double circ_area(int raio) {
    /* Área = πr² */
    return pi * raio * raio;
}

int main(void) { ... }

Vantagens:

  • tem tipo (float, double, etc.), ajudando na segurança do código,
  • pode ser limitada ao escopo (arquivo, função),
  • o compilador detecta erros se usar o nome errado.

Desvantagens:

  • não pode ser usada em inicializações de tamanho de array estático no ANSI C/C90, por exemplo (aí só #define mesmo).

Exemplo:

const int tam = 10;
int vetor[tam]; // erro em C clássico!

Solução 3. Para especificação de padrão de compilação C99 ou posterior

1º passo. Incluir no cabeçalho a diretiva de pré-processador correspondente à inclusão da biblioteca matemática C (#include <math.h>).

2º passo. Substituir no interior de cada função todas as aparições do número mágico 3.14 pela constante M_PI (dupla precisão, 3.141592653589793) já pronta da biblioteca matemática C.

#include ...
#include <math.h>

float circ_perim(int raio) {
    /* Circunferência = 2πr */
    return 2 * M_PI * raio;
}

double circ_area(int raio) {
    /* Área = πr² */
    return M_PI  * raio * raio;
}

int main(void) { ... }

Vantagens:

  • o valor 3.141592653589793 é muito mais próximo de pi do que 3.14,
  • usar M_PI mostra claramente para qualquer programador que aquele valor é pi, não um número qualquer,
  • se, um dia, precisar mudar algo como uma aproximação diferente, basta mudar em um lugar só,
  • usar <math.h> conecta o compilador com várias funções matemáticas padronizadas (seno, cosseno, tangente, etc.).

Desvantagens:

  • M_PI não é obrigatório pela especificação C, embora seja comum no GNU/Linux, BSD, Mac, etc,
  • no Windows/Visual Studio é necessário usar #define _USE_MATH_DEFINES antes de #include <math.h>, senão M_PI pode não ser definido,
  • pode ser que M_PI não exista em alguns compiladores, sendo necessário defini-la manualmente no pré-processador para máxima portabilidade (#ifndef M_PI #define M_PI 3.14159265358979323846 #endif),
  • alguns compiladores antigos ou "minimalistas" podem nem ter <math.h> completo ou suportar todas as suas definições,
  • programas minúsculos (ex: embarcados simples) podem preferir evitar a dependência de <math.h> só para pi.

Caso 2: Multiplicador constante

No meu programa, todas as operações aritméticas envolvem a multiplicação de um valor por 10.

#include <stdio.h>

int main(void) {
    int num1 = 13;
    int num2 = 29;

    printf("%d x %d = %d\n", num1, 10, num1 * 10);
    printf("%d x %d = %d\n", num1 + num2, 10, (num1 + num2) * 10);
    printf("%d x %d = %d\n", num2, 10, num2 * 10);
}

Solução 1. Diretiva do pré-processador

1º passo. Incluir no cabeçalho a diretiva de pré-processador correspondente à definição da constante simbólica FATOR_PADRAO (#define FATOR_PADRAO 10).

2º passo. Substituir no interior de cada função todas as aparições do número mágico 10 pela constante simbólica FATOR_PADRAO.

#include <stdio.h>
#define FATOR_PADRAO 10

int main(void) {
    int num1 = 13;
    int num2 = 29;

    printf("%d x %d = %d\n", num1, FATOR_PADRAO, num1 * FATOR_PADRAO);
    printf("%d x %d = %d\n", num1 + num2, FATOR_PADRAO, (num1 + num2) * FATOR_PADRAO);
    printf("%d x %d = %d\n", num2, FATOR_PADRAO, num2 * FATOR_PADRAO);
}

Solução 2. Constante tipada

1º passo. Incluir logo abaixo do cabeçalho e fora de qualquer função a definição da constante fator_padrao com tipo int (const int fator_padrao = 10).

2º passo. Substituir no interior de cada função todas as aparições do número mágico 10 pela constante fator_padrao.

#include <stdio.h>

const int fator_padrao = 10;

int main(void) {
    int num1 = 13;
    int num2 = 29;

    printf("%d x %d = %d\n", num1, fator_padrao, num1 * fator_padrao);
    printf("%d x %d = %d\n", num1 + num2, fator_padrao, (num1 + num2) * fator_padrao);
    printf("%d x %d = %d\n", num2, fator_padrao, num2 * fator_padrao);
}

4. Compile, corrija e responda

Dado o programa abaixo (por exemplo, main.c)...

#include <stdio.h>

int main(void) {
    char *fmt = "Circunferência de um círculo de raio %d: %.2f\n";
    printf(fmt, r, circ_perim(r));
    return 0;
}

float circ_perim(int raio) {
    /* Circunferência = 2πr */
    return 2 * 3.14 * raio;
}

Qual foi o erro encontrado na compilação?

alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula02$ gcc questao4.c -o questao4
questao4.c: In function 'main':
questao4.c:5:17: error: 'r' undeclared (first use in this function)
    5 |     printf(fmt, r, circ_perim(r));
      |                 ^
questao4.c:5:17: note: each undeclared identifier is reported only once for each function it appears in
questao4.c:5:20: warning: implicit declaration of function 'circ_perim' [-Wimplicit-function-declaration]
    5 |     printf(fmt, r, circ_perim(r));
      |                    ^~~~~~~~~~
questao4.c: At top level:
questao4.c:9:7: error: conflicting types for 'circ_perim'; have 'float(int)'
    9 | float circ_perim(int raio) {
      |       ^~~~~~~~~~
questao4.c:5:20: note: previous implicit declaration of 'circ_perim' with type 'int()'
    5 |     printf(fmt, r, circ_perim(r));
      |                    ^~~~~~~~~~

Por que esse erro aconteceu?

Primeiro erro. O identificador r não foi previamente declarado no programa. Para o compilador, se não foi declarado, então não existe.

Segundo erro. A função circ_perim foi chamada antes de ser declarada como um protótipo ou definida, o que faz o compilador assumir por padrão que ela retorna um int. Assim, há um erro de conflito de tipos quando o compilador vê na declaração da função circ_perim que ela retorna um float.

Qual foi a solução adotada?

Solução para o primeiro erro. Declarar a variável r antes de usar.

Solução para o segundo erro. Declarar o protótipo da função circ_perim antes da função main e defini-la depois da função main ou defini-la antes função main.

A sua solução é a única possível?

Há pelo menos duas soluções para o primeiro erro e pelo menos duas soluções para o segundo erro.

Se não for, cite outras possibilidades.

Erro 1, solução 1. Declarar r como variável global antes da função main.

Erro 1, solução 2. Declarar r como variável local dentro da função main e antes de ser chamada.

Erro 2, solução 1. Declarar circ_perim antes da função main e definir depois de main.

Erro 2, solução 2. Definir circ_perim antes da função main.

Erro 2, solução 3. Declarar circ_perim dentro da função main, mas antes de chamar, além de definir circ_perim fora de main depois de chamar.

Erro 2, solução 4. Definir circ_perim dentro da função main, mas antes de chamar.

5. Pesquise e responda

Pesquise, entre as funções da glibc, aquelas que podem solucionar os problemas abaixo e demonstre como elas podem ser utilizadas.

Pausar a execução do programa por 10 segundos.

sleep(unsigned int seconds), suspende a execução do programa pelo número de segundos especificado.

#include <unistd.h>
int main() {
    sleep(10); // Pausa por 10 segundos
    return 0;
}

Apenas imprimir uma quebra de linha.

putchar('\n');
puts("");
printf("\n");

Ler um número inteiro interativamente e atribuí-lo a uma variável.

#include <stdio.h>
int main() {
    int x;
    scanf("%d", &x);
    return 0;
}

Imprimir um número inteiro decimal em base 8 e base 16.

#include <stdio.h>
int main() {
    int x = 123;
    printf("Decimal: %d\n", x);
    printf("Octal: %o\n", x);       // base 8
    printf("Hexadecimal: %x\n", x); // base 16 (minúsculas)
    printf("Hexadecimal: %X\n", x); // base 16 (maiúsculas)
    return 0;
}

Terminar a execução do programa com um valor de estado de término.

#include <stdlib.h>
int main() {
    exit(0);
}
# Exercícios da aula 2: Dados e instruções ## 1. Pesquise e responda ### Quais são as finalidades da função printf? A função `printf` (print formatted) serve principalmente para exibir informações na tela. Ela é usada para imprimir texto, valores de variáveis, resultados de operações, mensagens de erro ou de status, ajudando na interação do programa com o usuário ou na depuração do código. #### Para que mais serve o printf? - **Depuração**: Você pode usar o `printf` para mostrar o valor de variáveis em diferentes pontos do programa e entender o que está acontecendo internamente. - **Interação**: Mostra mensagens para o usuário (pedindo dados, mostrando resultados, avisando de erros, etc). - **Formatação de saída**: Você pode exibir dados de forma alinhada, ajustando o número de casas decimais, espaçamento, bases numéricas (decimal, hexadecimal, octal), etc. #### Comparação com puts A função `puts()` é mais limitada, pois só imprime strings e adiciona uma quebra de linha automaticamente. O `printf` é mais flexível, pois permite imprimir números, caracteres, valores em hexadecimal, octal, float, etc., tudo em uma mesma chamada. ### Em que cabeçalho da glibc ela é declarada? A função `printf` é declarada no arquivo de cabeçalho chamado `stdio.h` (standard input output header) da glibc (GNU C Library - Biblioteca Padrão C do GNU) no ambiente GNU/Linux. O arquivo `stdio.h` contém as declarações (os "protótipos") das funções, mas o código de implementação delas está em arquivos compilados da biblioteca. ### Quantos argumentos ela recebe? A função `printf` é uma função chamada de "variadic" (função variádica), isso significa que aceita um número variável de argumentos, enquanto algumas funções, como `puts`, sempre recebem só um argumento. O primeiro argumento da função `printf` é sempre uma string de formato (por exemplo, "Nome: %s, Idade: %d"), e os demais argumentos (opcionais) são os valores que vão preencher os espaços marcados pelos especificadores de formato na string. Pode-se passar apenas a string inicial ou a string mais vários valores, dependendo da necessidade dos especificadores na string de formato. O compilador não impede que sejam colocados mais ou menos argumentos, mas se a quantidade de valores não combinar com o que está na string de formato, pode acontecer comportamento inesperado. ### Como fazer para que a saída imprima quebras de linha? Para que a função `printf` imprima uma quebra de linha na saída, basta incluir o caractere especial `\n` dentro da string passada como argumento. Em C, toda vez que o caractere `\n` é colocado dentro da string do primeiro argumento da função `printf` o sistema operacional entende que ali deve-se pular para a linha de baixo. O símbolo `\` indica "escape", fugir, e `n` vem de "newline", nova linha, mas `\n` representa um único caractere que tem um único código na tabela ASCII, 10 em decimal, 0x0A em hexadecimal e 00001010 em binário. Para haver quebra de linha em sistemas Unix/Linux, o `\n` já resolve, porém em Windows, o padrão de quebra de linha é `\r\n`, mas o C traduz o `\n` automaticamente para a quebra correta no sistema, então deve-se usar somente `\n` no código C. ### Para que servem os especificadores de formato %d, %s, %c e %f? Os especificadores de formato `%d`, `%s`, `%c` e `%f` servem para informar ao `printf` e funções similares como interpretar e imprimir os valores das variáveis passadas. Cada um corresponde a um tipo diferente de dado. - **%d** — % formato, d decimal integer - serve para imprimir valores inteiros (int), como 10, -5, 203. - **%s** — % formato, s string - serve para imprimir textos (strings), ou seja, sequências de caracteres terminadas por \0. - **%c** — % formato, c character - serve para imprimir um único caractere. - **%f** — % formato, f floating-point - serve para imprimir números com parte decimal (float ou double), como 3.14, -0.5, 100.0. #### Outros especificadores de formato: - **%i** - % formato, i decimal integer - análogo ao %d. - **%u** - % formato, u unsigned int - inteiros decimais sem sinal. - **%x** - % formato, x hexadecimal - inteiros em hexadecimal (letras minúsculas). - **%X** - % formato, X hexadecimal - inteiros em hexadecimal (letras maiúsculas). - **%o** - % formato, o octal - inteiros em octal. - **%e** - % formato, e exponential - números reais em notação científica com e minúsculo. - **%E** - % formato, e exponential - números reais em notação científica com E maiúsculo. - **%g** - % formato, g general - mais compacto entre o formato decimal comum (%f) e a notação científica (%e). - **%G** - % formato, G general - mais compacto entre o formato decimal comum (%f) e a notação científica (%E). - **%p** - % formato, p pointer - endereço de memória de um ponteiro. - **%%** - % formato, % caractere % - imprime o símbolo de porcentagem. ## 2. Operações aritméticas Escreva um programa que, dado um valor inteiro associado à variável `num`, calcule e imprima os resultados das seguintes operações: - num multiplicado por 15; - num somado a 42; - 123 menos num; - Divisão de num por 5; - Resto da divisão de num por 5. Esquema geral da saída do programa: ``` NUM x 15 = VALOR NUM + 42 = VALOR 123 - NUM = VALOR NUM / 5 = VALOR RESTO DE NUM / 5 = VALOR ``` ### Solução ```c #include <stdio.h> int main(void){ int num = 13; printf( "%d x 15 = %d\n" "%d + 42 = %d\n" "123 - %d = %d\n" "%d / 5 = %d\n" "Resto de %d / 5 = %d\n", num, num * 15, num, num + 42, num, 123 - num, num, num / 5, num, num % 5 ); /* end printf */ return 0; } /* end main */ ``` ## 3. Números mágicos O uso de valores literais sem um significado óbvio (**números mágicos**) é uma prática indesejável, mas pode ser evitada de várias formas. Sendo assim, analise as situações abaixo e proponha uma solução. ### Caso 1: Propriedades de círculos Meu programa tem funções para calcular a área e o perímetro de um círculo cujo raio (um inteiro) é recebido como argumento: ```c #include ... float circ_perim(int raio) { /* Circunferência = 2πr */ return 2 * 3.14 * raio; } double circ_area(int raio) { /* Área = πr² */ return 3.14 * raio * raio; } int main(void) { ... } ``` #### Solução 1. Diretiva do pré-processador **1º passo.** Incluir no cabeçalho a diretiva de pré-processador correspondente à definição da constante simbólica PI (`#define PI 3.14`). **2º passo.** Substituir no interior de cada função todas as aparições do número mágico 3.14 pela constante simbólica PI. ```c #include ... #define PI 3.14 float circ_perim(int raio) { /* Circunferência = 2πr */ return 2 * PI * raio; } double circ_area(int raio) { /* Área = πr² */ return PI * raio * raio; } int main(void) { ... } ``` **Vantagens:** - fácil de usar e entender, - funciona em C e C++, - pode ser usada em qualquer lugar do código. **Desvantagens:** - não tem tipo: PI vira apenas um "find and replace" no pré-processamento, - não respeita escopo (vale para todo o código), - erros de digitação não são detectados (por exemplo, PII passa batido). #### Solução 2. Constante tipada **1º passo.** Incluir logo abaixo do cabeçalho e fora de qualquer função a definição da constante pi com tipo float (`const float pi = 3.14`). **2º passo.** Substituir no interior de cada função todas as aparições do número mágico 3.14 pela constante pi. ```c #include ... const float pi = 3.14; float circ_perim(int raio) { /* Circunferência = 2πr */ return 2 * pi * raio; } double circ_area(int raio) { /* Área = πr² */ return pi * raio * raio; } int main(void) { ... } ``` **Vantagens:** - tem tipo (float, double, etc.), ajudando na segurança do código, - pode ser limitada ao escopo (arquivo, função), - o compilador detecta erros se usar o nome errado. **Desvantagens:** - não pode ser usada em inicializações de tamanho de array estático no ANSI C/C90, por exemplo (aí só `#define` mesmo). Exemplo: ```c const int tam = 10; int vetor[tam]; // erro em C clássico! ``` #### Solução 3. Para especificação de padrão de compilação C99 ou posterior **1º passo.** Incluir no cabeçalho a diretiva de pré-processador correspondente à inclusão da biblioteca matemática C (`#include <math.h>`). **2º passo.** Substituir no interior de cada função todas as aparições do número mágico 3.14 pela constante `M_PI` (dupla precisão, 3.141592653589793) já pronta da biblioteca matemática C. ```c #include ... #include <math.h> float circ_perim(int raio) { /* Circunferência = 2πr */ return 2 * M_PI * raio; } double circ_area(int raio) { /* Área = πr² */ return M_PI * raio * raio; } int main(void) { ... } ``` **Vantagens:** - o valor 3.141592653589793 é muito mais próximo de pi do que 3.14, - usar `M_PI` mostra claramente para qualquer programador que aquele valor é pi, não um número qualquer, - se, um dia, precisar mudar algo como uma aproximação diferente, basta mudar em um lugar só, - usar `<math.h>` conecta o compilador com várias funções matemáticas padronizadas (seno, cosseno, tangente, etc.). **Desvantagens:** - `M_PI` não é obrigatório pela especificação C, embora seja comum no GNU/Linux, BSD, Mac, etc, - no Windows/Visual Studio é necessário usar `#define _USE_MATH_DEFINES` antes de `#include <math.h>`, senão `M_PI` pode não ser definido, - pode ser que `M_PI` não exista em alguns compiladores, sendo necessário defini-la manualmente no pré-processador para máxima portabilidade (`#ifndef M_PI #define M_PI 3.14159265358979323846 #endif`), - alguns compiladores antigos ou "minimalistas" podem nem ter `<math.h>` completo ou suportar todas as suas definições, - programas minúsculos (ex: embarcados simples) podem preferir evitar a dependência de `<math.h>` só para pi. ### Caso 2: Multiplicador constante No meu programa, todas as operações aritméticas envolvem a multiplicação de um valor por **10**. ```c #include <stdio.h> int main(void) { int num1 = 13; int num2 = 29; printf("%d x %d = %d\n", num1, 10, num1 * 10); printf("%d x %d = %d\n", num1 + num2, 10, (num1 + num2) * 10); printf("%d x %d = %d\n", num2, 10, num2 * 10); } ``` #### Solução 1. Diretiva do pré-processador **1º passo.** Incluir no cabeçalho a diretiva de pré-processador correspondente à definição da constante simbólica `FATOR_PADRAO` (`#define FATOR_PADRAO 10`). **2º passo.** Substituir no interior de cada função todas as aparições do número mágico 10 pela constante simbólica `FATOR_PADRAO`. ```c #include <stdio.h> #define FATOR_PADRAO 10 int main(void) { int num1 = 13; int num2 = 29; printf("%d x %d = %d\n", num1, FATOR_PADRAO, num1 * FATOR_PADRAO); printf("%d x %d = %d\n", num1 + num2, FATOR_PADRAO, (num1 + num2) * FATOR_PADRAO); printf("%d x %d = %d\n", num2, FATOR_PADRAO, num2 * FATOR_PADRAO); } ``` #### Solução 2. Constante tipada **1º passo.** Incluir logo abaixo do cabeçalho e fora de qualquer função a definição da constante `fator_padrao` com tipo int (`const int fator_padrao = 10`). **2º passo.** Substituir no interior de cada função todas as aparições do número mágico 10 pela constante `fator_padrao`. ```c #include <stdio.h> const int fator_padrao = 10; int main(void) { int num1 = 13; int num2 = 29; printf("%d x %d = %d\n", num1, fator_padrao, num1 * fator_padrao); printf("%d x %d = %d\n", num1 + num2, fator_padrao, (num1 + num2) * fator_padrao); printf("%d x %d = %d\n", num2, fator_padrao, num2 * fator_padrao); } ``` ## 4. Compile, corrija e responda Dado o programa abaixo (por exemplo, `main.c`)... ```c #include <stdio.h> int main(void) { char *fmt = "Circunferência de um círculo de raio %d: %.2f\n"; printf(fmt, r, circ_perim(r)); return 0; } float circ_perim(int raio) { /* Circunferência = 2πr */ return 2 * 3.14 * raio; } ``` ### Qual foi o erro encontrado na compilação? ``` alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula02$ gcc questao4.c -o questao4 questao4.c: In function 'main': questao4.c:5:17: error: 'r' undeclared (first use in this function) 5 | printf(fmt, r, circ_perim(r)); | ^ questao4.c:5:17: note: each undeclared identifier is reported only once for each function it appears in questao4.c:5:20: warning: implicit declaration of function 'circ_perim' [-Wimplicit-function-declaration] 5 | printf(fmt, r, circ_perim(r)); | ^~~~~~~~~~ questao4.c: At top level: questao4.c:9:7: error: conflicting types for 'circ_perim'; have 'float(int)' 9 | float circ_perim(int raio) { | ^~~~~~~~~~ questao4.c:5:20: note: previous implicit declaration of 'circ_perim' with type 'int()' 5 | printf(fmt, r, circ_perim(r)); | ^~~~~~~~~~ ``` ### Por que esse erro aconteceu? **Primeiro erro.** O identificador `r` não foi previamente declarado no programa. Para o compilador, se não foi declarado, então não existe. **Segundo erro.** A função `circ_perim` foi chamada antes de ser declarada como um protótipo ou definida, o que faz o compilador assumir por padrão que ela retorna um `int`. Assim, há um erro de conflito de tipos quando o compilador vê na declaração da função `circ_perim` que ela retorna um `float`. ### Qual foi a solução adotada? **Solução para o primeiro erro.** Declarar a variável `r` antes de usar. **Solução para o segundo erro.** Declarar o protótipo da função `circ_perim` antes da função `main` e defini-la depois da função `main` ou defini-la antes função `main`. ### A sua solução é a única possível? Há pelo menos duas soluções para o primeiro erro e pelo menos duas soluções para o segundo erro. ### Se não for, cite outras possibilidades. **Erro 1, solução 1.** Declarar `r` como variável global antes da função `main`. **Erro 1, solução 2.** Declarar `r` como variável local dentro da função `main` e antes de ser chamada. **Erro 2, solução 1.** Declarar `circ_perim` antes da função `main` e definir depois de `main`. **Erro 2, solução 2.** Definir `circ_perim` antes da função `main`. **Erro 2, solução 3.** Declarar `circ_perim` dentro da função `main`, mas antes de chamar, além de definir `circ_perim` fora de `main` depois de chamar. **Erro 2, solução 4.** Definir `circ_perim` dentro da função `main`, mas antes de chamar. ## 5. Pesquise e responda Pesquise, entre as funções da **glibc**, aquelas que podem solucionar os problemas abaixo e demonstre como elas podem ser utilizadas. ### Pausar a execução do programa por 10 segundos. `sleep(unsigned int seconds)`, suspende a execução do programa pelo número de segundos especificado. ```c #include <unistd.h> int main() { sleep(10); // Pausa por 10 segundos return 0; } ``` ### Apenas imprimir uma quebra de linha. ```c putchar('\n'); puts(""); printf("\n"); ``` ### Ler um número inteiro interativamente e atribuí-lo a uma variável. ```c #include <stdio.h> int main() { int x; scanf("%d", &x); return 0; } ``` ### Imprimir um número inteiro decimal em base 8 e base 16. ```c #include <stdio.h> int main() { int x = 123; printf("Decimal: %d\n", x); printf("Octal: %o\n", x); // base 8 printf("Hexadecimal: %x\n", x); // base 16 (minúsculas) printf("Hexadecimal: %X\n", x); // base 16 (maiúsculas) return 0; } ``` ### Terminar a execução do programa com um valor de estado de término. ```c #include <stdlib.h> int main() { exit(0); } ```
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: blau_araujo/cblc#11
No description provided.