Quake2源码中C语言指针与数组相关代码行功能详解
Quake 2 源码文件头读取逻辑逐行解释
这段代码是Quake2加载碰撞模型、BSP地图文件时,解析文件头的核心逻辑,buf是把整个目标文件完整读入内存后的起始地址指针。
#1行 header = *(dheader_t *)buf; 作用
这行是典型的90年代C语言游戏开发里的裸内存块拷贝写法:
- 首先把原本声明为
unsigned int*的缓冲区指针,强转为dheader_t结构体类型的指针,之后直接解引用,把缓冲区起始位置开始、长度刚好等于dheader_t结构体大小的连续内存,整块复制到栈上的header结构体变量中。 - 这种写法比逐字段赋值、或者调用
memcpy的代码更短,在当年的编译器下生成的指令效率也更高。因为dheader_t的所有成员都是4字节int类型,结构体内部不会有编译器自动插入的对齐填充字节,内存布局完全连续,这么拷贝不会出现数据错位。 - 注意:这一步拷贝出来的
header里存的是磁盘文件里的原始字节数据,数值不一定和当前运行设备的CPU字节序匹配,不能直接用,所以才需要第二行的转换处理。
#2行循环代码的作用
对应的代码片段:
for (i=0 ; i<sizeof(dheader_t)/4 ; i++) ((int *)&header)[i] = LittleLong ( ((int *)&header)[i]);
这段是做全结构体的批量小端字节序适配,逻辑拆分如下:
- 循环终止条件
sizeof(dheader_t)/4的逻辑很直接:整个结构体的所有成员都是按4字节int对齐的,总大小一定是4的整数倍,除以4就得到整个结构体总共包含多少个4字节的int存储单元,循环就是逐单元遍历整个结构体的全部内存。 - 循环内的
(int *)&header操作,是把header结构体的起始地址强转成int数组指针,用下标[i]就可以直接访问结构体里第i个4字节块——不管这个块属于ident标识、version版本号,还是后面lumps数组里的成员,只要是4字节长度就统一按int处理。 LittleLong是id引擎从初代Quake就沿用的字节序转换函数:运行时先判断当前CPU的字节序,如果是x86这类天生小端的CPU,直接返回传入值不做任何修改;如果是PowerPC、SPARC这类大端架构的CPU,就把传入的4字节整数的字节顺序翻转成小端序再返回。- 做字节序转换的核心原因:Quake2所有的资源文件(地图、模型、资源包)全是按小端字节序存在磁盘上的,当年id开发时主力平台是x86,但也要兼容其他大端架构的设备,所有从文件读出来的多字节整数都要过一遍这个转换,才能保证不管在什么CPU上跑,程序读到的数值都是正确的。
补充:按现在的C语言标准,这种把结构体指针强转成int指针访问的写法属于类型双关的未定义行为,但90年代用的老编译器(Watcom C、早期MSVC)都不会对这类代码做激进优化破坏运行逻辑,在当时的开发环境下是兼顾效率和可移植性的常规写法。
内容的提问来源于stack exchange,提问作者darro911
相关产品推荐
相关产品推荐

