在 C 中表示字符的最佳方法是什么?



我知道char被允许签名或无符号取决于实现。如果我想做的只是操作字节,这并不真正困扰我。(实际上,我不认为char数据类型是一个字符,而是一个字节(。

但是,如果我理解的话,字符串文字是 signed char s(实际上它们不是,但请参阅下面的更新(,函数 fgetc(( 返回unsigned char s转换为int。因此,如果我想操作字符,使用有符号、无符号或模糊字符是首选样式吗?为什么从文件中读取字符的约定与文本的约定不同?

我问是因为我在 c 中有一些代码可以在字符串文字和文件内容之间进行字符串比较,但是signed char *unsigned char *可能真的会使我的代码容易出错。

更新 1

好的,正如一些人指出的那样(在答案和评论中(,字符串文字实际上是数组char,而不是数组signed char。这意味着我真的应该char *用于字符串文字,而不是考虑它们是有符号的还是无符号的。这让我非常高兴(直到我不得不开始与无符号字符进行转换/比较(。

但是,重要的问题仍然存在,如何从文件中读取字符,并将其与字符串文本进行比较。其关键是从使用 fgetc(( 显式读取文件中unsigned charint转换为允许有符号或无符号的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类型从一开始就没有指定。

一些实现使用"一致的整数类型",其中charshortint等都是默认签名的。这是有道理的,因为它使类型彼此一致。

其他实现使用 unsigned for 字符,因为从未存在任何具有负索引的符号表(这将是愚蠢的(,并且因为他们认为需要超过 128 个字符(这是一个非常有效的问题(。

当 C 语言正确标准化时,改变这一点为时已晚,市场上已经有太多不同的编译器和为它们编写的程序。因此,出于向后兼容性的原因,char的签名性是实现定义的。

如果您只使用它来存储字符/字符串,则char的符号性无关紧要。仅当您决定在算术表达式中涉及char类型或使用它来存储整数值时,这才重要 - 这是一个非常糟糕的主意。

  • 对于字符/字符串,请始终使用 char(或wchar_t(。
  • 对于任何其他形式的 1 字节大数据,请始终使用 uint8_tint8_t

但是,如果我理解,字符串文字是有符号的字符

不,字符串文字是数组char

函数 fgetc(( 返回转换为 int 的无符号字符

不,它返回转换为intchar。之所以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混淆。

相关内容

最新更新