would it be possible to use a struct
Sure.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
 
#define READ(f, var) fread(&(var), sizeof(var), 1, (f))
 
typedef struct ElfHeader {
    uint8_t  ident[16];
    uint16_t type, machine;
    uint32_t version;
    union { uint32_t entry32; uint64_t entry64; };
    union { uint32_t phoff32; uint64_t phoff64; };
    union { uint32_t shoff32; uint64_t shoff64; };
    uint32_t flags;
    uint16_t ehsize, phentsize, phnum;
    uint16_t shentsize, shnum, shstrndx;
} ElfHeader;
 
void readElfHeader(FILE *fin, ElfHeader *elf);
void printElfHeader(const ElfHeader *elf);
const char *sys_name(uint8_t sys);
const char *type_name(uint16_t type);
const char *machine_name(uint16_t machine);
 
int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    FILE *fin = fopen(argv[1], "rb");
    if (!fin) {
        fprintf(stderr, "Error: cannot open input file\n");
        exit(EXIT_FAILURE);
    }
    ElfHeader elf;
    readElfHeader(fin, &elf);
    fclose(fin);
    printElfHeader(&elf);
    return 0;
}
 
void readElfHeader(FILE *fin, ElfHeader *elf) {
    READ(fin, elf->ident);
    READ(fin, elf->type);
    READ(fin, elf->machine);
    READ(fin, elf->version);
    if (elf->ident[4] == 1) { // 32_bits
        READ(fin, elf->entry32);
        READ(fin, elf->phoff32);
        READ(fin, elf->shoff32);
    } else if (elf->ident[4] == 2) { // 64_bits
        READ(fin, elf->entry64);
        READ(fin, elf->phoff64);
        READ(fin, elf->shoff64);
    } else {
        fprintf(stderr, "Error: bad class: %d\n", elf->ident[4]);
        exit(EXIT_FAILURE);
    }
    READ(fin, elf->flags);
    READ(fin, elf->ehsize);
    READ(fin, elf->phentsize);
    READ(fin, elf->phnum);
    READ(fin, elf->shentsize);
    READ(fin, elf->shnum);
    READ(fin, elf->shstrndx);
}
 
void printElfHeader(const ElfHeader *elf) {
    printf("ELF Header:\n");
    printf("  Magic:  ");
    for (int i = 0; i < 16; i++) printf(" %02x", elf->ident[i]);
    printf("\n  %-34s %s\n", "Class:", elf->ident[4] == 1 ? "ELF32" : "ELF64");
    printf("  %-34s %s endian\n", "Data:", elf->ident[5] == 1 ? "little" : "endian");
    printf("  %-34s %u\n", "Version:", elf->ident[6]);
    printf("  %-34s %s\n", "OS:", sys_name(elf->ident[7]));
    printf("  %-34s %u\n", "ABI Version:", elf->ident[8]);
    printf("  %-34s %s\n", "Type:", type_name(elf->type));
    printf("  %-34s %s\n", "Machine:", machine_name(elf->machine));
    printf("  %-34s %x\n", "Version:", elf->version);
    if (elf->ident[4] == 1) { // 32_bits
        printf("  %-34s %x\n", "Entry point address:", elf->entry32);
        printf("  %-34s %u\n", "Start of program headers:", elf->phoff32);
        printf("  %-34s %u\n", "Start of section headers:", elf->shoff32);
    } else { // 64_bits
        printf("  %-34s %lx\n", "Entry point address:", elf->entry64);
        printf("  %-34s %lu\n", "Start of program headers:", elf->phoff64);
        printf("  %-34s %lu\n", "Start of section headers:", elf->shoff64);
    }
    printf("  %-34s %x\n", "Flags:", elf->flags);
    printf("  %-34s %u (bytes)\n", "Size of this header:", elf->ident[4] == 1 ? 32 : 64);
    printf("  %-34s %u\n", "Size of program headers:", elf->phentsize);
    printf("  %-34s %u\n", "Number of program headers:", elf->phnum);
    printf("  %-34s %u\n", "Size of section headers:", elf->shentsize);
    printf("  %-34s %u\n", "Number of section headers:", elf->shnum);
    printf("  %-34s %u\n", "Section header string table index:", elf->shstrndx);
}
 
const char *sys_name(uint8_t sys) {
    switch (sys) {
    case 0x00: return "SYSTEM V";
    case 0x01: return "HP-UX";
    case 0x02: return "NET BSD";
    case 0x03: return "Linux";
    case 0x04: return "HP-UX";
    case 0x05: return "GNU Hurd";
    case 0x06: return "Solaris";
    case 0x07: return "AIX";
    case 0x08: return "IRIX";
    case 0x09: return "FreeBSD";
    case 0x0A: return "TRU64";
    case 0x0B: return "Novell Modesto";
    case 0x0C: return "OpenBSD";
    case 0x0D: return "OpenVMS";
    case 0x0E: return "NonStop Kernel";
    case 0x0F: return "AROS";
    case 0x10: return "Fenix OS";
    case 0x11: return "CloudABI";
    case 0x12: return "Stratus Technologies OpenVOS";
    }
    return "Unknown";
}
 
const char *type_name(uint16_t type) {
    switch (type) {
    case 0x00:   return "NONE";
    case 0x01:   return "REL";
    case 0x02:   return "EXEC";
    case 0x03:   return "DYN";
    case 0x04:   return "CORE";
    case 0xFE00: return "LOOS";
    case 0xFEFF: return "HIOS";
    case 0xFF00: return "LOPROC";
    case 0xFFFF: return "HIPROC";
    }
    return "Unknown";
}
 
const char *machine_name(uint16_t machine) {
    switch (machine) {
    case 0x00: return "No specific instruction set";
    case 0x01: return "AT&T WE 32100";
    case 0x02: return "SPARC";
    case 0x03: return "x86";
    case 0x04: return "Motorola 68000k (M68k)";
    case 0x05: return "Motorola 88000 (M88k)";
    case 0x06: return "Intel MCU";
    case 0x07: return "Intel 80860";
    case 0x08: return "MIPS";
    case 0x09: return "IBM_System/370";
    case 0x0A: return "MIPS RS3000 Little-endian";
    case 0x0B: return "Reserved for future use";
    case 0x0C: return "Reserved for future use";
    case 0x0D: return "Reserved for future use";
    case 0x0E: return "Hewlett-Packard PA-RISC";
    case 0x0F: return "Reserved for future use";
    case 0x13: return "Intel 80960";
    case 0x14: return "PowerPC";
    case 0x15: return "PowerPC(64-bit)";
    case 0x16: return "S390, including S390x";
    case 0x28: return "ARM (up to ARMv7/Aarch32)";
    case 0x2A: return "SuperH";
    case 0x32: return "IA-64";
    case 0x3E: return "amd64";
    case 0x8C: return "TMS320C6000 Family";
    case 0xB7: return "ARM 64-bits (ARMv8/Aarch64)";
    case 0xF3: return "RISC-V";
    case 0xF7: return "Berkeley Packet Filter";
    case 0x101: return "WDC 65C816";
    }
    return "Unknown";
}