【问题标题】:Is reinterpret_cast bad when dealing with low-level byte manipulation?处理低级字节操作时 reinterpret_cast 不好吗?
【发布时间】:2012-12-15 06:21:28
【问题描述】:

我正在编写一个 websocket 服务器,我必须处理需要取消屏蔽的屏蔽数据。

掩码为 un​​signed char[4],数据也是 unsigned char* 缓冲区。

我不想逐字节异或,我更愿意一次异或 4 个字节。

uint32_t * const end = reinterpret_cast<uint32_t *>(data_+length);
for(uint32_t *i = reinterpret_cast<uint32_t *>(data_); i != end; ++i) {
    *i ^= mask_;
}

在这种情况下使用 reinterpret_cast 有什么问题吗?

替代方案是以下代码,它不那么清晰且不那么快:

uint64_t j = 0;
uint8_t *end = data_+length;
for(uint8_t *i = data_; i != end; ++i,++j) {
    *i ^= mask_[j % 4];
}

我对替代方案很感兴趣,包括依赖于 c++11 功能的替代方案。

【问题讨论】:

  • “当...时 reinterpret_cast 不好” - “是的,永远不要使用它!” - 每个 C++ 人,永远。
  • 好吧,如果您涉及reinterpret_cast 的代码甚至远程接近正确,我个人不会有问题。 ;-)
  • 作为一个即时反应,我想知道length 是否保证是sizeof(uint32_t) 的倍数。否则代码有明显缺陷。
  • @H2CO3 有时您无法绕过reinterpret_cast。在(几乎)所有情况下,C 风格的转换都是不好的。
  • 另一种可能性是data 可能无法正确对齐以处理该地址处的uint32_t。在许多平台上这不是问题,但有些平台需要正确对齐数据才能访问。如果数据正确对齐,我想知道你为什么不使用更大的类型。

标签: c++ casting c++11 reinterpret-cast


【解决方案1】:

发布的方法存在一些潜在问题:

  1. 在某些类型大于char 的系统上,需要正确对齐才能访问。 uint32_t 的一个典型要求是对象与可被四整除的地址对齐。
  2. 如果length / sizeof(uint32_t) != 0 循环可能永远不会终止。
  3. 根据系统的字节序,mask 需要包含不同的值。如果mask 是由*reinterpret_cast&lt;uint32_t&gt;(char_mask) 产生的一个合适的数组,那么它不应该是一个数组。

如果这些问题都解决了,reinterpret_cast&lt;...&gt;(...) 可以在您遇到的情况下使用。重新解释指针的含义是该操作存在并且有时需要它的原因之一。不过,我会创建一个合适的测试用例来验证它是否正常工作,以避免在将代码移植到不同平台时不得不寻找问题。

我个人会采用不同的方法,直到分析显示它太慢:

char* it(data);
if (4 < length) {
    for (char* end(data + length - 4); it < end; it += 4) {
        it[0] ^= mask_[0];
        it[1] ^= mask_[1];
        it[2] ^= mask_[2];
        it[3] ^= mask_[3];
    }
}
it != data + length && *it++ ^= mask_[0];
it != data + length && *it++ ^= mask_[1];
it != data + length && *it++ ^= mask_[2];
it != data + length && *it++ ^= mask_[3];

我肯定在软件中使用了许多类似的方法,这些方法意味着更快,并且没有发现它们是一个显着的性能问题。

【讨论】:

    【解决方案2】:

    在这种情况下,reinterpret_cast 并没有什么特别的问题。但是,请小心。

    目前的 32 位循环是不正确的,因为它不适合有效负载大小不是 32 位倍数的情况。我想有两种可能的解决方案:

    • 在for循环检查中将!=替换为&lt;(人们使用&lt;是有原因的,这不是因为他们很笨......)并按字节执行尾随1-3字节
    • 排列缓冲区,使有效负载部分的缓冲区大小为 32 位的倍数,并对多余的字节进行异或运算。 (大概代码在向调用者返回字节时会检查有效负载长度,所以这无关紧要。)

    此外,根据代码的结构,您可能还必须应对某些 CPU 的未对齐数据访问。如果您在 32 位对齐的缓冲区中缓冲了整个帧、标头和所有内容,并且有效负载长度小于 126 字节或 >65,535 字节,则掩码键和有效负载都将未对齐。

    无论如何,我的服务器使用类似于第一个循环的东西:

    for(int i=0;i<n;++i)
        payload[i]^=key[i&3];
    

    与 32 位选项不同,这基本上是不可能出错的。

    【讨论】:

      猜你喜欢
      • 2014-02-24
      • 1970-01-01
      • 2012-02-19
      • 2016-02-09
      • 2021-10-05
      • 2015-10-12
      • 1970-01-01
      • 2014-12-26
      • 1970-01-01
      相关资源
      最近更新 更多