我能安全地将unsigned char转换为char吗?
函数 getc 及其它类似函数,已知会返回一个 unsigned char,再被强制转换成 int。因此,在典型系统中的取值范围通常是0-255,加上 EOF。
如果我已经检查出 EOF,剩下的可能性是我有一个 unsigned char 被强制转换成 int,我可以安全地把它再转换回成一个 char,例如存储到字符串中吗?
我见过很多代码,简单地假设 unsigned char 可以存入 char,通常代码甚至没有显式的强制转换,而是依赖隐式转换。
然而,我理解C 标准对带符号整数的一些情况是未定义或实现定义的:
- 带符号整数溢出是未定义的(无符号溢出在模运算下是有定义的)
- 将超出范围的值强制转换为带符号整数是实现定义的,可能会触发信号(无符号转换总是定义为对最大值加一取模)
因此,我怀疑要安全地把一个 unsigned char 转换成一个 char,我需要这个函数:
static inline char uchar_to_char(unsigned char uch)
{
if (CHAR_MIN < 0 && uch > CHAR_MAX)
{
return (char)(((int)uch) - (((int)UCHAR_MAX)+1));
}
return (char)uch;
}
...在大多数平台上,优化的编译器应该把它变成一个空操作函数,只是用不同的数据类型返回你给它的同一个值。
但是,真的是这样吗?我真的需要那个函数吗?还是 char 是一个特殊情况,转换会以不同的方式工作?我对基于C99甚至更新的标准的答案感兴趣。我也对ISO C + POSIX的组合感兴趣,因此如果POSIX增加了ISO C没有的内容,可能会使简单的强制转换成为允许的。
我只对那些 char 恰好是 signed char 的系统感兴趣。我知道可以把 char 定义为 unsigned char。
相关:
- Can I turn unsigned char into char and vice versa?(尽管这也是针对C++的,问题主要关于
char *与unsigned char *之间的转换,这与char与unsigned char之间的转换不同,尽管它也涉及非指针的情况)
解决方案
对于给定的示例,假设 CHAR_MAX == 127
C89:
§6.2.1.2 Signed and unsigned integers
当一个带整型的值被降级为较小位数的带符号整数,或无符号整数被转换为其相应的带符号整数时,如果该值不能被新类型表示,结果将是实现定义的。
(char) (int) 128 和 (char) (unsigned char) 128 将是实现定义的。
C99/C11/C17/C23:
§6.3.1.3 Signed and unsigned integers
当一个整型值被转换为除 _Bool之外的另一种整型时, 如果该值可以被新类型表示,则保持不变。
否则,如果新类型是无符号的,...。
否则,新类型是带符号的且该值不能在其中表示;结果要么是实现定义的,要么会引发一个实现定义的信号。
(char) (int) 128 与 (char) (unsigned char) 128 将是实现定义的,或会引发一个信号。
当前的C2y草案的措辞相同,只是 _Bool -> bool。
对于 char/signed char/unsigned char 没有特别的措辞。
是的,你确实需要在最大可移植性方面进行特殊处理。然而,在大多数实现上,简单的强制转换就能达到你需要的效果。
我不认为POSIX为此定义了具体行为,仍然属于实现定义。POSIX确实要求使用二进制补码表示,因此你可以通过memcpy/联合体进行转换。