You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中正确表示FAT12的12位数字数组?

问题描述

我想要在C中实现FAT12规范,其中FAT是一个由12位数字组成的数组。由于C中的数据类型仅支持整字节大小,我尝试在结构体中使用位域来存储两个12位数字,理论上会占用3字节:

struct TwoEntries {
    uint16_t first : 12;
    uint16_t second : 12;
};

但该结构体的实际大小为4字节,这是由结构体填充导致的,填充会使数组无法适配目标数据。因此我想请教:是否存在正确声明12位数字数组的方法?

解决方案

直接用位域结构体的方式,因编译器填充和对齐规则很难实现跨平台的12位条目数组。更可靠的做法是手动处理字节的打包与解包,或使用编译器特定的无填充属性(注意可移植性),以下是具体方案:

方案1:手动字节操作(推荐,跨平台)

FAT12的存储规则是每3个字节存储两个12位条目:

  • 第N个条目(N从0开始):若N为偶数,占用字节3*(N/2)的全部8位 + 字节3*(N/2)+1的低4位;若N为奇数,占用字节3*(N/2)+1的高4位 + 字节3*(N/2)+2的全部8位。

你可以直接用std::array<uint8_t, ...>或普通字节数组存储FAT数据,再编写读写函数访问指定索引的12位值:

#include <cstdint>
#include <vector>

// 读取FAT12中第index个12位条目
uint16_t read_fat12_entry(const std::vector<uint8_t>& fat_data, size_t index) {
    size_t byte_offset = index * 3 / 2;
    if (index % 2 == 0) {
        // 偶数索引:低字节全8位 + 下一字节低4位
        return (static_cast<uint16_t>(fat_data[byte_offset]) | 
                (static_cast<uint16_t>(fat_data[byte_offset + 1] & 0x0F) << 8));
    } else {
        // 奇数索引:上一字节高4位 + 当前字节全8位
        return ((static_cast<uint16_t>(fat_data[byte_offset] & 0xF0) >> 4) | 
                (static_cast<uint16_t>(fat_data[byte_offset + 1]) << 4));
    }
}

// 写入FAT12中第index个12位条目
void write_fat12_entry(std::vector<uint8_t>& fat_data, size_t index, uint16_t value) {
    // 确保value是12位有效数据
    value &= 0x0FFF;
    size_t byte_offset = index * 3 / 2;
    if (index % 2 == 0) {
        // 偶数索引:更新低字节和下一字节的低4位
        fat_data[byte_offset] = static_cast<uint8_t>(value & 0x00FF);
        fat_data[byte_offset + 1] = (fat_data[byte_offset + 1] & 0xF0) | 
                                    static_cast<uint8_t>((value >> 8) & 0x0F);
    } else {
        // 奇数索引:更新当前字节的高4位和下一字节
        fat_data[byte_offset] = (fat_data[byte_offset] & 0x0F) | 
                                static_cast<uint8_t>((value << 4) & 0xF0);
        fat_data[byte_offset + 1] = static_cast<uint8_t>((value >> 4) & 0x00FF);
    }
}

这种方式完全避开结构体填充问题,且符合FAT12实际存储格式,跨平台性最好。

方案2:强制结构体无填充(编译器特定)

如果一定要用结构体,可通过编译器扩展禁用填充,但不同编译器语法不同,且位域布局可能存在差异:

GCC/Clang

struct __attribute__((packed)) TwoEntries {
    uint16_t first : 12;
    uint16_t second : 12;
};

此时结构体大小应为3字节,但需测试确认编译器是否会将两个12位域紧凑排列在3字节中(部分编译器可能仍按uint16_t对齐分配,导致还是4字节)。

MSVC

#pragma pack(push, 1)
struct TwoEntries {
    uint16_t first : 12;
    uint16_t second : 12;
};
#pragma pack(pop)

该方式依赖编译器特性,移植性差,不推荐跨平台项目使用。

方案3:封装成类(更优雅)

把FAT12数组封装成类,内部用字节数组存储,对外提供类似数组的访问接口,比如重载operator[]:

#include <cstdint>
#include <vector>

class FAT12 {
private:
    std::vector<uint8_t> data;

public:
    explicit FAT12(size_t entry_count) {
        // 计算所需字节数:每2个条目占3字节
        size_t byte_count = (entry_count * 3 + 1) / 2;
        data.resize(byte_count, 0);
    }

    uint16_t operator[](size_t index) const {
        size_t byte_offset = index * 3 / 2;
        if (index % 2 == 0) {
            return (static_cast<uint16_t>(data[byte_offset]) | 
                    (static_cast<uint16_t>(data[byte_offset + 1] & 0x0F) << 8));
        } else {
            return ((static_cast<uint16_t>(data[byte_offset] & 0xF0) >> 4) | 
                    (static_cast<uint16_t>(data[byte_offset + 1]) << 4));
        }
    }

    void set_entry(size_t index, uint16_t value) {
        value &= 0x0FFF;
        size_t byte_offset = index * 3 / 2;
        if (index % 2 == 0) {
            data[byte_offset] = static_cast<uint8_t>(value & 0x00FF);
            data[byte_offset + 1] = (data[byte_offset + 1] & 0xF0) | 
                                    static_cast<uint8_t>((value >> 8) & 0x0F);
        } else {
            data[byte_offset] = (data[byte_offset] & 0x0F) | 
                                static_cast<uint8_t>((value << 4) & 0xF0);
            data[byte_offset + 1] = static_cast<uint8_t>((value >> 4) & 0x00FF);
        }
    }

    // 暴露底层数据(比如写入磁盘时使用)
    const std::vector<uint8_t>& get_raw_data() const {
        return data;
    }
};

这样使用更方便,也隐藏了底层字节操作细节。

内容的提问来源于stack exchange,提问作者wediaklup

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 02:20:39