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