请忽略这些建议您“直接写入文件”的人。有很多问题,最终属于“整数表示”的范畴。似乎有一些令人信服的理由将整数直接写入外部存储使用fwrite
或不使用,这里有一些可靠的事实。
瓶颈是外部存储控制器。如果您正在编写网络应用程序,要么是那个,要么是网络。因此,如果您的内存配置文件适合您的平台,则将两个字节写为单个fwrite
或两个不同的 s 应该具有大致相同的速度。您可以使用一定程度调整您的 s 使用fputc
的缓冲区量(注意:必须是 2 的幂),因此我们始终可以根据我们的分析器告诉我们的内容对每个平台进行微调,尽管这些信息可能应该优雅地浮动通过温和的建议向标准库上游提供对其他项目也有用的建议。FILE *
setvbuf
当今计算机之间的基础整数表示是不一致的。假设您unsigned int
使用使用 32 位整数和大端表示的系统 X 直接将 s 写入文件,您最终会在使用 16 位整数和小端表示的系统 Y 或系统 Z 上读取该文件时遇到问题使用具有混合字节序表示和 32 个填充位的 64 位整数。如今,我们拥有这种 15 年前的计算机组合,人们用 ARM big.Little SoC、智能手机和智能电视、游戏机和 PC 来折磨自己,所有这些都有自己的怪癖,超出了标准 C 的范围,特别是在整数表示、填充等方面。
C 的开发考虑到了抽象,允许您以可移植的方式表达您的算法,因此您不必为每个操作系统编写不同的代码!这是一个读取四个十六进制数字并将其转换为unsigned int
值的示例,可移植:
unsigned int value;
int value_is_valid = fscanf(fd, "%04x", &value) == 1;
assert(value_is_valid); // #include <assert.h>
/* NOTE: Actual error correction should occur in place of that
* assertioon
*/
我应该指出我选择%04X
而不是%08X
更现代的原因......如果我们即使在今天也提出问题,不幸的是有些学生使用超过 20 年的教科书和编译器......他们int
是 16-有点和技术上,他们的编译器在这方面是兼容的(尽管他们真的应该在整个学术界推动 gcc 和 llvm)。考虑到可移植性,这是我编写该值的方式:
value &= 0xFFFF;
fprintf(fd, "%04x", value);
// side-note: We often don't check the return value of `fprintf`, but it can also become \
very important, particularly when dealing with streams and large files...
假设您的unsigned int
值占用两个字节,这就是我如何使用大端表示法读取这两个字节的方式:
int hi = fgetc(fd);
int lo = fgetc(fd);
unsigned int value = 0;
assert(hi >= 0 && lo >= 0); // again, proper error detection & handling logic should be here
value += hi & 0xFF; value <<= 8;
value += lo & 0xFF;
...这就是我如何按照大端顺序编写这两个字节的方式:
fputc((value >> 8) & 0xFF, fd);
fputc(value & 0xFF, fd);
// and you might also want to check this return value (perhaps in a finely tuned end product)
也许你对小端更感兴趣。整洁的事情是,代码真的没有那么不同。这是输入:
int lo = fgetc(fd);
int hi = fgetc(fd);
unsigned int value = 0;
assert(hi >= 0 && lo >= 0);
value += hi & 0xFF; value <<= 8;
value += lo & 0xFF;
...这是输出:
fputc(value & 0xFF, fd);
fputc((value >> 8) & 0xFF, fd);
对于大于两个字节的任何内容(即 along unsigned
或long signed
),您可能希望fwrite((char unsigned[]){ value >> 24, value >> 16, value >> 8, value }, 1, 4, fd);
或例如减少样板文件。考虑到这一点,形成预处理器宏似乎并没有滥用:
#define write(fd, ...) fwrite((char unsigned){ __VA_ARGS__ }, 1, sizeof ((char unsigned) { __VA_ARGS__ }), fd)
我想人们可能会认为这就像选择两个弊端中的一个更好:预处理器滥用或4
上面代码中的幻数,因为现在我们可以write(fd, value >> 24, value >> 16, value >> 8, value);
不用4
硬编码......但是对于初学者来说一句话:副作用可能引起头痛,所以不要在write
.
好吧,这就是我今天对这篇文章的更新……社交延迟的极客现在退出。