我知道char
被允许签名或无符号取决于实现。如果我想做的只是操作字节,这并不真正困扰我。(实际上,我不认为char
数据类型是一个字符,而是一个字节(。
但是,如果我理解的话,字符串文字是 signed char
s(实际上它们不是,但请参阅下面的更新(,函数 fgetc(( 返回unsigned char
s转换为int
。因此,如果我想操作字符,使用有符号、无符号或模糊字符是首选样式吗?为什么从文件中读取字符的约定与文本的约定不同?
我问是因为我在 c 中有一些代码可以在字符串文字和文件内容之间进行字符串比较,但是signed char *
与unsigned char *
可能真的会使我的代码容易出错。
更新 1
好的,正如一些人指出的那样(在答案和评论中(,字符串文字实际上是数组char
,而不是数组signed char
。这意味着我真的应该将char *
用于字符串文字,而不是考虑它们是有符号的还是无符号的。这让我非常高兴(直到我不得不开始与无符号字符进行转换/比较(。
但是,重要的问题仍然存在,如何从文件中读取字符,并将其与字符串文本进行比较。其关键是从使用 fgetc(( 显式读取文件中unsigned char
的int
转换为允许有符号或无符号的char
类型。
请允许我提供一个更详细的例子。
int main(void)
{
FILE *someFile = fopen("ThePathToSomeRealFile.html", "r");
assert(someFile);
char substringFromFile[25];
memset((void*)substringFromFile,0,sizeof(substringFromFile));
//Alright, the real example is to read the first few characters from the file
//And then compare them to the string I expect
const char *expectedString = "<!DOCTYPE";
for( int counter = 0; counter < sizeof(expectedString)/sizeof(*expectedString); ++counter )
{
//Read it as an integer, because the function returns an `int`
const int oneCharacter = fgetc(someFile);
if( ferror(someFile) )
return EXIT_FAILURE;
if( int == EOF || feof(someFile) )
break;
assert(counter < sizeof(substringFromFile)/sizeof(*substringFromFile));
//HERE IS THE PROBLEM:
//I know the data contained in oneCharacter must be an unsigned char
//Therefore, this is valid
const unsigned char uChar = (const unsigned char)oneCharacter;
//But then how do I assign it to the char?
substringFromFile[counter] = (char)oneCharacter;
}
//and ultimately here's my goal
int headerIsCorrect = strncmp(substringFromFile, expectedString, 9);
if(headerIsCorrect != 0)
return EXIT_SUCCESS;
//else
return EXIT_FAILURE;
}
从本质上讲,我知道我的 fgetc(( 函数返回的东西(经过一些错误检查(可以作为unsigned char
编码。我知道char
可能是也可能不是unsigned char
.这意味着,根据c标准的实施情况,对char
进行转换将不涉及重新解释。但是,在系统使用有符号char
实现的情况下,我不得不担心可以由unsigned char
编码的值不能通过char
编码(即(INT8_MAX UINT8_MAX]之间的那些值(。
博士
问题是,我应该 (1( 复制 fgetc(( 读取的基础数据(通过强制转换指针 - 别担心,我知道该怎么做(,还是 (2( 从 unsigned char
向下铸造到 char
(这只有在我知道值不能超过 INT8_MAX 的情况下才是安全的,或者这些值可以出于任何原因被忽略(?
历史原因是(正如我被告知的那样,我没有参考资料(char
类型从一开始就没有指定。
一些实现使用"一致的整数类型",其中char
、short
、int
等都是默认签名的。这是有道理的,因为它使类型彼此一致。
其他实现使用 unsigned for 字符,因为从未存在任何具有负索引的符号表(这将是愚蠢的(,并且因为他们认为需要超过 128 个字符(这是一个非常有效的问题(。
当 C 语言正确标准化时,改变这一点为时已晚,市场上已经有太多不同的编译器和为它们编写的程序。因此,出于向后兼容性的原因,char
的签名性是实现定义的。
如果您只使用它来存储字符/字符串,则char
的符号性无关紧要。仅当您决定在算术表达式中涉及char
类型或使用它来存储整数值时,这才重要 - 这是一个非常糟糕的主意。
- 对于字符/字符串,请始终使用
char
(或wchar_t(。 - 对于任何其他形式的 1 字节大数据,请始终使用
uint8_t
或int8_t
。
但是,如果我理解,字符串文字是有符号的字符
不,字符串文字是数组char
。
函数 fgetc(( 返回转换为 int 的无符号字符
不,它返回转换为int
的char
。之所以int
,是因为返回类型可能包含 EOF
,这是一个整数常量而不是字符常量。
拥有有符号的字符 * 与无符号的字符 * 可能真的会使我的代码容易出错。
不,不是真的。从形式上讲,标准中的此规则适用:
指向对象类型的指针可以转换为指向其他对象类型的指针。如果 生成的指针未正确对齐引用的类型,行为未定义。否则,当再次转换回来时,结果应与原始指针相等。
不存在从指针到有符号字符转换为指向无符号字符的指针的情况,反之亦然,会导致任何对齐问题或其他问题。
我知道根据实现,允许对字符进行签名或未签名。如果我想做的只是操作字节,这并不真正困扰我。
如果您要进行比较或将char
分配给其他整数类型,它应该会打扰您。
但是,如果我理解,字符串文字是有符号的字符
它们的类型是 char[]
,所以如果 char
=== unsigned char
,则所有字符串文字都是unsigned char[]
的。
函数 fgetc(( 返回转换为 int 的无符号字符。
这是正确的,并且需要省略不需要的符号扩展。
因此,如果我想操作字符,使用有符号、无符号或模糊字符是首选样式吗?
为了可移植性,我建议遵循各种libc实现所适应的做法:使用char
,但在处理转换为unsigned char
(char*
到unsigned char*
(之前。这样,隐式整数提升不会将范围0x80
中的字符0xff
转换为更广泛类型的负数。
简而言之:(signed char)a < (signed char)b
并不总是等同于(unsigned char)a < (unsigned char)b
。下面是一个示例。
为什么从文件中读取字符的约定与文本的约定不同?
getc()
需要一种方法来返回EOF
这样它就不会与任何真正的char
混淆。