从 Char 到 Int 的转换是否总是在 C 中给出正值

Does casting from Char to Int always give positive values in C

提问人:murage kibicho 提问时间:6/9/2023 更新时间:6/9/2023 访问量:93

问:

我正在编写生产就绪的 C,我需要非常快速地找到 char 数组中字符的频率。我正在尝试删除断言调用以在强制转换期间检查正值。我的断言是多余的代码还是必要的?

    char input[] = "Hello World";
    int inputLength = sizeof(input)/ sizeof(char);
    int *frequencies = calloc(256, sizeof(int));
    for(int i = 0; i < inputLength-1; i++)
    {
        int value = (int) input[i];
        assert(value > -1);//Is this line redundant?
        frequencies[value] += 1;
    }
    printf("(%d)", inputLength);
    PrintFrequencies(frequencies);
    free(frequencies);
转换 字符 C 字符串

评论

2赞 Weather Vane 6/9/2023
强制转换不是必需的,如果已签名,则需要断言。char
1赞 Some programmer dude 6/9/2023
请注意,指定为始终等于 。另请注意,数组的大小将包括 null 终止符。并且运算符的结果是一个 类型的值,您需要打印格式。sizeof(char)1inputsizeofsize_t%zu
1赞 Weather Vane 6/9/2023
也许你需要,然后没有断言。int value = (unsigned char) input[i];
2赞 Some programmer dude 6/9/2023
对于像这样的小数,为什么不简单地创建一个普通数组呢?喜欢?请注意,我使用该类型是因为数组中不能有负值。256unsigned frequencies[256] = { 0 };unsigned
1赞 Andrew Henle 6/9/2023
@WeatherVane 使用如果使用 defined 编译时完全消失的运行时是解决此问题的可怕方法。是否签名在编译时是已知的。哎呀,就用.assertNDEBUGcharunsigned char

答:

4赞 Lundin 6/9/2023 #1

从形式上讲,不要求标准 C 编译器支持的字符集的值具有任何特定值,无论是正值还是负值。

类型可以按 signed 或 unsigned:默认情况下是 char signed 还是 unsigned?。在它被签名并实现一些“扩展字符集”的情况下(例如,超越经典的“7 位 ASCII”),那么字符串理论上可以包含负值。char

因此,根据您需要编码的可移植性,断言可能会有一席之地。但是,如注释中所述,转换为无符号类型会消除该问题。请考虑改用它:

uint8_t value = input[i];

现在保证在 0 - 255 的范围内,并且代码是可移植的。value

6赞 John Bollinger 6/9/2023 #2

从 Char 到 Int 的转换是否总是在 C 中给出正值

一般来说,不可以。 可以是有符号类型,也可以是无符号类型,由 C 实现自行决定,但很多时候它是有符号类型。char

表示基本执行字符集成员的所有值都保证为非负数。这包括大写和小写拉丁字母、十进制数字、各种标点符号、空格字符和一些控制字符。但是,表示其他字符的值可能是负数。此外,构成多字节字符表示的多个值可以包括一些被视为单个字符的负值。charcharcharchar

我正在编写生产就绪的 C,我需要非常快速地找到 char 数组中字符的频率。我正在尝试删除断言调用以在强制转换期间检查正值。我的断言是多余的代码还是必要的?

你的语义错了。如果你正在阅读任意文本,并且希望你的程序是健壮的,那么你确实需要为带有负值的 s 做好准备。但assert()char

  1. assert离子是这项工作的错误工具。断言用于检查程序假定的不变量是否确实成立。例如,如果您(认为您)可以保证值始终为非负数,则可以使用断言。如果断言失败,则意味着您的代码是错误的。char

    切勿使用断言来验证输入数据或执行程序所依赖的任何其他测试,因为根据编译程序的方式,断言表达式可能根本不会被计算。

  2. 如果遇到负值,程序最好处理负值,而不是失败。在这方面,请注意,将 your 显式转换为 .您可以在任何需要整数的地方直接使用 a。另一方面,投射到 可能是有意义的,因为这将是便宜的 - 可能是免费的,即使已经签名 - 并且它将解决您的签名问题。charcharintcharunsigned charchar

评论

0赞 murage kibicho 6/10/2023
哈哈,你是发明布林带的人吗?这太疯狂了
0赞 John Bollinger 6/10/2023
不,@muragekibicho,那是别人。我们可能关系很远,但我无法告诉你是怎么回事。不好意思。
1赞 Vlad from Moscow 6/9/2023 #3

对于初学者来说,这句话

int inputLength = sizeof(input)/ sizeof(char);

是多余的。由于数组包含一个字符串,因此在 for 循环中,您可以检查当前字符是否为终止零字符 '。\0'

还要注意,通常字符数组可以比数组中存储的字符串大得多。因此,使用这个变量,以这种方式计算通常可能是错误的。如果需要存储字符串的长度,则使用标准 C 函数会更正确。 . 使用此铸件strlen

int value = (int) input[i];

实际上相当于

int value = input[i];

由于整数提升。

该类型可以表现为有符号类型或无符号类型。要使代码独立于类型的行为,您需要将每个字符强制转换为类型。charsigned charunsigned charcharunsigned char

所以这个断言

assert(value > -1);

没有用。

具有 256 个该类型元素的数组不是很大。因此,您可以使用自动存储持续时间来定义它。此外,在数组声明中使用类型而不是有符号类型在逻辑上是一致的。intunsigned intint

您的代码片段可能如下所示

char input[] = "Hello World";

unsigned int frequencies[256] = { 0 };

for ( const char *p = input; *p != '\0'; ++p )
{
    ++frequencies[( unsigned char )*p];
}

PrintFrequencies(frequencies);