12

在 C++ 中处理字节时,我经常使用使用 char 的库。另一种方法是将“字节”定义为无符号字符,但这不是他们决定使用的标准。我经常将字节从 C# 传递到 C++ dll 并将它们转换为 char 以使用该库。

当将 int 转换为 chars 或将 chars 转换为其他简单类型时,可能会出现哪些副作用。具体来说,您何时处理过这个损坏的代码,您是如何发现它是因为 char 签名的?

幸运的是我没有在我的代码中遇到这个,在学校的嵌入式系统课上使用了一个 char 签名的转换技巧。我希望更好地理解这个问题,因为我觉得它与我正在做的工作相关。

4

8 回答 8

4

一个主要风险是您是否需要移动字节。有符号的字符在右移时会保留符号位,而无符号的字符则不会。这是一个小测试程序:

#include <stdio.h>

int main (void)
{
    signed char a = -1;
    unsigned char b = 255;

    printf("%d\n%d\n", a >> 1, b >> 1);

    return 0;
}

它应该打印 -1 和 127,即使 a 和 b 以相同的位模式开始(给定 8 位字符、二进制补码和使用算术移位的有符号值)。

简而言之,您不能依赖轮班对有符号和无符号字符进行相同的工作,因此如果您需要可移植性,请使用unsigned char而不是charor signed char

于 2010-02-03T15:21:31.190 回答
2

char当您在实现协议或编码方案时需要将 a 的数值与十六进制常量进行比较时,就会出现最明显的问题。

例如,在实现 telnet 时,您可能想要这样做。

// Check for IAC (hex FF) byte
if (ch == 0xFF)
{
    // ...

或者在测试 UTF-8 多字节序列时。

if (ch >= 0x80)
{
    // ...

幸运的是,这些错误通常不会持续很长时间,因为即使是在带有签名的平台上进行的最粗略的测试也会char发现它们。它们可以通过使用字符常量来修复,将数字常量转换为 acharunsigned char在比较运算符将两者提升为 之前将字符转换为int。但是,将其char直接转换为 an 是unsigned行不通的。

if (ch == '\xff')               // OK

if ((unsigned char)ch == 0xff)  // OK, so long as char has 8-bits

if (ch == (char)0xff)           // Usually OK, relies on implementation defined behaviour

if ((unsigned)ch == 0xff)       // still wrong
于 2010-02-03T20:20:27.537 回答
1

最让我恼火的一个:

typedef char byte;

byte b = 12;

cout << b << endl;

当然是化妆品,但是...

于 2010-02-03T15:12:27.653 回答
1

在编写使用文本中的字符作为状态树索引的搜索算法时,我一直被 char 签名所困扰。在将字符扩展为更大的类型时,我也遇到了问题,并且符号位传播导致其他地方出现问题。

我发现当我开始得到奇怪的结果时,以及由于搜索文本而不是我在最初开发期间使用的文本而产生的段错误(显然,值 >127 或 <0 的字符会导致这种情况,并且不一定是存在于典型的文本文件中。

使用变量时,请始终检查变量的符号。通常,除非我有充分的理由,否则现在我将类型签名,必要时进行强制转换。这非常适合在库中普遍使用char来简单地表示一个字节。请记住,char没有定义的签名(与其他类型不同),您应该对其进行特殊处理,并注意。

于 2010-02-03T15:16:38.863 回答
0

将整数转换为字符或将字符转换为其他简单类型时

关键点是,将有符号值从一种原始类型转换为另一种(更大)类型不会保留位模式(假设二进制补码)。带有位模式的有符号字符0xff是 -1,而带有十进制值 -1 的有符号短字符是0xffff. 但是,将具有值的 unsigned char0xff转换为 unsigned short 会产生0x00ff. 因此,在类型转换为更大或更小的数据类型之前,请始终考虑正确的符号。如果您不需要,切勿在签名数据类型中携带未签名数据- 如果外部库强制您这样做,请尽可能晚地进行转换(或者如果外部代码充当数据源,则尽可能早地进行转换)。

于 2010-02-03T15:24:15.643 回答
0

在为多个平台编译时,您将惨遭失败,因为 C++ 标准没有定义char为具有某种“签名”。

因此 GCC 引入了强制某些行为的选项-fsigned-char。例如,-funsigned-char可以在此处找到有关该主题的更多信息。

编辑:

正如您询问损坏代码的示例一样,有很多可能会破坏处理二进制数据的代码。例如,您处理 8 位音频样本(范围 -128 到 127)的图像,并且您希望将音量减半。现在想象这个场景(天真的程序员假设char == signed char):

char sampleIn;

// If the sample is -1 (= almost silent), and the compiler treats char as unsigned,
// then the value of 'sampleIn' will be 255
read_one_byte_sample(&sampleIn);

// Ok, halven the volume. The value will be 127!
char sampleOut = sampleOut / 2;

// And write the processed sample to the output file, for example.
// (unsigned char)127 has the exact same bit pattern as (signed char)127,
// so this will write a sample with the loudest volume!!
write_one_byte_sample_to_output_file(&sampleOut);

我希望你喜欢这个例子 ;-) 但老实说,我从来没有真正遇到过这样的问题,即使是在我记忆中的初学者......

希望这个答案足以满足您的反对意见。简短的评论呢?

于 2010-02-03T15:37:38.923 回答
0

C 和 C++ 语言规范定义了 3 种用于保存字符的数据类型 charsigned charunsigned char. 后两者已在其他答案中进行了讨论。让我们看看char类型。

标准说char数据类型可以是有符号无符号的,并且是一个实现决定。这意味着某些编译器或编译器版本可以以char不同方式实现。这意味着该char数据类型不利于算术或布尔运算。对于算术和布尔运算,signed以及unsigned的版本char可以正常工作。

总之,char数据类型有3个版本。该char数据类型在保存字符方面表现良好,但不适合跨平台和翻译器的算术,因为它的符号是实现定义的。

于 2010-02-03T17:54:04.007 回答
0

标志扩展。我的 URL 编码函数的第一个版本产生了像“%FFFFFFA3”这样的字符串。

于 2010-06-12T07:10:48.783 回答