Alvaro: Resolução dos exercícios da aula 3 #13

Open
opened 2025-05-26 02:13:49 -03:00 by alvaro · 0 comments

Exercícios da aula 3: Tipos de dados

1. Pesquise e demonstre

No programa quadrado-long.c, da aula 2, encontre uma forma de demonstrar o tipo do valor avaliado pela expressão base * base nesta função:

unsigned long quadrado(long base) { 
    return base * base; /* Qual é o tipo desse expressão? */
}

Solução

#include <stdio.h>
#include <stdlib.h>

#define TIPO_DE(x) _Generic((x),\
    long: "long",\
    unsigned long: "unsigned long",\
    default: "outro tipo")

unsigned long quadrado(long base) {
    printf("Tipo de 'base * base': %s (%zu bytes)\n",
            TIPO_DE(base * base), sizeof(base * base));
    return base * base;
}

int main(void) {
    long num = 100000;
    printf("%ld^2 = %lu\n", num, quadrado(num));
    return EXIT_SUCCESS;
}

Saída:

alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula03$ ./a.out 
Tipo de 'base * base': long (8 bytes)
100000^2 = 10000000000

Explicação

No programa quadrado-long.c, a expressão base * base é avaliada como um valor do tipo long, porque ambos os operandos (base) são do tipo long.

Podemos usar o operador sizeof e _Generic (disponível em C11) para inspecionar o tipo em tempo de compilação.

Como base é long, o resultado é o produto de long por long, portanto long.

O retorno unsigned long da função quadrado só ocorre após a multiplicação, caso em que o tipo da expressão é convertido para unsigned long.

Se base for muito grande (ex: 2.000.000.000), base * base pode estourar (já que long tem limite). Nesse caso, mesmo retornando unsigned long, o cálculo intermediário (long * long) já terá ocorrido com overflow.

2. Desafio: conversão de Fahrenheit para Celsius

Criar um programa que converta valores inteiros de -100ºF a 100ºF para Celsius e apresente os resultados no terminal.

Fórmula da conversão: C = 5 × (F - 32) / 9

Os valores em Fahrenheit serão expressos pela variável i, declarada no loop for.

Por exemplo:

for (int i = -100; i <= 100; i++) {
    /* O código que você achar que deve ser executado... */
}

Solução

#include <stdio.h>
#include <stdlib.h>

float oF_to_oC(int oF) {
    return (float)5 * (oF - 32) / 9;
}

int main(void) {

    for (int i = -100; i <= 100; i += 4)
            printf(
                "%4dºF = %6.2fºC   %4dºF = %6.2fºC   "
                "%4dºF = %6.2fºC   %4dºF = %6.2fºC   "
                "%4dºF = %6.2fº\n",
                i + 0, oF_to_oC(i + 0), i + 1, oF_to_oC(i + 1),
                i + 2, oF_to_oC(i + 2), i + 3, oF_to_oC(i + 3),
                i + 4, oF_to_oC(i + 4));

    return EXIT_SUCCESS;
}

3. Pesquise e responda

O operador sizeof(TIPO) expressa o tamanho em bytes de TIPO com o tipo size_t.

O que é o tipo size_t?

size_t é um tipo de dado inteiro sem sinal (unsigned) definido pela linguagem C para representar tamanhos de objetos em bytes. Ele é usado principalmente para retornar o resultado de sizeof(), representar índices e tamanhos em funções de manipulação de memória (malloc, memcpy, etc) e evitar problemas de portabilidade, já que seu tamanho varia conforme a arquitetura (32-bit ou 64-bit).

Como utilizá-lo no seu programa sem incluir cabeçalhos?

Se você não quiser incluir <stddef.h> ou <stdio.h>, pode declarar size_t manualmente no seu programa, mas isso não é recomendado, pois o tamanho exato de size_t depende do compilador e do sistema. Sem os cabeçalhos padrão perde-se a portabilidade.

// Declaração aproximada (não portável)
typedef unsigned long size_t;  // Em sistemas 64-bit
// ou
typedef unsigned int size_t;   // Em sistemas 32-bit

Em quais cabeçalhos ele é definido?

size_t é definido nos seguintes cabeçalhos padrão:

  • <stddef.h> (Definição principal)
  • <stdio.h> (Para funções de I/O como printf)
  • <stdlib.h> (Para funções como malloc)
  • <string.h> (Para funções como strlen)

Exemplo de uso recomendado:

#include <stddef.h>  // Para size_t
#include <stdio.h>   // Para printf
#include <stdlib.h>

int main() {
    size_t tamanho = sizeof(int);
    printf("Tamanho: %zu\n", tamanho);
    return EXIT_SUCCESS;
}

Qual é o especificador de formatos para ele no printf?

O especificador correto para size_t no printf é %zu (recomendado, portável e seguro).

Exemplo de uso recomendado:

#include <stddef.h>  // Para size_t
#include <stdio.h>   // Para printf
#include <stdlib.h>

int main() {
    size_t x = 42;
    printf("Valor: %zu\n", x);  // Correto!
    return EXIT_SUCCESS;
}

Alternativas (não portáveis e não recomendadas):

  • %lu (se size_t for unsigned long)
  • %u (se size_t for unsigned int)

Essas alternativas devem ser evitadas, pois elas podem causar comportamento indefinido em sistemas onde size_t não corresponde ao tipo assumido.

4. Compile, corrija e responda

O programa abaixo deveria imprimir 0.25 no terminal…

#include <stdio.h>

int main(void) {
    int valor = 1 / 4;
    
    printf("%d\n", valor);
    
    return 0;
}

Responda:

Depois de compilado e executado, qual foi o valor impresso?

O programa imprime 0 e não 0.25 como esperado.

alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula03$ gcc aula03f.c 
alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula03$ ./a.out 
0

Por que isso aconteceu?

O problema está na linha "int valor = 1 / 4;", pois ocorre uma divisão inteira, já que int / int = int.

Quando ambos os operandos são inteiros (1 e 4), o C realiza uma divisão inteira, descartando a parte fracionária.

1 / 4 resulta em quociente 0 e resto 1, por isso o resultado foi 0.

Como solucionar?

Existem pelo menos 2 formas de corrigir.

Solução 1. Usar float ou double em pelo menos um operando decimal, pois se um dos operandos é de ponto flutuante (float/double), a divisão será de ponto flutuante.

Exemplo:

float valor = 1.0f / 4;  // Ou 1 / 4.0f, ou 1.0f / 4.0f
printf("%.2f\n", valor);  // Saída: 0.25

Solução 2. Converter explicitamente um operando para float/double usando casting.

Exemplo:

float valor = (float)1 / 4;  // Casting para float
printf("%.2f\n", valor);     // Saída: 0.25

5. Analise, pesquise e comente o código

Programa dump.c:

#include <stdio.h>
 
void print_bytes(char *label, void *addr, size_t size); 

int main(void) {
    
    int num = 100000;

    int sqr_int = num * num;
    unsigned long sqr_long = (unsigned long)num * num;

    print_bytes("num     ", &num, sizeof(num));
    printf("-> %d\n", num);
    
    print_bytes("sqr_int ", &sqr_int, sizeof(sqr_int));
    printf("-> %d\n", sqr_int);
    
    print_bytes("sqr_long", &sqr_long, sizeof(sqr_long));
    printf("-> %lu\n", sqr_long);
    
    return 0;
}


void print_bytes(char *label, void *addr, size_t size) {

    unsigned char *bytes  = (unsigned char *)addr;

    printf("%p - %s - ", bytes, label);

    for (size_t i = 0; i < 8; i++)
        printf("%02x ", i > (size - 1) ? 0 : bytes[i]);
}

Solução

#include <stdio.h>

void print_bytes(char *label, void *addr, size_t size);
/* Função print_bytes. Recebe como argumentos nesta ordem:
    1. o dado guardado na posição de memória cujo endereço está associado a uma
       variável ponteiro para char chamado *label (label = rótulo),
    2. o dado guardado na posição de memória cujo endereço está associado a uma
       variável ponteiro sem tipo definido chamado *addr (address = endereço) e
    3. o número size de tipo size_t. */

int main(void) {
    int num = 100000;
    /* Define o inteiro num e o inicializa com o valor 100000.  */

    int sqr_int = num * num;
    /* Define o inteiro sqr_int (square integer) e tenta inicializar com o valor
       num * num. Isso certamente gerará overflow, já que int possui 4 bytes e
       vai até no máximo 2^(4x8) / 2 - 1 = 2^31 - 1 = 2.147.483.647 e esse valor
       é quase 5 vezes menor que   100000 x 100000 = 10.000.000.000.  */
    
    unsigned long sqr_long = (unsigned long)num * num;
    /* Define o inteiro sqr_long (square long integer) e o inicializa com o
       valor num * num. Isso certamente não gerará overflow, já que long possui
       8 bytes e vai até no máximo 2^(8x8) / 2 - 1 = 2^63 - 1 =
       9.223.372.036.854.775.807 e esse valor é absurdamente maior que
                  10.000.000.000 = 100000 x 100000.  */
    
    print_bytes("num     ", &num, sizeof(num));
    /* Imprime o endereço de memória que possui uma cópia do dado de num
       alterado para ponteiro unsigned char, a string com o nome de num e
       byte a byte do dado contido nesse endereço. Tudo isso sem quebra de
       linha. */
    
    printf("-> %d\n", num);
    /* Imprime o valor armazenado em num e uma quebra de linha. */
    
    print_bytes("sqr_int ", &sqr_int, sizeof(sqr_int));
    /* Imprime o endereço de memória que possui uma cópia do dado de sqr_int
       alterado para ponteiro unsigned char, a string com o nome de sqr_int e
       byte a byte do dado contido nesse endereço. Tudo isso sem quebra de
       linha. */
    
    printf("-> %d\n", sqr_int);
    /* Imprime o valor armazenado em sqr_int e uma quebra de linha. */
    
    print_bytes("sqr_long", &sqr_long, sizeof(sqr_long));
    /* Imprime o endereço de memória que possui uma cópia do dado de sqr_long
       alterado para ponteiro unsigned char, a string com o nome de sqr_long e
       byte a byte do dado contido nesse endereço. Tudo isso sem quebra de
       linha. */
    
    printf("-> %lu\n", sqr_long);
    /* Imprime o valor armazenado em sqr_long e uma quebra de linha. */

    return 0;
}

void print_bytes(char *label, void *addr, size_t size) {
/* Função print_bytes, imprime nesta ordem:
   1. o endereço guardado pelo ponteiro bytes que armazena o valor de *addr
      modificado para unsigned char,
   2. a string que inicia no endereço armazenado no ponteiro *label e
   3. o valor em hexadecimal, byte a byte na ordenação little-endian, do
      número unsigned char armazenado no endereço guardado por *bytes. */

    unsigned char *bytes  = (unsigned char *)addr;
        /* Define o ponteiro *bytes como unsigned char, muda o tipo do dado
       armazenado no endereço associado ao ponteiro *addr para unsigned char e
       inicializa o ponteiro *bytes para o valor anterior que sofreu casting.*/

    printf("%p - %s - ", bytes, label);
    /* Imprime o dado de endereço associado ao ponteiro *bytes e o dado string
       de char armazenado a partir do endereço inicial de *label até o
       caractere \0, mas não faz quebra de linha. */

    for (size_t i = 0; i < 8; i++)
    /* Define o contador i como size_t, inicializa i como 0 e itera por
       incremento de i até que o valor de i seja 7. Ou seja i vai variar na
       sequência (0, 1, 2, 3, 4, 5, 6, 7). */
        printf("%02x ", i > (size - 1) ? 0 : bytes[i]);
        /* Continuando a impressão anterior na mesma linha, imprime o i-ésimo
           byte em hexadecimal do dado armazenado no endereço associado ao
           ponteiro *bytes do byte menos significativo para o byte mais
           significativo e da esquerda para a direita (little-endian) enquanto
           o contador i for menor ou igual ao valor de size - 1, ou seja i
           percorrerá size valores, de 0 a size - 1. Quando i for maior que
           size - 1 serão impressos números 00 até que i seja igual a 7.
           Assim serão escritos os valores hexadecimais dos 8 bytes do dado.
           Tudo isso sem quebra de linha.*/
}

Saída:

alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula03$ ./a.out 
0x7ffe8948f208 - num      - a0 86 01 00 00 00 00 00 -> 100000
0x7ffe8948f20c - sqr_int  - 00 e4 0b 54 00 00 00 00 -> 1410065408
0x7ffe8948f210 - sqr_long - 00 e4 0b 54 02 00 00 00 -> 10000000000

Observações:

  1. Em "(unsigned long)num * num;" o cast força a multiplicação a ser feita em unsigned long (64 bits), evitando overflow.

  2. A função print_bytes converte addr para unsigned char* (byte a byte).

  3. Saídas esperadas para num = 100000 (little-endian):

    num (int, 4 bytes):

    • Valor: 0x000186a0 → Bytes: a0 86 01 00
    • Bytes: a0 86 01 00 00 00 00 00

    sqr_int (overflow):

    • 10.000.000.000 em 32 bits é truncado para 0x540BE400 e vira o decimal 1.410.065.408. Bem menor que o valor esperado.
    • Bytes: 00 e4 0b 54 00 00 00 00

    sqr_long (64 bits):

    • 10.000.000.000 em 64 bits é representado integralmente como 0x00000002540BE400
    • Bytes: 00 e4 0b 54 02 00 00 00

Problemas e soluções:

  1. Overflow em sqr_int:

    • int (32 bits) não consegue armazenar 10.000.000.000
    • Solução: Usar long long ou unsigned long desde o início
  2. Portabilidade:

    • O tamanho de long varia (32/64 bits)
    • Garantia: Usar uint64_t (do <stdint.h>) para precisão

Sugestões de Melhoria

1. Adicionar Verificação de Overflow:

if (num > INT_MAX / num) {
    printf("Aviso: Overflow em sqr_int!\n");
}

2. Usar Tipos de Tamanho Fixo:

#include <stdint.h>
uint64_t sqr = (uint64_t)num * num;
# Exercícios da aula 3: Tipos de dados ## 1. Pesquise e demonstre No programa `quadrado-long.c`, da aula 2, encontre uma forma de demonstrar o tipo do valor avaliado pela expressão `base * base` nesta função: ```c unsigned long quadrado(long base) { return base * base; /* Qual é o tipo desse expressão? */ } ``` ### Solução ```c #include <stdio.h> #include <stdlib.h> #define TIPO_DE(x) _Generic((x),\ long: "long",\ unsigned long: "unsigned long",\ default: "outro tipo") unsigned long quadrado(long base) { printf("Tipo de 'base * base': %s (%zu bytes)\n", TIPO_DE(base * base), sizeof(base * base)); return base * base; } int main(void) { long num = 100000; printf("%ld^2 = %lu\n", num, quadrado(num)); return EXIT_SUCCESS; } ``` **Saída:** ``` alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula03$ ./a.out Tipo de 'base * base': long (8 bytes) 100000^2 = 10000000000 ``` ### Explicação No programa `quadrado-long.c`, a expressão `base * base` é avaliada como um valor do tipo `long`, porque ambos os operandos (`base`) são do tipo `long`. Podemos usar o operador `sizeof` e `_Generic` (disponível em C11) para inspecionar o tipo em tempo de compilação. Como `base` é `long`, o resultado é o produto de `long` por `long`, portanto `long`. O retorno `unsigned long` da função `quadrado` só ocorre após a multiplicação, caso em que o tipo da expressão é convertido para `unsigned long`. Se `base` for muito grande (ex: 2.000.000.000), `base * base` pode estourar (já que `long` tem limite). Nesse caso, mesmo retornando `unsigned long`, o cálculo intermediário (`long * long`) já terá ocorrido com overflow. ## 2. Desafio: conversão de Fahrenheit para Celsius Criar um programa que converta valores inteiros de **-100ºF** a **100ºF** para Celsius e apresente os resultados no terminal. **Fórmula da conversão:** `C = 5 × (F - 32) / 9` Os valores em Fahrenheit serão expressos pela variável `i`, declarada no loop `for`. Por exemplo: ```c for (int i = -100; i <= 100; i++) { /* O código que você achar que deve ser executado... */ } ``` ### Solução ```c #include <stdio.h> #include <stdlib.h> float oF_to_oC(int oF) { return (float)5 * (oF - 32) / 9; } int main(void) { for (int i = -100; i <= 100; i += 4) printf( "%4dºF = %6.2fºC %4dºF = %6.2fºC " "%4dºF = %6.2fºC %4dºF = %6.2fºC " "%4dºF = %6.2fº\n", i + 0, oF_to_oC(i + 0), i + 1, oF_to_oC(i + 1), i + 2, oF_to_oC(i + 2), i + 3, oF_to_oC(i + 3), i + 4, oF_to_oC(i + 4)); return EXIT_SUCCESS; } ``` ## 3. Pesquise e responda O operador `sizeof(TIPO)` expressa o tamanho em bytes de `TIPO` com o tipo `size_t`. ### O que é o tipo `size_t`? `size_t` é um tipo de dado inteiro sem sinal (unsigned) definido pela linguagem C para representar tamanhos de objetos em bytes. Ele é usado principalmente para retornar o resultado de `sizeof()`, representar índices e tamanhos em funções de manipulação de memória (`malloc`, `memcpy`, etc) e evitar problemas de portabilidade, já que seu tamanho varia conforme a arquitetura (32-bit ou 64-bit). ### Como utilizá-lo no seu programa sem incluir cabeçalhos? Se você não quiser incluir `<stddef.h>` ou `<stdio.h>`, pode declarar `size_t` manualmente no seu programa, mas isso não é recomendado, pois o tamanho exato de `size_t` depende do compilador e do sistema. Sem os cabeçalhos padrão perde-se a portabilidade. ```c // Declaração aproximada (não portável) typedef unsigned long size_t; // Em sistemas 64-bit // ou typedef unsigned int size_t; // Em sistemas 32-bit ``` ### Em quais cabeçalhos ele é definido? `size_t` é definido nos seguintes cabeçalhos padrão: - `<stddef.h>` (Definição principal) - `<stdio.h>` (Para funções de I/O como `printf`) - `<stdlib.h>` (Para funções como `malloc`) - `<string.h>` (Para funções como `strlen`) **Exemplo de uso recomendado:** ```c #include <stddef.h> // Para size_t #include <stdio.h> // Para printf #include <stdlib.h> int main() { size_t tamanho = sizeof(int); printf("Tamanho: %zu\n", tamanho); return EXIT_SUCCESS; } ``` ### Qual é o especificador de formatos para ele no `printf`? O especificador correto para `size_t` no `printf` é **`%zu`** (recomendado, portável e seguro). **Exemplo de uso recomendado:** ```c #include <stddef.h> // Para size_t #include <stdio.h> // Para printf #include <stdlib.h> int main() { size_t x = 42; printf("Valor: %zu\n", x); // Correto! return EXIT_SUCCESS; } ``` **Alternativas (não portáveis e não recomendadas):** - `%lu` (se `size_t` for `unsigned long`) - `%u` (se `size_t` for `unsigned int`) Essas alternativas devem ser evitadas, pois elas podem causar comportamento indefinido em sistemas onde `size_t` não corresponde ao tipo assumido. ## 4. Compile, corrija e responda O programa abaixo deveria imprimir **0.25** no terminal… ```c #include <stdio.h> int main(void) { int valor = 1 / 4; printf("%d\n", valor); return 0; } ``` ### Responda: #### Depois de compilado e executado, qual foi o valor impresso? O programa imprime **0** e não **0.25** como esperado. ``` alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula03$ gcc aula03f.c alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula03$ ./a.out 0 ``` #### Por que isso aconteceu? O problema está na linha `"int valor = 1 / 4;"`, pois ocorre uma **divisão inteira**, já que `int / int = int`. Quando ambos os operandos são inteiros (1 e 4), o C realiza uma divisão inteira, descartando a parte fracionária. `1 / 4` resulta em quociente **0** e resto **1**, por isso o resultado foi **0**. #### Como solucionar? Existem pelo menos **2 formas** de corrigir. **Solução 1.** Usar `float` ou `double` em pelo menos um operando decimal, pois se um dos operandos é de ponto flutuante (`float`/`double`), a divisão será de ponto flutuante. **Exemplo:** ```c float valor = 1.0f / 4; // Ou 1 / 4.0f, ou 1.0f / 4.0f printf("%.2f\n", valor); // Saída: 0.25 ``` **Solução 2.** Converter explicitamente um operando para `float`/`double` usando casting. **Exemplo:** ```c float valor = (float)1 / 4; // Casting para float printf("%.2f\n", valor); // Saída: 0.25 ``` ## 5. Analise, pesquise e comente o código **Programa dump.c:** ```c #include <stdio.h> void print_bytes(char *label, void *addr, size_t size); int main(void) { int num = 100000; int sqr_int = num * num; unsigned long sqr_long = (unsigned long)num * num; print_bytes("num ", &num, sizeof(num)); printf("-> %d\n", num); print_bytes("sqr_int ", &sqr_int, sizeof(sqr_int)); printf("-> %d\n", sqr_int); print_bytes("sqr_long", &sqr_long, sizeof(sqr_long)); printf("-> %lu\n", sqr_long); return 0; } void print_bytes(char *label, void *addr, size_t size) { unsigned char *bytes = (unsigned char *)addr; printf("%p - %s - ", bytes, label); for (size_t i = 0; i < 8; i++) printf("%02x ", i > (size - 1) ? 0 : bytes[i]); } ``` ### Solução ```c #include <stdio.h> void print_bytes(char *label, void *addr, size_t size); /* Função print_bytes. Recebe como argumentos nesta ordem: 1. o dado guardado na posição de memória cujo endereço está associado a uma variável ponteiro para char chamado *label (label = rótulo), 2. o dado guardado na posição de memória cujo endereço está associado a uma variável ponteiro sem tipo definido chamado *addr (address = endereço) e 3. o número size de tipo size_t. */ int main(void) { int num = 100000; /* Define o inteiro num e o inicializa com o valor 100000. */ int sqr_int = num * num; /* Define o inteiro sqr_int (square integer) e tenta inicializar com o valor num * num. Isso certamente gerará overflow, já que int possui 4 bytes e vai até no máximo 2^(4x8) / 2 - 1 = 2^31 - 1 = 2.147.483.647 e esse valor é quase 5 vezes menor que 100000 x 100000 = 10.000.000.000. */ unsigned long sqr_long = (unsigned long)num * num; /* Define o inteiro sqr_long (square long integer) e o inicializa com o valor num * num. Isso certamente não gerará overflow, já que long possui 8 bytes e vai até no máximo 2^(8x8) / 2 - 1 = 2^63 - 1 = 9.223.372.036.854.775.807 e esse valor é absurdamente maior que 10.000.000.000 = 100000 x 100000. */ print_bytes("num ", &num, sizeof(num)); /* Imprime o endereço de memória que possui uma cópia do dado de num alterado para ponteiro unsigned char, a string com o nome de num e byte a byte do dado contido nesse endereço. Tudo isso sem quebra de linha. */ printf("-> %d\n", num); /* Imprime o valor armazenado em num e uma quebra de linha. */ print_bytes("sqr_int ", &sqr_int, sizeof(sqr_int)); /* Imprime o endereço de memória que possui uma cópia do dado de sqr_int alterado para ponteiro unsigned char, a string com o nome de sqr_int e byte a byte do dado contido nesse endereço. Tudo isso sem quebra de linha. */ printf("-> %d\n", sqr_int); /* Imprime o valor armazenado em sqr_int e uma quebra de linha. */ print_bytes("sqr_long", &sqr_long, sizeof(sqr_long)); /* Imprime o endereço de memória que possui uma cópia do dado de sqr_long alterado para ponteiro unsigned char, a string com o nome de sqr_long e byte a byte do dado contido nesse endereço. Tudo isso sem quebra de linha. */ printf("-> %lu\n", sqr_long); /* Imprime o valor armazenado em sqr_long e uma quebra de linha. */ return 0; } void print_bytes(char *label, void *addr, size_t size) { /* Função print_bytes, imprime nesta ordem: 1. o endereço guardado pelo ponteiro bytes que armazena o valor de *addr modificado para unsigned char, 2. a string que inicia no endereço armazenado no ponteiro *label e 3. o valor em hexadecimal, byte a byte na ordenação little-endian, do número unsigned char armazenado no endereço guardado por *bytes. */ unsigned char *bytes = (unsigned char *)addr; /* Define o ponteiro *bytes como unsigned char, muda o tipo do dado armazenado no endereço associado ao ponteiro *addr para unsigned char e inicializa o ponteiro *bytes para o valor anterior que sofreu casting.*/ printf("%p - %s - ", bytes, label); /* Imprime o dado de endereço associado ao ponteiro *bytes e o dado string de char armazenado a partir do endereço inicial de *label até o caractere \0, mas não faz quebra de linha. */ for (size_t i = 0; i < 8; i++) /* Define o contador i como size_t, inicializa i como 0 e itera por incremento de i até que o valor de i seja 7. Ou seja i vai variar na sequência (0, 1, 2, 3, 4, 5, 6, 7). */ printf("%02x ", i > (size - 1) ? 0 : bytes[i]); /* Continuando a impressão anterior na mesma linha, imprime o i-ésimo byte em hexadecimal do dado armazenado no endereço associado ao ponteiro *bytes do byte menos significativo para o byte mais significativo e da esquerda para a direita (little-endian) enquanto o contador i for menor ou igual ao valor de size - 1, ou seja i percorrerá size valores, de 0 a size - 1. Quando i for maior que size - 1 serão impressos números 00 até que i seja igual a 7. Assim serão escritos os valores hexadecimais dos 8 bytes do dado. Tudo isso sem quebra de linha.*/ } ``` **Saída:** ``` alvaro@dinosaur:~/estudo/debxp/cblc_resolucoes/aula03$ ./a.out 0x7ffe8948f208 - num - a0 86 01 00 00 00 00 00 -> 100000 0x7ffe8948f20c - sqr_int - 00 e4 0b 54 00 00 00 00 -> 1410065408 0x7ffe8948f210 - sqr_long - 00 e4 0b 54 02 00 00 00 -> 10000000000 ``` ### Observações: 1. Em `"(unsigned long)num * num;"` o cast força a multiplicação a ser feita em `unsigned long` (64 bits), evitando overflow. 2. A função `print_bytes` converte `addr` para `unsigned char*` (byte a byte). 3. **Saídas esperadas para `num = 100000` (little-endian):** **`num` (`int`, 4 bytes):** - Valor: `0x000186a0` → Bytes: `a0 86 01 00` - Bytes: `a0 86 01 00 00 00 00 00` **`sqr_int` (overflow):** - `10.000.000.000` em 32 bits é truncado para `0x540BE400` e vira o decimal `1.410.065.408`. Bem menor que o valor esperado. - Bytes: `00 e4 0b 54 00 00 00 00` **`sqr_long` (64 bits):** - `10.000.000.000` em 64 bits é representado integralmente como `0x00000002540BE400` - Bytes: `00 e4 0b 54 02 00 00 00` ### Problemas e soluções: 1. **Overflow em `sqr_int`:** - `int` (32 bits) não consegue armazenar `10.000.000.000` - **Solução:** Usar `long long` ou `unsigned long` desde o início 2. **Portabilidade:** - O tamanho de `long` varia (32/64 bits) - **Garantia:** Usar `uint64_t` (do `<stdint.h>`) para precisão ### Sugestões de Melhoria **1. Adicionar Verificação de Overflow:** ```c if (num > INT_MAX / num) { printf("Aviso: Overflow em sqr_int!\n"); } ``` **2. Usar Tipos de Tamanho Fixo:** ```c #include <stdint.h> uint64_t sqr = (uint64_t)num * num; ```
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#13
No description provided.