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

时间:2014-06-04 08:17:35

标签: c++ c++11 language-lawyer c++14

在标题为Warning generated due wrong strcmp parameter handling的问题之后,似乎存在一些关于标准实际上保证字符类型的值表示的问题。


问题

这看起来不错,但标准是否保证(1)总是会产生 true

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

*p1 == *p2; // (1)

2 个答案:

答案 0 :(得分:17)

这可能令你惊讶,

C ++ 11 标准版(N3337)以及即将推出的 C ++ 14 中没有这样的保证( N3797)。

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

*p1 == *p2; // (1), not guaranteed to be true

注意:无论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的对象的对象表示是由类型{{{}对象占用的 N unsigned char个对象的序列1}} T,等于N。对象的值表示是保存类型sizeof(T)的值的位集。

  



那么,问题是什么?

如果我们将 unsigned char UCHAR_MAX)的最大值分配给T*p1 已签名,{ {1}}无法代表此值。我们会溢出*p2,它最有可能最终获得*p2的价值。

注意有符号整数溢出实际上是未定义的行为。功能


*p2

-1的双方必须具有相同的类型才能比较它们,目前一方是*p1 = UCHAR_MAX; *p1 == *p2; // (1) 而另一方是operator==

编译器将因此使用整数提升来查找可以表示两种类型的所有组合可能值的类型;在这种情况下,结果类型将是unsigned char

在整数提升之后,语句在语义上等同于char,这当然是错误的。

答案 1 :(得分:14)

strcmp (buf1, reinterpret_cast<char const *> (buf2));
     

看起来很好,

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

C99:

  

7.21字符串处理<string.h>

     

7.21.1字符串函数约定

     

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

那就是说,

  

但标准是否保证(1)始终会产生 true

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

*p1 == *p2; // (1)

您所写的内容无法保证。

使用带有二进制补码表示的带符号char,8位字节的常见实现。如果*p1UCHAR_MAX,则*p2 == -1*p1 == *p2将为false,因为促销int会为其提供不同的值。

如果您的意思是(char) *p1 == *p2*p1 == (unsigned char) *p2,那么这些仍然无法保证,因此您需要确保从char数组复制到数组unsigned char,您不包含此类转化。