我知道根据实施情况,允许char
签名或签名。如果我想做的就是操纵字节,这并不会让我感到烦恼。 (事实上,我不认为char
数据类型是一个字符,而是一个字节。)
但是,如果我理解,字符串文字是signed char
s(实际上它们不是,但请参阅下面的更新),函数fgetc()将unsigned char
返回到{{1} }}。因此,如果我想操纵字符,使用有符号,无符号或模糊字符的首选样式是什么?为什么从文件中读取字符的惯例与文字不同?
我问,因为我在c中有一些代码在字符串文字和文件内容之间进行字符串比较,但是int
vs signed char *
可能会让我的代码容易出错。
更新1
好的,有些人指出(在答案和评论中)字符串文字实际上是unsigned char *
数组,而不是char
数组。这意味着我真的应该使用signed char
来表示字符串文字,而不是考虑它们是有符号还是无符号。这让我非常高兴(直到我必须开始使用无符号字符进行转换/比较)。
然而,重要的问题仍然是,如何从文件中读取字符,并将它们与字符串文字进行比较。其中的关键是使用fgetc()从char *
读取转换为明确从文件中读取int
到unsigned char
类型,允许签名或无符号的。
请允许我提供更详细的例子。
char
基本上,我知道我的fgetc()函数返回的东西(经过一些错误检查后)可编码为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;
}
。我知道unsigned char
可能是也可能不是char
。这意味着,根据c标准的实现,对unsigned char
执行转换将涉及 no 重新解释。但是,如果系统是使用签名char
实现的,我不得不担心char
无法编码的unsigned char
可以编码的值(即(INT8_MAX UINT8_MAX])之间的那些值。
TL;博士
问题是,如果我(1)复制他们的fgetc()读取的基础数据(通过铸造指针 - 不要担心,我知道该怎么做),或者(2)从{{1到char
(如果我知道值不能超过INT8_MAX,那么这是安全的,或者无论出于何种原因可以忽略这些值)?
答案 0 :(得分:1)
历史原因是(正如我所知,我没有参考),char
类型从一开始就没有明确规定。
某些实现使用了“一致的整数类型”,其中char
,short
,int
等都是默认签名的。这是有道理的,因为它使类型彼此一致。
其他实现使用unsigned作为字符,因为从来没有任何带有负索引的符号表(这将是愚蠢的),因为他们看到需要超过128个字符(一个非常有效的问题)。
当C正确标准化时,改变它已经太晚了,为它们编写的太多不同的编译器和程序已经在市场上销售。因此,出于向后兼容性原因,char
的签名已由实现定义。
char
的签名无关紧要,如果您只使用它来存储字符/字符串。只有当您决定将char
类型包含在算术表达式中或使用它来存储整数值时,这才是最重要的 - 这是一个非常糟糕的主意。
char
(或wchar_t)。uint8_t
或int8_t
。但是,如果我理解,字符串文字是签名字符
不,字符串文字是char
数组。
函数fgetc()返回转换为int
的无符号字符
不,它返回char
转换为int
。它是int
,因为返回类型可能包含EOF
,它是一个整数常量而不是字符常量。
使用signed char * vs unsigned char *可能会使我的代码容易出错。
不,不是真的。形式上,该标准的规则适用:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果 结果指针未正确对齐引用类型,行为未定义。否则,当再次转换回来时,结果将与原始指针进行比较。
不存在从指向signed char到指向unsigned char的指针,反之亦然,会导致任何对齐问题或其他问题。
答案 1 :(得分:1)
我知道根据实现情况允许签名或取消签名。如果我想要做的就是操纵字节,这并不会让我感到烦恼。
如果您要进行比较或将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
。这是一个example。
为什么从文件中读取字符的惯例与文字不同?
getc()
需要一种方法来返回EOF
,以便不会与任何真实的char
混淆。