【问题标题】:Why can't I static_cast between char * and unsigned char *?为什么我不能在 char * 和 unsigned char * 之间进行静态转换?
【发布时间】:2012-04-26 11:12:41
【问题描述】:

显然编译器认为它们是不相关的类型,因此需要reinterpret_cast。为什么会有这样的规定?

【问题讨论】:

  • 我正在获取字符串的 SHA-1 哈希值。 c_str() 返回 const char *,SHA-1 函数将 const unsigned char * 作为参数。
  • 如果该字符串包含负字符值,您期望会发生什么?
  • 我希望任何负值 c 变为 c + 256,这是将有符号字节转换为无符号字节的标准。老实说,我只是在进行转换以计算哈希值。我不在乎它们是如何转换的,只要它们每次都以相同的方式转换即可。
  • @Nick:将char 转换为unsigned char 是一种转换。将char * 转换为unsigned char*,然后读取元素假设,当它们没有被转换时,它们是非常不同的。它将适用于转换实际上不需要更改表示的系统(例如,在二进制补码系统上),但由于这是一个特定于实现的假设,因此需要显式的 reinterpret_cast 是合适的。

标签: c++ char reinterpret-cast static-cast unsigned-char


【解决方案1】:

它们是完全不同的类型,见标准:

3.9.1 基本类型 [basic.fundamental]

1 声明为字符 char) 的对象应足够大 存储实现的基本字符集的任何成员。如果一个 该集合中的字符存储在字符对象中,积分 该字符对象的值等于单个的值 该字符的字符文字形式。它是 实现定义的 char 对象是否可以持有负数 价值观。字符可以显式声明为无符号或
签。 普通字符、有符号字符和无符号字符是 三种不同的类型。 char、signed char 和 unsigned char 占用相同的存储量并具有相同的对齐方式 要求(basic.types);也就是说,它们具有相同的对象 表示。对于字符类型,对象的所有位
表征参与价值表征。对于未签名 字符类型,值表示的所有可能的位模式 代表数字。这些要求不适用于其他类型。在 任何特定的实现,一个普通的 char 对象都可以采用 与有符号字符或无符号字符相同的值;哪个是 实现定义。

与此类似也是以下失败的原因:

unsigned int* a = new unsigned int(10);
int* b = static_cast<int*>(a); // error different types

ab 是完全不同的类型,你真正要问的是为什么 static_cast 可以毫无问题地执行以下操作时限制如此之大

unsigned int a = new unsigned int(10);
int b = static_cast<int>(a); // OK but may result in loss of precision

为什么它不能推断出目标类型是相同的位域宽度并且可以表示?它可以对标量类型执行此操作,但对于指针,除非目标是从源派生的并且您希望执行向下转换,那么指针之间的转换将不起作用。

Bjarne Stroustrop 说明了为什么 static_cast 在此链接中很有用:http://www.stroustrup.com/bs_faq2.html#static-cast 但以缩写形式让用户清楚地说明他们的意图是什么,并让编译器有机会检查你是什么可以实现意图,因为static_cast 不支持不同指针类型之间的转换,那么编译器可以捕获此错误以提醒用户,如果他们真的想要进行此转换,则应使用reinterpret_cast

【讨论】:

  • 感谢您在此处说明标准。我没有。
  • 同样的原因,您可以将 static_cast 从 doubles 转换为 int,反之,您不能做的是 static_cast double* 到 int*,指针类型不同,但您可以从一个值转换为另一个警告说可能会降低精度
  • @Nick:即使是intfloat。它们可能具有相同的大小,但是如果您有一个int,那么请尝试像float 一样读取它,那么您得到的结果取决于float 在内存中的存储方式。由于规范没有float如何存储在内存中,规范不能定义int的样子。记住:C++ 标准的存在是为了保证你得到什么。为了定义这种行为,标准必须详细说明float 在内存中的布局方式,以及int 在内存中的布局方式。
  • @Nick:简而言之:您将“真实机器上发生的事情”与“规范要求”的概念混淆了。
  • 规范没有说最高有效位必须是符号位,因此实现可以让它成为最低有效位。如果是这种情况,当编译器将值为 1 的无符号字符转换为有符号字符时,它会知道需要将位左移 1 以说明符号位。但是对于 unsigned char* 和 char*,调整指针位置的值不是演员的工作。无论如何它都不知道要调整多少个字符。
【解决方案2】:

您正在尝试使用 static_cast 转换不相关的指针。这不是 static_cast 的用途。在这里你可以看到:Type Casting

使用 static_cast 您可以转换数字数据(例如 char 到 unsigned char 应该可以工作)或指向相关类的指针(通过某些继承相关)。这两种情况都不是。您想将一个不相关的指针转换为另一个,因此您必须使用 reinterpret_cast。

基本上,您尝试对编译器执行的操作与尝试将 char * 转换为 void * 相同。


好的,这里有一些额外的想法,为什么允许这样做从根本上是错误的。 static_cast 可用于将数字类型相互转换。所以这样写是完全合法的:

char x = 5;
unsigned char y = static_cast<unsigned char>(x);

还有什么可能:

double d = 1.2;
int i = static_cast<int>(d);

如果您在汇编程序中查看此代码,您会发现第二次强制转换不仅仅是对 d 位模式的重新解释,而是在此处插入了一些用于转换的汇编程序指令。

现在,如果我们将这种行为扩展到数组,只需一种不同的方式来解释位模式就足够了,它可能会起作用。但是如何将双精度数组转换为整数数组呢? 这就是您必须声明您想要重新解释位模式的地方 - 有一种称为 reinterpret_cast 的机制,或者您必须做一些额外的工作。正如您所看到的,简单地为指针/数组扩展 static_cast 是不够的,因为它需要表现得类似于 static_casting 类型的单个值。这有时需要额外的代码,并且不清楚如何为数组完成此操作。在你的情况下 - 停在 \0 - 因为这是惯例?这对于非字符串情况(数字)是不够的。如果数据类型的大小发生变化(例如 x86-32 位上的 int 与 double)会发生什么?

无法为所有用例正确定义您想要的行为,这就是它不在 C++ 标准中的原因。否则,您将不得不记住以下内容:“我可以将这种类型转换为另一种类型,只要它们是整数类型,具有相同的宽度并且......”。这样就很清楚了——它们要么是相关的 CLASSES——然后你可以转换指针,或者它们是数字类型——然后你可以转换值。

【讨论】:

  • 我在开篇文章中承认编译器说它们是不相关的指针。我想知道的是为什么。在我看来,如果T1T2“相关”,那么T1 * 应该与T2 *“相关”。为什么输入规则听起来不正确(对于原始类型)?
  • @Nick 它不是“以某种方式相关”而是“相关类”。正如您所说, char 和 unsigned char 是原语 - 而不是类。这就是原因——如果你仔细阅读的话,这就是我所说的。你是对的 - 如果 T1 类与 T2 类相关,那么你可以使用 static_cast 将 T1* 转换为 T2*。这不是你在做什么。在 C++ 标准要求的关系意义上,char 与 unsigned char 无关。
  • 但是,如果你删除指针,编译器在任何原始类型之间转换都没有问题,例如unsigned char a = 255; char b = static_cast&lt;char&gt;(a); 似乎有点奇怪,因为如果 T1T2 是类,则指针之间的转换是不正确的,因为您可以执行以下操作:class A; class B : public A; B *b = new B[4]; b[0] = B(); @987654335 @ @a[1] = A(); B b1 = b[1]; // oops 似乎唯一时间应该是安全的强制转换是在原始类型之间。
  • 是的 - 其行为是明确且明确的。您有一些关于如何将一种数字类型转换为另一种的规则。这可能就像将位模式复制到新内存中一样简单,也可能像将 double 转换为 int 一样复杂。仍然 - 它仅用于一个值,因此很容易定义。见我上面的解释。根据类型是否为指针类型,static_cast 有两种完全不同的用途。他们应该为 2 个用例(例如 static_cast 和 numeric_cast)使用不同的名称。
【解决方案3】:

除了作为指针之外,unsigned char *char * 没有任何共同点(EdChum 已经提到 charsigned charunsigned char 是三种不同的类型)。对于指向任何不同结构的 Foo *Bar * 指针类型,您可以说同样的话。

static_cast表示源类型的指针可以作为目的类型的指针,需要有子类型关系。因此,它不能在您的问题的上下文中使用;您需要的是 reinterpret_cast,它完全符合您的要求,或者是 C 风格的演员表。

【讨论】:

    猜你喜欢
    • 2011-03-15
    • 2013-01-19
    • 1970-01-01
    • 1970-01-01
    • 2011-10-19
    • 2013-09-23
    • 2010-11-16
    • 2017-06-08
    相关资源
    最近更新 更多