C 安全地将字符转换为整数

C 安全地将字符转换为整数,c,C,当我有char保存某个整数(比如23)并想将其转换为更大的整数(int)时, 我听说可能有一些问题,因为编译器必须决定是将char解释为signed还是unsigned? 这是真的吗?这会导致问题吗?如何避免这种情况 换句话说(我不确定下面的公式是否等同于上面的公式),这种转换会产生什么问题: char someCharVal = //... int x = someCharVal; 以及如何避免它们? ps.“傻瓜”的解释欢迎问题是,简单明了,当错误地将无符号值视为有符号值时,

当我有
char
保存某个整数(比如23)并想将其转换为更大的整数(
int
)时, 我听说可能有一些问题,因为编译器必须决定是将
char
解释为
signed
还是
unsigned
? 这是真的吗?这会导致问题吗?如何避免这种情况

换句话说(我不确定下面的公式是否等同于上面的公式),这种转换会产生什么问题:

   char someCharVal = //...
    int x = someCharVal;
以及如何避免它们?


ps.“傻瓜”的解释欢迎

问题是,简单明了,当错误地将无符号值视为有符号值时,会出现符号扩展

让我们检查8位和16位2的补码中
5
-5
的位模式:

      8-bit          16-bit
    =========  ===================
+5  0000 0101  0000 0000 0000 0101
-5  1111 1011  1111 1111 1111 1011
将数字从8位转换为16位时,顶部位向左扩展。换句话说,8位数字左边的零位将延伸到16位数字的上半部分

类似地,该顶部位中的一位将向左延伸

这就是C扩展符号数的方式(无论如何,对于两个的补码,一的补码和符号大小编码是不同的事情,但是现在很少有实现使用它们)

因此,如果要将
有符号字符
转换为
有符号整数
,或将
无符号字符
转换为
无符号整数
,则没有问题。C将给出正确的值

当您切换到或从签名类型切换到其他类型时,就会出现问题。 ,问题是底层数据的处理方式可能与您预期的不同

例如,请参见以下代码,其中包含8位
char
和32位
int
类型:

#include <stdio.h>

int main (void) {
    printf ("unsigned char  50 -> unsigned int %11u\n", (unsigned char)50);
    printf ("unsigned char -50 -> unsigned int %11u\n", (unsigned char)-50);
    printf ("unsigned char  50 ->   signed int %11d\n", (unsigned char)50);
    printf ("unsigned char -50 ->   signed int %11d\n", (unsigned char)-50);

    printf ("  signed char  50 -> unsigned int %11u\n", (  signed char)50);
    printf ("  signed char -50 -> unsigned int %11u\n", (  signed char)-50);
    printf ("  signed char  50 ->   signed int %11d\n", (  signed char)50);
    printf ("  signed char -50 ->   signed int %11d\n", (  signed char)-50);

    return 0;
}
第一个不寻常的例子是第二行。它实际上接受
有符号字符-50
位值,将其视为
无符号字符
,并将其扩展为
无符号整数
,正确保留其无符号值206

第二种情况也是如此,因为
signed int
能够容纳
unsigned char
值的全部范围(在这个实现中)

第三种异常情况是将
-50
扩展为
有符号整数
,然后将底层位模式视为
无符号整数
,从而获得较大的正值

请注意,当值的“签名性”不变时,不会出现任何问题

C标准不强制要求默认情况下
char
类型具有什么符号,它可以是有符号的,也可以是无符号的。因此,如果您想要真正的可移植代码,它不应该包含任何“裸”字符类型


如果要使用有符号值,请使用有符号值。这包括显式使用
signed char
而不是
char
。同样,如果要使用unsigned value,请在所有位置使用unsigned(包括显式使用
unsigned char
)。除非您完全知道会发生什么,否则不要从有符号字符升级为无符号字符,反之亦然。

对于
有符号字符
,int的范围始终等于或大于
有符号字符
的范围,并且从
有符号字符
转换为
int
总是安全的

对于
unsigned char
,理论上
UCHAR_MAX
可以等于
UINT_MAX
,小于
INT_MAX
;而且从
无符号字符
整数
的转换可能是不安全的。要做到这一点,
UCHAR_MAX
必须为32767或更大(这在实践中非常罕见);因此,转换几乎总是安全的

因为
char
可以是有符号的,也可以是无符号的,所以从
char
int
的转换几乎总是安全的(理论上不保证安全)

然而


以上所有内容都假设您正在使用(有符号或无符号)
char
的完整范围。这是极为罕见的。通常,如果使用的是
char
,则仅使用0到127之间的值以避免便携性问题,如果需要存储负值或更大的正值,则首先使用不同的数据类型(例如
signed char
uint8\u t
int
,等等)。如果
char
仅用于存储0到127之间的值,则无论
char\u MIN
char\u MAX
有哪些值,将
char
转换为
int
始终是安全的。

此问题的答案可能取决于系统。看看这个问题:
char
可以是有符号的,也可以是无符号的
CHAR_MAX
保证至少为
127
,因此
23
始终为正值。为了避免任何问题,在编写代码时请记住这种可能性。如果您想存储一个小的非负数,最好使用
unsigned char
。@merlin2011:链接上说
char
可能有符号或无符号取决于实现。但我认为它并没有完全回答这个问题。那么如何将
char
安全地转换为
int
?@dmcr\u code,如果“safe”表示他想保留整数的符号,那么他可能需要先检查它是否有符号,并决定将它放在哪种类型的整数中?@paxdaiblo:如果我的char持有
-23
。当我执行
(unsigned char)charVal
并将其分配给
int
时。int将保持一个正值,对吗?因此,原始(负)值已丢失。不是吗?或者我在引用一本书,我从中听说了一些事情,但有点困惑问题出在哪里:“只有当将字符量转换为更大的整数时,问题才变得重要。反过来说,结果是定义良好的:多余的位被简单地丢弃。但是,将字符转换为整数的编译器
unsigned char  50 -> unsigned int          50
unsigned char -50 -> unsigned int         206 # -50 unsigned is 256-50
unsigned char  50 ->   signed int          50
unsigned char -50 ->   signed int         206 # same as above
  signed char  50 -> unsigned int          50
  signed char -50 -> unsigned int  4294967246 # sign extend, treat as unsigned
  signed char  50 ->   signed int          50                      (2^32 - 50)
  signed char -50 ->   signed int         -50