【问题标题】:reinterpret casting to and from unsigned char* and char*重新解释与 unsigned char* 和 char* 之间的转换
【发布时间】:2010-11-16 19:35:38
【问题描述】:

我想知道是否有必要在下面的函数中重新解释_cast。 ITER_T 可能是 char*、unsigned char*、std::vector 迭代器或其他类似的东西。到目前为止它似乎并没有受到伤害,但是转换是否会影响字节的复制方式?

template<class ITER_T>
char *copy_binary(
  unsigned char length,
  const ITER_T& begin)
{
  // alloc_storage() returns a char*
  unsigned char* stg = reinterpret_cast<unsigned char*>(alloc_storage(length));
  std::copy(begin, begin + length, stg);
  return reinterpret_cast<char*>(stg);
}

【问题讨论】:

  • alloc_storage函数返回什么类型?

标签: c++ templates casting


【解决方案1】:

reinterpret_casts 用于低级实现定义的强制转换。根据标准,reinterpret_casts可用于以下转换(C++03 5.2.10):

  • 指向整数类型的指针
  • 指针的整数类型
  • 指向函数的指针可以转换为指向不同类型函数的指针
  • 指向对象的指针可以转换为指向不同类型对象的指针
  • 指向成员函数的指针或指向数据成员的指针可以转换为不同类型的函数或对象。这种指针转换的结果是未指定的,除了指针 a 转换回其原始类型。
  • 如果指向类型 A 的指针可以使用 reinterpret_cast 显式转换为类型 B,则类型 A 的表达式可以转换为类型 B 的引用。

也就是说,在您的情况下使用 reinterpret_cast 不是一个好的解决方案,因为标准未指定转换为不同的类型,尽管从 char * 转换为 unsigned char * 并返回应该适用于大多数机器。

在您的情况下,我会考虑使用 static_cast 或通过将 stg 定义为类型 char * 来完全不进行转换:

template<class ITER_T>
char *copy_binary(
  unsigned char length,
  const ITER_T& begin)
{
  // alloc_storage() returns a char*
  char* stg = alloc_storage(length);
  std::copy(begin, begin + length, stg);
  return stg;
}

【讨论】:

  • +1 对我来说似乎是一个很好的答案——它告诉你什么时候使用 reinterpret_cast 并且你不应该在这里使用它。
  • 这一切都很好,除了关于使用'static_cast'的部分。我的理解是静态转换只能用于相关指针类型和void*之间的转换。根据标准,'unsigned char' 和 'char' 是不相关的类型,因此这种情况仅包含在 reinterpret_cast 中的第 4 个项目符号中。
【解决方案2】:

编写的代码按照标准 4.7 (2) 的要求工作,尽管这仅适用于具有二进制补码表示的机器。

如果 alloc_storage 返回一个 char*,并且 'char' 是有符号的,那么如果我正确理解 4.7 (3),如果迭代器的值类型是无符号的,那么结果将由实现定义,并且您将放弃强制转换并传递 char * 复制。

【讨论】:

    【解决方案3】:

    简短的回答是肯定的,它可能会影响。

    charunsigned char 是可转换的类型(C++ 标准 0x n2800 中的 3.9.1),因此您可以将一个分配给另一个。 你根本不需要演员表。

    [3.9.1] ... 一个字符、一个有符号字符和一个无符号字符 char占用相同的存储量 并具有相同的对齐方式 要求;也就是说,他们有 相同的对象表示


    [4.7] ...

    2 如果目标类型是 无符号,结果值为 最小无符号整数等于 源整数(模 2n 其中 n 是用于的位数 表示无符号类型)。

    [注意: 在二进制补码表示中, 这种转换是概念性的,并且 位模式没有变化 (如果没有截断)。 ——尾注 ]

    3 如果目标类型是有符号的, 值不变如果可以的话 以目标类型表示 (和位域宽度);否则, value 是实现定义的。

    因此,即使在最坏的情况下,您也将获得最佳(较少实现定义)的转换。无论如何,在大多数实现中,这不会改变位模式中的任何内容,如果查看生成的汇编程序,您甚至不会进行转换。

    template<class ITER_T>
    char *copy_binary( unsigned char length, const ITER_T& begin)
    {
      char* stg = alloc_storage(length);
      std::copy(begin, begin + length, stg);
      return stg;
    }
    

    使用 reinterpret_cast 你依赖于编译器:

    [5.2.10.3] 执行的映射 reinterpret_cast 是 实现定义。 [注:它 可能会或可能不会产生 表示不同于 原值。 ——尾注]

    注意:This 是一个有趣的相关帖子。

    【讨论】:

      【解决方案4】:

      所以如果我做对了,转换为 unsigned char 是为了保证一个无符号的逐字节复制。但是然后你把它扔回去以获得回报。该功能看起来有点狡猾,以这种方式设置它的上下文/原因到底是什么?一个快速的解决方法可能是用 memcpy() 替换所有这些(但正如评论的那样,不要在迭代器对象上使用它)——否则只需删除多余的强制转换。

      【讨论】:

      • 你应该小心使用带有迭代器的 memcpy。在这种情况下,他使用的是向量,但包含的数据并不总是连续的。
      • 这里是上下文:我正在使用一个保存 char* 和子字符串范围的 StringSlice 类。但是,我保留在 StringSlice 中的一些数据确实应该被解释为无符号的。具有 StringSlice 成员的类允许某人使用 unsigned char 或signed char 的几种方式设置数据,并且有一些方法可以将数据检索为 StringSlice、unsigned char* 或将数据复制为 unsigned char
      • 你在上面的链接中发布的这个函数在.. 在后者中,要检索 StringSlice 中的数据副本?
      猜你喜欢
      • 2018-08-22
      • 2011-03-15
      • 2016-03-26
      • 1970-01-01
      • 2011-10-19
      • 2013-09-23
      • 1970-01-01
      • 2013-06-15
      • 2012-09-03
      相关资源
      最近更新 更多