写操作系统的lab时,需要读取fat12文件系统中BPB的信息,主要思路就是创个BPB的类,成员变量就是BytesPerSec, SecPerClus这些,然后通过fseek
和fread
来读取,从而实现成员变量的赋值。
开始的代码大致如下:
// 这是份错误代码
unsigned short BytesPerSec;
unsigned char SecPerClus;
unsigned short ResvdSecCnt;
unsigned char NumFATs;
unsigned short RootEntCnt;
unsigned short TotSec16;
unsigned char Media;
unsigned short FATSz16;
unsigned short SecPerTrk;
unsigned short NumHeads;
unsigned int HiddSec;
unsigned int TotSec32;
unsigned int BytesPerClus;
unsigned int fatBase;
unsigned int rootBase;
unsigned int fileBase;
// 使struct的成员变量按1字节对齐
// 默认的是4字节
// #pragma pack(1)
class BPB {
unsigned short BPB_BytesPerSec;
unsigned char BPB_SecPerClus;
unsigned short BPB_ResvdSecCnt;
unsigned char BPB_NumFATs;
unsigned short BPB_RootEntCnt;
unsigned short BPB_TotSec16;
unsigned char BPB_Media;
unsigned short BPB_FATSz16;
unsigned short BPB_SecPerTrk;
unsigned short BPB_NumHeads;
unsigned int BPB_HiddSec;
unsigned int BPB_TotSec32;
public:
BPB() = default;
void init(FILE *img) {
fseek(img, 11, SEEK_SET);
// 使用this指针正好给类中的成员赋值
fread(this, 1, 25, img);
BytesPerSec = this->BPB_BytesPerSec;
SecPerClus = this->BPB_SecPerClus;
ResvdSecCnt = this->BPB_ResvdSecCnt;
NumFATs = this->BPB_NumFATs;
RootEntCnt = this->BPB_RootEntCnt;
TotSec16 = this->BPB_TotSec16;
Media = this->BPB_Media;
FATSz16 = this->BPB_FATSz16;
SecPerTrk = this->BPB_SecPerTrk;
NumHeads = this->BPB_NumHeads;
HiddSec = this->BPB_HiddSec;
TotSec32 = BPB_TotSec16 == 0 ? BPB_TotSec32 : 0; // if TotSec16!=0, TotSec32就是0,而不是TotSec32
BytesPerClus = BytesPerSec * SecPerClus;
fatBase = ResvdSecCnt * BytesPerSec;
rootBase = fatBase + NumFATs * FATSz16 * BytesPerSec;
fileBase = rootBase + (RootEntCnt * 32 + BytesPerSec - 1) / BytesPerSec * BytesPerSec;
}
};
#pragma pack(1)
然而事情并不简单,首先是结构体的内存对齐方式,对于char、short,成员默认的对齐方式是4个字节,从而提高效率(C++中结构体和类除了默认访问权限不一样,其他基本一样)
class Line {
char s;
int a;
}
// sizeof(Line) = 8
// 因为char占用了4个字节
#pragma pack(1)
可以让数据以连续形式存储,从而使得fread
将读到的字节对应放入成员变量中。
fread
的精妙之处在于使用了this
,直接将读取到的值赋给成员变量,从而省略了手动赋值的麻烦
unsigned char
因为SecPerClus
只需要1字节,所以我们想当然地使用了unsigned char
,毕竟char也只是1字节嘛。但是有个十分重要的问题,那就是我们使用unsigned char
的目的是为了存取数值,而cout << char
会显示ASCII码中对应的字符
因为我们定义的全局变量SecPerClus
Media
类型为unsigned char
,如果直接输出SecPerClus
的值就是空的,因为ASCII码为1时对应的是开始符SOH
(Start of Heading)。
解决方法就是 定义的全局变量类型全改为int,一劳永逸
当初想着验证下读取的对不对,就写了个helper函数输出所有信息,谁知道会遇到这种问题,还是经验不够哇