我想使用一个期望这样的数据的函数:
void process(char *data_in, int data_len);
Run Code Online (Sandbox Code Playgroud)
所以它只是处理一些字节.
但是当谈到原始字节时,我更习惯使用"unsigned char"(它在某种程度上"感觉"更适合处理正0到255的值),所以我的问题是:
我可以随时安全地传入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
char或unsigned char类型.
因此,此规则不允许访问signed char(或char)通过unsigned char*(或char),反之亦然 - 您应该能够毫无问题地执行此操作.
3)结果值
在对类型转换指针进行反省略后,您是否能够使用您获得的值?重要的是要记住,上述指针的转换和解除引用相当于重新解释(不改变!)存储在字符地址处的位模式.那么当有符号字符的位模式被解释为无符号字符的位模式(反之亦然)时会发生什么?
当从无符号到有符号时,典型的效果是0到128之间的值没有任何反应,而128以上的值变为负数.反之亦然:从有符号转为无符号时,负值将显示为大于128的值.
但标准实际上并未保证这种行为.唯一的标准保证的是,所有这三种类型,char,unsigned char和signed char,被用于值表示所有位(不一定是8,顺便说一句).因此,如果您将其中一个解释为另一个,制作一些副本然后将其存储回原始位置,您可以确定不会丢失任何信息(如您所示),但您不一定知道这些值是什么实际上是指(至少不是完全可移植的方式).
Mit*_*eat 17
unsigned char或signed char只是解释:没有转换发生.
由于您正在处理字节,以显示意图,因此最好将其声明为
void process(unsigned char *data_in, int data_len);
Run Code Online (Sandbox Code Playgroud)
[正如编辑所说:平原char可以是有符号或无符号类型.C和C++标准明确地允许或者(它始终是一个单独的类型从任一unsigned char或signed char,但具有相同的范围作为其中之一)]
是的,您始终可以毫无问题地将字符转换为无符号字符,反之亦然。如果您运行以下代码,并将其与 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++ 中的答案是否相同?” 是的!