第一次召回 C 有 unsigned char、signed char 和 char:3 种不同的类型。 char 与 unsigned char 或 signed char 具有相同的范围。
[编辑]
OP 添加了“当我读/写二进制缓冲区时”,因此下面的部分(我的原始帖子)处理“char* 和unsigned char* 之间的区别”,并带有一个没有该 r/w 问题的示例案例.在本节中......
通过<stdio.h> 读取/写入二进制文件可以使用任何I/O 函数完成,尽管使用fread()/fwite() 更为常见。
对于面向字节的数据,所有 I/O 函数的行为都如同
字节输入函数从流中读取字符,就好像连续
调用fgetc 函数。 C17dr § 7.21.3 11
字节输出函数将字符写入流,就好像连续
调用fputc 函数。 § 7.21.3 12
让我们看看这两个。
...fgetc 函数将该字符作为unsigned char... § 7.21.7.1 2
fputc 函数写入 c 指定的字符(转换为 unsigned char)§ 7.21.7.3 2
因此,所有最低级别的 I/O 最好被认为是读/写unsigned char。
现在直接解决
什么时候我不能使用char*而需要使用unsigned char*? (OP)
通过写入,char*、unsigned char* 等指针可以在 OP 级代码中使用,而底层输出函数通过 unsigned char * 访问数据。这对 OP 的写入执行没有影响,除非 char 被编码为一个的补码/符号幅度 - 不会检测到陷阱代码。
与读取一样,底层输入函数通过unsigned char * 保存数据并且不会发生陷阱。通过int fgetc() 读取的单个字节将报告unsigned char 范围内的值,即使char 已签名。
在读/写二进制缓冲区中使用unsigned char* 与char* 的重要性不在于I/O 调用本身(这都是unsigned char * 访问),而在于设置数据之前写作和阅读后数据的解释 - 请参阅下面的memcmp()。
当我不能使用char*而需要使用unsigned char*时?
一个很好的例子是与字符串相关的代码。
虽然<string.h> 中的函数在函数参数中使用char*,但即使char 被签名,实现也好像char 是unsigned char。
对于本子条款中的所有函数,每个字符都应被解释为具有unsigned char 类型(因此每个可能的对象表示都是有效的并且具有不同的值)。 C17dr § 7.24.1 3
因此,即使char 是一个签名 char,像int strcmp(char *a, char *b) 这样的函数的执行方式就像int strcmp(unsigned char *a, unsigned char *b)。
-
当字符串的 有符号 char c 和 char d 具有不同符号的值时,这会有所不同。
例如。假设c < 0, d > 0
// 通过char * 访问,char 已签名
c unsigned char * 访问
c > d 是假的
这会导致与 strcmp() 返回的符号不同,因此会影响排序字符串。
// Incorrect code when `char` is signed.
int strcmp(const char *a, const char *b) {
while (*a == *b && *a) { a++; b++; }
return (*a > *b) - (*a < *b);
}
// Correct code when `char` is signed or unsigned, 2's complement or not
int strcmp(const char *a, const char *b) {
const char *ua = a;
const char *ub = b;
while (*ua == *ub && *ua) { ua++; ub++; }
return (*ua > *ub) - (*ua < *ub);
}
[编辑]
同样适用于读取二进制数据并与memcmp()比较。
- 在不使用 2 补码的旧 C 实现中,可能有 2 个零:+0 和 -0(或陷阱)。
+0 在正确查看为unsigned char 时结束了一个字符串。 -0 不是终止字符串的 null 字符,即使作为 signed char 它的值为零。
// Incorrect code when `char` is signed and not 2's complement.
// Conversion to `unsigned char` done too late.
int strcmp(const char *a, const char *b) {
while ((unsigned char)*a == (unsigned char)*b && (unsigned char)*a) { a++; b++; }
return ((unsigned char)*a > (unsigned char)*b) - ((unsigned char)*a < (unsigned char)*b);
}