C中将字节转换为有符号整数

4

我在C语言中有一个存储在char类型变量中的二进制值,我想将这个字节转换为带符号整数。

目前我有如下代码:

char a = 0xff;
int b = a; 
printf("value of b: %d\n", b);

标准输出结果为“255”,期望的输出结果是“-1”。

1
首先将类型转换为 signed char: int b = (signed char) a; - obataku
ff 的十六进制是 255,所以如果你想要 -1,你期望进行什么样的转换或变换? - CB Bailey
谢谢你们,oldrinb和Charles =) - xwgou
3个回答

5
根据C99标准,
6.3.1.3 有符号和无符号整数
当将整型值转换为 _Bool 以外的其他整型类型时,如果该值可由新类型表示,则保持不变。
否则,如果新类型是无符号的,则通过重复加上或减去超过新类型可以表示的最大值的一个值,直到该值在新类型范围内,来转换该值。
否则,新类型为有符号类型并且该值无法被其表示; 结果是实现定义的或引发实现定义的信号。
您需要将 char 强制转换为 signed char,然后再分配给 int,因为任何 char 可以取得的值都可以直接表示为 int。
#include <stdio.h>

int main(void) {
  char a = 0xff;
  int b = (signed char) a;
  printf("value of b: %d\n", b);
  return 0;
}

快速测试表明它在这里可以正常工作:
C:\dev\scrap>gcc -std=c99 -oprint-b print-b.c

C:\dev\scrap>print-b
value of b: -1

请注意,C99标准未定义char是被视为有符号还是无符号的。
引用如下: 6.2.5 类型 类型为 char 的对象足够大,可以存储基本执行字符集中的任何成员。如果将基本执行字符集的成员存储在 char 对象中,则其值保证为正。如果存储任何其他字符,则结果值是实现定义的,但必须在该类型可以表示的值范围内。
三种类型 charsigned charunsigned char 统称为字符类型。实现应定义 char 具有与 signed charunsigned char 相同的范围、表示和行为。

1
需要注意的是,从技术上讲,有符号整数溢出会导致未定义的行为,因此为了在不同平台上获得一致的结果,您需要自己定义环绕语义(即检查SCHAR_MAX并相应地采取措施)。 - eq-
一个虚构的编译器是否可以合法地拥有(比如说)SCHAR_MAY == 512,以便这段代码会打印出“b的值:255”? - tiwo
1
@eq 这里不是 C 语言中的溢出,而是由 C99 中的 6.3.1.3p3 规定的整数转换。因此,这里没有未定义的行为。 - ouah
@ouah,直接引用一下:“结果要么是实现定义的,要么会引发实现定义的信号”,所以目前没有未定义的行为,但它可能会在你的程序中崩溃 :) - Jens Gustedt

4

替换:

char a = 0xff

by

signed char a = 0xff;  // or more explicit: = -1

printf打印-1,你可以这样做。

如果您不想改变a的类型,可以按照@veer在评论中添加的方法,将a简单地转换为(signed char),然后再将其值分配给b

请注意,在这两种情况下,这种整数转换是实现定义的,但这是常见的实现定义行为。


2
@JensGustedt 这不是 C 定义中的溢出。这是完全有效(但实现定义的)C 代码。 - ouah
嗯,signed char 通常可以容纳从 -128127 的值。在这里,您正在将无符号值 255 分配给它。这是一种溢出。 Ouah 正确指出,然后发生的事情是实现定义的。其中可能发生的这些实现定义的事情之一是“引发信号”。 - Jens Gustedt
1
@JensGustedt 在赋值之前,255的值被转换为(signed char),这个转换受到C99中6.3.1.3p3的规定。由于转换是在赋值之前进行的,因此不会发生溢出。 - ouah
顺便说一下,C语言指出 (C99, 4.p3) 在所有其他方面正确的程序,在正确的数据操作下包含未指定的行为,应该是一个正确的程序,而根据定义实现定义行为是未指定的行为。我认为崩溃不是正确程序的可接受行为。 - ouah
2
但是标准明确提到,在转换为有符号整数类型时,如果值在目标类型中不可表示,可能会引发实现定义的信号。我认为在这种情况下,只要实现文档化了它,崩溃是允许的。 - Daniel Fischer
显示剩余5条评论

1

你从一开始就错了:

char a = 0xff;

如果charsigned,你似乎是这样假设的,在这里你已经有一个超出范围的值了,0xFF是一个无符号数量,值为255。如果你想把char看作有符号数,请使用signed char并将其赋值为-1。如果你想把它看作位模式,请使用unsigned char并将其赋值为0xFF。然后你对int的初始化将会按照你的期望执行。 charsigned charunsigned char在标准的定义中是三种不同的类型。将char本身保留给字符,打印可读的人类内容。

请注意,按定义它们是三种不同的类型,但实际上 char 要么是 unsigned char 要么是 signed char。DR#068 澄清了这一点 http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_068.html - ouah
@ouah,为了更清楚地说明:char具有与其中一种[un]signed char类型相同的值范围,但它是一个独立的类型(如C的类型系统所示),不同于任何一个。我想要表达的观点是,这三种类型在标准中给出的语义差异很大,你应该根据它们的意图使用它们。如果可以避免,不要将char用于算术运算,否则可能会在另一半可能的平台上出错。 - Jens Gustedt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接