我可以将unsigned char转换为char,反之亦然吗?

use*_*453 56 c c++

我想使用一个期望这样的数据的函数:

void process(char *data_in, int data_len);
Run Code Online (Sandbox Code Playgroud)

所以它只是处理一些字节.

但是当谈到原始字节时,我更习惯使用"unsigned char"(它在某种程度上"感觉"更适合处理正0到255的值),所以我的问题是:

我可以随时安全地传入unsigned char *此功能吗?

换一种说法:

  • 是否保证我可以随意地在char和unsigned char之间转换(强制转换),而不会丢失任何信息?
  • 我可以安全地转换(强制转换)指向char和unsigned char的指针,而不会丢失任何信息吗?

额外奖励:C和C++的答案是否相同?

jog*_*pan 89

简短的回答是肯定的,如果你使用一个明确的演员,但要详细解释,有三个方面需要注意:

1)转换的合法性通常可以在任一方向和(对于某种类型)
之间转换,因为源类型可以首先转换为(这是标准转换,§4.10),并且可以使用转换为目标类型明确的(§5.2.9/ 13):signed T*unsigned T*Tvoid *void *static_cast

static_cast<unsigned char*>(static_cast<void *>(data_in))
Run Code Online (Sandbox Code Playgroud)

这可以缩写为(§5.2.10/ 7)

reinterpret_cast<unsigned char *>(data_in)
Run Code Online (Sandbox Code Playgroud)

因为char是标准布局类型(§3.9.1/ 7,8和§3.9/ 9),签名不会改变对齐方式(§3.9.1/ 1).它也可以写成C风格的演员:

(unsigned char *)(data_in)
Run Code Online (Sandbox Code Playgroud)

同样,这有两种方式,从后unsigned*signed*后.还可以保证,如果以一种方式然后再应用此过程,则指针值(即它指向的地址)将不会更改(§5.2.10/ 7).

所有这些不仅适用于signed char *和之间的转换unsigned char *,也适用于char */ unsigned char *char */ signed char *.(char,signed char并且unsigned char在形式上是三种不同类型的,§3.9.1/ 1.)

要清楚,你使用的三种演员方法中的哪一种并不重要,但你必须使用一种.仅仅传递指针将不起作用,因为转换虽然合法,但不是标准转换,因此不会隐式执行(如果您尝试,编译器将发出错误).

2)对值的访问定义良好
如果在函数内部取消引用指针,即执行*data_in以检索基础字符的glvalue,会发生什么情况; 这个定义明确且合法吗?这里的相关规则是严格别名规则(§3.10/ 10):

如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:

  • [...]
  • 与对象的动态类型对应的有符号或无符号类型的类型,
  • [...]
  • a charunsigned char类型.

因此,此规则不允许访问signed char(或char)通过unsigned char*(或char),反之亦然 - 您应该能够毫无问题地执行此操作.

3)结果值
在对类型转换指针进行反省略后,您是否能够使用您获得的值?重要的是要记住,上述指针的转换和解除引用相当于重新解释(不改变!)存储在字符地址处的位模式.那么当有符号字符的位模式被解释为无符号字符的位模式(反之亦然)时会发生什么?

当从无符号到有符号时,典型的效果是0到128之间的值没有任何反应,而128以上的值变为负数.反之亦然:从有符号转为无符号时,负值将显示为大于128的值.

但标准实际上并未保证这种行为.唯一的标准保证的是,所有这三种类型,char,unsigned charsigned char,被用于值表示所有位(不一定是8,顺便说一句).因此,如果您将其中一个解释为另一个,制作一些副本然后将其存储回原始位置,您可以确定不会丢失任何信息(如您所示),但您不一定知道这些值是什么实际上是指(至少不是完全可移植的方式).

  • 对于 2) 和 3) 事情有点复杂,至少对于 C。两种类型的表示都不能有填充位,这是正确的。但是签名类型(`signed char` 和最终`char`,如果它是有符号的)可能有一个“陷阱”表示。这将是对应于“负零”的位模式,如果它是实现定义的,如果这是这些类型的有效值。例如,常量`SCHAR_MIN` 可能只是`127` 而不是`128`。不过,我不知道任何真正的现有架构具有此功能。 (2认同)

Mit*_*eat 17

unsigned charsigned char只是解释:没有转换发生.

由于您正在处理字节,以显示意图,因此最好将其声明为

void process(unsigned char *data_in, int data_len);
Run Code Online (Sandbox Code Playgroud)

[正如编辑所说:平原char可以是有符号或无符号类型.C和C++标准明确地允许或者(它始终是一个单独的类型从任一unsigned charsigned char,但具有相同的范围作为其中之一)]

  • @ user2015453如果无法更改定义,请勿更改声明. (3认同)

sis*_*aty 5

是的,您始终可以毫无问题地将字符转换为无符号字符,反之亦然。如果您运行以下代码,并将其与 ASCII 表(参考http://www.asciitable.com/)进行比较,您可以自己看到一个证明,以及 C/C++ 如何处理转换 - 他们处理完全一样:

#include "stdio.h"


int main(void) {
    //converting from char to unsigned char
    char c = 0;
    printf("%d byte(s)\n", sizeof(char));  // result: 1byte, i.e. 8bits, so there are 2^8=256 values that a char can store.
    for (int i=0; i<256; i++){
        printf("int value: %d - from: %c\tto: %c\n", c,  c, (unsigned char) c);
        c++;
    }

    //converting from unsigned char to char
    unsigned char uc = 0;
    printf("\n%d byte(s)\n", sizeof(unsigned char));
    for (int i=0; i<256; i++){
        printf("int value: %d - from: %c\tto: %c\n", uc, uc, (char) uc);
        uc++;
    }
}
Run Code Online (Sandbox Code Playgroud)

我不会发布输出,因为它有太多行!在输出中可以注意到,在每个部分的前半部分,即从 i=0:127 开始,从字符到无符号字符的转换工作良好,反之亦然,没有任何修改或丢失。

但是,从 i=128:255 开始,chars 和 unsigned chars 不能被强制转换,否则你会有不同的输出,因为 unsigned char 保存了 [0:256] 中的值,char 保存了区间 [-128:127] 中的值])。尽管如此,这第二部分的行为是无关紧要的,因为在 C/C++ 中,一般来说,你只能使用字符/无符号字符作为 ASCII 字符,它们只能取 128 个不同的值和其他 128 个值(字符为正或负对于无符号字符)从不使用。

如果您从不将值放入不代表字符的字符中,也从不将值放入不代表字符的无符号字符中,那么一切都会好起来的!

额外:即使您在 C/C++ 的字符串中使用 UTF-8 或其他编码(用于特殊字符),使用这种类型转换的所有内容都可以,例如,使用 UTF-8 编码(参考。http:// lwp.interglacial.com/appf_01.htm):

char hearts[]   = {0xe2, 0x99, 0xa5, 0x00};
char diamonds[] = {0xe2, 0x99, 0xa6, 0x00};
char clubs[]    = {0xe2, 0x99, 0xa3, 0x00};
char spades[]   = {0xe2, 0x99, 0xa0, 0x00};
printf("hearts (%s)\ndiamonds (%s)\nclubs (%s)\nspades (%s)\n\n", hearts, diamonds, clubs, spades);
Run Code Online (Sandbox Code Playgroud)

该代码的输出将是:
红心(?)
钻石(?)
俱乐部(?)
黑桃(?)

即使您将其每个字符转换为无符号字符。

所以:

  • “我可以总是安全地将一个无符号字符 * 传递给这个函数吗?” 是的!

  • “是否保证我可以随意在 char 和 unsigned char 之间安全地转换(转换),而不会丢失任何信息?” 是的!

  • “我可以随意在指向 char 和 unsigned char 的指针之间安全地转换(强制转换),而不会丢失任何信息吗?” 是的!

  • “C 和 C++ 中的答案是否相同?” 是的!