char* 和 unsigned char* 有什么区别?

5
在使用C语言编写Linux程序时,我不理解在读写二进制缓冲区时 char *unsigned char * 之间的区别是什么?
何时不能使用 char* 并需要使用 unsigned char*

无符号数不会有负数。 - Cid
1
这回答了你的问题吗?char和unsigned char有什么区别? - Alex Lop.
1
如果 char 是有符号还是无符号的,这取决于具体实现。而 unsigned char 总是无符号的。 - Some programmer dude
@Some programmer dude 我编辑了这篇文章,我正在使用C语言读写二进制缓冲区。 - parser1234
1
例如,如果您需要比较字节并认为0xff大于0x01,则应使用“unsigned char *”。但是,如果您认为0xff小于0x01,则应使用“signed char *”。 - Joël Hecht
1个回答

3
首先需要注意的是,C语言中有三种不同的字符类型:unsigned char、signed char和char。其中,char类型的范围与unsigned char或signed char相同。
编辑部分补充了“当我读写二进制缓冲区时”的问题。因此,下面的部分(原始帖子)将在没有读写关注的情况下介绍“char *”和“unsigned char *”之间的区别,并提供一个示例用例。在该部分中...
可以使用任何I/O函数读写基于字节的数据,尽管使用fread()/fwrite()更为常见。
对于基于字节的数据,所有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)
在写入时,OP级别代码可以使用char *、unsigned char *或其他指针,但底层的输出函数通过unsigned char *访问数据。这对OP的写入执行没有任何影响,除非char被编码为1的补码/符号数 - 陷阱代码不会被检测到。
同样地,在读取时,底层输入函数保存数据通过unsigned char *,并且没有陷阱发生。即使char是signed,通过int fgetc()读取的单个字节也会报告unsigned char范围内的值。
在读写二进制缓冲区时,使用unsigned char *char *相比的重要性并不在于I/O调用本身(它们都可以访问unsigned char *),而在于编写数据和读取数据之前的数据设置和解释 - 请参见下面的memcmp()

什么情况下不能使用char *而需要使用unsigned char *

一个很好的例子是字符串相关的代码。

尽管<string.h>中的函数在函数参数中使用char *,但实现会像charunsigned char一样执行,即使char是有符号的。

对于本子句中的所有函数,每个字符都应解释为如果其类型为unsigned char(因此每种可能的对象表示都是有效的,并且具有不同的值)。C17dr §7.24.1 3

因此,即使char有符号的,像int strcmp(char *a, char *b)这样的函数也会像执行int strcmp(unsigned char *a,unsigned char *b)一样。

  1. char cchar d通过不同符号的值不同时,这将产生差异。
    例如,假设c < 0,d > 0

    // 通过char *访问 c < d为true // 通过unsigned char *访问 c > d为false

这导致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()进行比较。

  1. 在旧的C实现中,未使用二进制补码的情况下,可能存在两个零:+0和-0(或陷阱)。

+0被正确地视为unsigned char时,结束了一个字符串。-0不是用于终止字符串的null character,即使作为一个signedchar它的值为零。

// 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);
}

嗯?问:“我不明白区别在哪里...”,答:“1)这会有所不同,当...”似乎存在几乎完全的相关性。 - David C. Rankin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接