这段代码是怎么回事?
What is going on in this code?
有人可以为我解释一下代码吗?
为什么是 32 位 UINT
?
为什么 0xff
?
这里的 4 个右移是做什么的?
int writeUINT32little(FILE *f, UINT32 i)
{
int rc;
rc = fputc((i & 0xff), f);
if (rc == EOF) return rc;
rc = fputc(((i >> 8) & 0xff), f);
if (rc == EOF) return rc;
rc = fputc(((i >> 16) & 0xff), f);
if (rc == EOF) return rc;
return fputc(((i >> 24) & 0xff), f);
}
这需要一个 32 位无符号整数并将其逐字节输出到文件,最低有效字节在前(即小端)。
0xff
是十六进制的写法,十进制写255
,二进制写11111111
。 &
是按位 and
函数。放在一起,(i & 0xff)
屏蔽掉整数的最低有效字节。
然后移位将整个内容移位超过 8 位,并重复该过程以打印下一个字节。
这个函数据推测所做的是将一个 32 位无符号整数写入小端格式的文件流。
它真正做的是,它不是做一个简单的小任务并把它做对,而是试图同时做两件事,但都做错了。
在深入探讨为什么这是错误的之前,我将快速回答问题本身:
- 为什么
UINT32
?不知道。它是非标准的,最好用 uint32_t
. 代替
- 为什么
0xff
?用作将低 8 位保留在较大变量(32 位)中的掩码
- 4个右移在这里做什么?移位用于将原始 UINT 中的每个字节以正确的 "little-endian" 顺序移至最右边的位。
现在,将其排除在外...
为什么这是错误的?
char
保证是 at least 8 bits wide,但 可以 更大。这意味着 fputc
每次调用可能会写入 8 位,但它可能会写入更多。这意味着在 char 为(例如)16 位的特定体系结构上,该函数每次调用将写入 64 位并且它根本不是小端。此外,该函数非常仔细地检查错误,但不会向调用者公开相关的失败信息(写入了多少字节?)。
什么是更好的设计?
首先,一如既往,做一件小事,并把它做好:
uint32_t toLittleEndian(uint32_t v)
{
uint32_t result = v >> 24;
result |= v << 24;
result |= (v >> 8) & 0xf0;
result |= (v << 8) & 0xf00;
return result;
}
注意:此实现故意冗长。有一个使用位旋转操作的优化机会,但 C 对此没有原生支持,需要汇编代码
此函数仅将大端32位值转换为其等效的小端表示(实际上它在两者之间交替并且可以命名为switchEndian
) .
接下来,我们需要将值写入文件:
int write(uint32_t v, FILE *f)
{
return fwrite(&v, sizeof(v), 1, f);
}
这将写入正好 32 位宽整数,return 实际写入的字节数。此外,它是可重复使用的。
接下来,如果需要,我们可以创建一个方便的复合函数:
int writeLittleEndian(uint32_t v, FILE *f)
{
return write(toLittleEndian(v), f);
}
有人可以为我解释一下代码吗?
为什么是 32 位 UINT
?
为什么 0xff
?
这里的 4 个右移是做什么的?
int writeUINT32little(FILE *f, UINT32 i)
{
int rc;
rc = fputc((i & 0xff), f);
if (rc == EOF) return rc;
rc = fputc(((i >> 8) & 0xff), f);
if (rc == EOF) return rc;
rc = fputc(((i >> 16) & 0xff), f);
if (rc == EOF) return rc;
return fputc(((i >> 24) & 0xff), f);
}
这需要一个 32 位无符号整数并将其逐字节输出到文件,最低有效字节在前(即小端)。
0xff
是十六进制的写法,十进制写255
,二进制写11111111
。 &
是按位 and
函数。放在一起,(i & 0xff)
屏蔽掉整数的最低有效字节。
然后移位将整个内容移位超过 8 位,并重复该过程以打印下一个字节。
这个函数据推测所做的是将一个 32 位无符号整数写入小端格式的文件流。
它真正做的是,它不是做一个简单的小任务并把它做对,而是试图同时做两件事,但都做错了。
在深入探讨为什么这是错误的之前,我将快速回答问题本身:
- 为什么
UINT32
?不知道。它是非标准的,最好用uint32_t
. 代替
- 为什么
0xff
?用作将低 8 位保留在较大变量(32 位)中的掩码 - 4个右移在这里做什么?移位用于将原始 UINT 中的每个字节以正确的 "little-endian" 顺序移至最右边的位。
现在,将其排除在外...
为什么这是错误的?
char
保证是 at least 8 bits wide,但 可以 更大。这意味着 fputc
每次调用可能会写入 8 位,但它可能会写入更多。这意味着在 char 为(例如)16 位的特定体系结构上,该函数每次调用将写入 64 位并且它根本不是小端。此外,该函数非常仔细地检查错误,但不会向调用者公开相关的失败信息(写入了多少字节?)。
什么是更好的设计?
首先,一如既往,做一件小事,并把它做好:
uint32_t toLittleEndian(uint32_t v)
{
uint32_t result = v >> 24;
result |= v << 24;
result |= (v >> 8) & 0xf0;
result |= (v << 8) & 0xf00;
return result;
}
注意:此实现故意冗长。有一个使用位旋转操作的优化机会,但 C 对此没有原生支持,需要汇编代码
此函数仅将大端32位值转换为其等效的小端表示(实际上它在两者之间交替并且可以命名为switchEndian
) .
接下来,我们需要将值写入文件:
int write(uint32_t v, FILE *f)
{
return fwrite(&v, sizeof(v), 1, f);
}
这将写入正好 32 位宽整数,return 实际写入的字节数。此外,它是可重复使用的。
接下来,如果需要,我们可以创建一个方便的复合函数:
int writeLittleEndian(uint32_t v, FILE *f)
{
return write(toLittleEndian(v), f);
}