将unsigned char*强制转换为char*是否安全,并将解除引用的指针视为真正指向char?

Fil*_*efp 19 c++ language-lawyer c++11 c++14

在标题为由于错误的strcmp参数处理而生成的警告之后,似乎存在一些关于标准实际上保证关于字符类型的值表示的问题.


问题

这看起来很好,但标准是否保证(1)总是会产生真实的

char unsigned * p1 = ...;
char          * p2 = reinterpret_cast<char *> (p1);

*p1 == *p2; // (1)
Run Code Online (Sandbox Code Playgroud)

Fil*_*efp 17

这可能令你惊讶,

但是在C++ 11标准(N3337)和即将推出的C++ 14(N3797)中都没有这样的保证.

char unsigned * p1 = ...;
char          * p2 = reinterpret_cast<char *> (p1);

*p1 == *p2; // (1), not guaranteed to be true
Run Code Online (Sandbox Code Playgroud)

注意:无论char签名还是未签名,都是特定实现的; [basic.fundamental]p1.



细节

标准保证每种字符类型;

  • 具有相同的对齐要求 ;
  • 占用相同数量的存储空间;
  • 字符类型占用的所有存储位应参与值表示,并且;
  • 值表示是相同的.

共享相同数量的存储,对齐要求以及有关位参与的保证意味着将涉及一种类型(unsigned char)的左值转换为另一种(char)是安全的......就实际的转换而言.

3.9.1p1 基本类型 [basic.fundamental]

它是实现定义的,是否char可以保持负值.可以显式声明字符signedunsigned.

char,一个signed char,unsigned char占据存储相同量的并具有相同的对准要求(3.11); 也就是说,它们具有相同的对象表示.对于字符类型,对象表示的所有位都参与值表示.

对于无符号字符类型,值表示的所有可能位模式表示数字.这些要求不适用于其他类型.

3.9p4 类型 [basic.types]

对象表示类型的对象的T是序列Ñ unsigned char由类型的对象吸收的对象T,,其中N等于sizeof(T).对象的值表示是保存type值的位集T.



那么,问题是什么?

如果我们将unsigned char(UCHAR_MAX)的最大值分配给*p1并且*p2签名,*p2则无法表示此值.我们将溢出*p2,它最有可能最终具有价值-1.

注意:有符号整数溢出实际上是未定义的行为.


*p1 = UCHAR_MAX;

*p1 == *p2; // (1)
Run Code Online (Sandbox Code Playgroud)

operator==在我们比较之前,双方必须具有相同的类型,目前一方是unsigned char另一方char.

因此,编译器将求助于整体提升以找到可以表示两种类型的所有组合可能值的类型; 在这种情况下,结果类型将是int.

在整体提升之后,语句在语义上等同于int (UCHAR_MAX) == int(-1),当然这是错误的.

  • 小吹毛求疵,问题中没有赋值,也没有溢出。由于整数提升规则和 char 可以具有负值这一事实,这些值并不相等。 (2认同)

小智 14

strcmp (buf1, reinterpret_cast<char const *> (buf2));
Run Code Online (Sandbox Code Playgroud)

看起来很好,

它是.strcmp获取const char *参数,但在内部将它们转换为const unsigned char *(如果需要),这样即使char签名并且两个不同的字节在查看它们时可以比较相等char,它们在查看时仍会比较不同strcmp.

C99:

7.21字符串处理 <string.h>

7.21.1字符串函数约定

3对于本子条款中的所有函数,每个字符都应被解释为具有类型unsigned char(因此每个可能的对象表示都是有效的并且具有不同的值).

那说,

但是标准是否保证(1)总是产生真实的

char unsigned * p1 = ...;
char          * p2 = reinterpret_cast<char *> (p1);

*p1 == *p2; // (1)
Run Code Online (Sandbox Code Playgroud)

你写的不保证.

char使用二进制补码表示,使用带符号的8位字节进行通用实现.如果*p1UCHAR_MAX,那么*p2 == -1,并且*p1 == *p2将是假的,因为促销int给他们不同的价值观.

如果您的意思是(char) *p1 == *p2,或者*p1 == (unsigned char) *p2,那么仍然无法保证,那么您确实需要确保如果从数组复制char到数组unsigned char,则不要包含这样的转换.