Desafio C
Tagged union para formas geométricas
Enunciado
Crie uma tagged union chamada Forma que pode representar um círculo (com raio float), um retângulo (com base e altura float) ou um triângulo (com base e altura float). Use um enum para a etiqueta. Implemente funções para calcular a área de cada forma e uma função area que recebe uma Forma e retorna a área correta. No main, crie uma forma de cada tipo e imprima suas áreas.
Requisitos
- Definir um enum
TipoFormacom CIRCULO, RETANGULO, TRIANGULO. - Definir uma union com os dados de cada forma (círculo: raio; retângulo: base e altura; triângulo: base e altura).
- Criar uma struct
Formaque contenha o tipo e a union. - Implementar a função
areaque usaswitchna etiqueta para calcular a área correta. - A saída deve mostrar as áreas com duas casas decimais.
Código inicial
#include <stdio.h>
#include <math.h>
// Defina o enum, a union e a struct aqui
// Implemente a função area
int main() {
// Crie e teste as formas
return 0;
}
Saída esperada
Área do círculo: 78.54
Área do retângulo: 12.00
Área do triângulo: 6.00
Ver dica
Use M_PI para o valor de π (inclua <math.h>). Na union, você pode ter uma struct para retângulo e triângulo, ou membros separados. Lembre-se de inicializar a etiqueta corretamente.
Mostrar solução
#include <stdio.h>
#include <math.h>
// Enum com os tipos de forma
typedef enum {
CIRCULO,
RETANGULO,
TRIANGULO
} TipoForma;
// Union com os dados específicos de cada forma
typedef union {
float raio; // para círculo
struct { float base, altura; } ret; // para retângulo
struct { float base, altura; } tri; // para triângulo
} DadosForma;
// Struct que combina a etiqueta e os dados
typedef struct {
TipoForma tipo;
DadosForma dados;
} Forma;
// Calcula a área com base no tipo
float area(Forma f) {
switch (f.tipo) {
case CIRCULO:
return M_PI * f.dados.raio * f.dados.raio;
case RETANGULO:
return f.dados.ret.base * f.dados.ret.altura;
case TRIANGULO:
return (f.dados.tri.base * f.dados.tri.altura) / 2.0f;
default:
return 0.0f;
}
}
int main() {
// Círculo de raio 5
Forma c = { .tipo = CIRCULO, .dados.raio = 5.0f };
// Retângulo 4x3
Forma r = { .tipo = RETANGULO, .dados.ret = { 4.0f, 3.0f } };
// Triângulo base 4, altura 3
Forma t = { .tipo = TRIANGULO, .dados.tri = { 4.0f, 3.0f } };
printf("Área do círculo: %.2f\n", area(c));
printf("Área do retângulo: %.2f\n", area(r));
printf("Área do triângulo: %.2f\n", area(t));
return 0;
}
Passo a passo
- Definimos o
enum TipoFormacom as constantesCIRCULO,RETANGULO,TRIANGULO. - A
union DadosFormacontém umfloat raiopara círculo e duasstructsanônimas para retângulo e triângulo, cada uma combaseealtura. Assim, cada forma usa apenas a memória necessária. - A
struct Formaagrupa a etiquetatipoe osdadosda union. - A função
areausaswitchna etiqueta para acessar o membro correto da union e calcular a área. Para o círculo, usamosM_PIda<math.h>. - No
main, inicializamos três variáveisFormausando inicializadores designados (.tipo = ...,.dados.raio = ..., etc.). - Imprimimos as áreas com
%.2fpara duas casas decimais.
Por que funciona
A tagged union garante que sempre acessamos o membro correto da union, pois a etiqueta indica qual está ativo. O switch direciona para o cálculo apropriado. Como a union compartilha memória, a struct Forma ocupa o espaço do maior membro mais o espaço da etiqueta, economizando memória em comparação com uma struct que tivesse todos os campos.
Erros comuns
- Acessar membro errado da union: se você criar uma
Formacomtipo = CIRCULOmas tentar acessarf.dados.ret.base, o resultado é lixo. Sempre verifique a etiqueta antes de acessar. - Esquecer de incluir
<math.h>: sem ela,M_PInão está definido e o compilador pode emitir erro. Alguns compiladores exigem#define _USE_MATH_DEFINESantes de<math.h>no Windows. - Não inicializar a union corretamente: ao usar inicializadores designados, certifique-se de que a etiqueta corresponda ao membro inicializado. Exemplo:
O correto seriaForma f = { .tipo = RETANGULO, .dados.raio = 5.0f }; // ERRO: raio não é retângulo.dados.ret = { ... }. - Divisão inteira: na área do triângulo, usar
/ 2em vez de/ 2.0fpode truncar o resultado se base e altura forem inteiros. Como são float,/ 2.0fgarante divisão real.
Outra forma de resolver
Poderia usar uma struct com todos os campos e uma flag booleana para indicar qual usar, mas isso desperdiça memória. A tagged union é mais eficiente. Outra alternativa é usar void* e casts, mas perde a segurança de tipos. A tagged union é o padrão em C para tipos variantes.
Saída esperada
Área do círculo: 78.54
Área do retângulo: 12.00
Área do triângulo: 6.00