【问题标题】:Having 8-bit and 16-bit views on the same data?对同一数据有 8 位和 16 位视图?
【发布时间】:2015-08-08 18:41:43
【问题描述】:

问题

我正在使用 C++ 开发视频游戏模拟器。

内存由 uint8_t 值的数组表示。通常,我还需要以 16 位值(两个连续的 8 位值)访问此内存的内容。

我有一些看起来像这样的东西:

struct Memory {

  uint8_t map[0xFFFF];

  // Let's say that [] is for accessing 8-bit values
  uint8_t& operator[](uint16_t address) {
      return map[address];
  }

 // () is for 16-bit values
  uint16_t& operator()(uint16_t address) {
      return ???; // Looking for a good implementation here
  }
};

例如,如果内存包含 [0xC0, 0xFF, 0x66, 0xAA...] 那么[] 将返回:

mem[0] -> 0xC0 mem[1] -> 0xFF mem[2] -> 0x66

并且() 将返回(取决于系统字节序):

mem(0) -> 0xFFC0 mem(1) -> 0x66FF mem(2) -> 0xAA66

这些访问方法将被大量调用。我想利用() 中的指针进行快速访问。我不想通过移位和 |'ing 8 位对来计算 16 位值(我不能,因为该方法必须返回一个引用)。

我的问题: C++ 中有没有办法在同一个缓冲区上拥有不同的视图?指向相同数据的 8 位视图和 16 位视图将是理想的。

(例如,JS ArrayBuffer API 提供了 DataView,它可以做到这一点。也许有一个聪明的技巧可以用 C++ 中的指针干净地实现这一点?)

尝试 #1

我的第一个猜测是使用某种联合:

union mem_t {
    uint8_t bytes[0xFFFF];
    uint16_t words[0x8000]
}

但随后只能访问偶数字节的 16 位值:

mem.words[0] -> 0xFFC0

mem.words[1] -> 0xAA66 // Should be 0x66FF :(

尝试 #2

一个可行的解决方案是为偶数和奇数地址添加两个额外的 16 位指针,以处理重叠

uint16_t* even16 = (uint16_t*) map;
uint16_t* odd16 = (uint16_t*) (map + 1);

uint16_t& Memory::operator()(uint16_t address) {
    return address % 2 ? odd16[address / 2] : even16[address / 2];
}

它工作正常,但看起来很复杂,我相信还有更优雅的解决方案。

谢谢。

【问题讨论】:

  • 您在寻找return *(uint16_t*)&map[address]; 吗?你把事情弄得太复杂了。
  • 感谢伊戈尔的建议,这很好用。但是,我仍然对每次访问都没有强制转换的“懒惰”方法感兴趣。
  • 然后投射一次并保存到指针中...
  • 您听起来好像您已经测量了性能并确定演员表是性能瓶颈。这不太可能,因为强制转换通常纯粹是为了编译器的利益 - 它不对应于在运行时执行的任何实际机器代码。
  • Igor:感谢您澄清这一点。我会采用你的方法。 deviantfan:这样的缓存指针不能访问从所有地址开始的 16 位数据(只有那些与第一次访问的地址具有相同奇偶校验的地址才能访问,带有偏移量)。

标签: c++ arrays struct buffer 8-bit


【解决方案1】:

类似这样,但您可能会违反主机系统上的内存对齐规则:

struct Memory {

    uint8_t map[0xFFFF];

    // Let's say that [] is for accessing 8-bit values
    uint8_t& operator[](uint16_t address) {
        return map[address];
    }

    // () is for 16-bit values
    uint16_t& operator()(uint16_t address) {
        return *reinterpret_cast<uint16_t*>(&map[address]);
    }
};

【讨论】:

  • 感谢您的建议。这个 reinterpret_cast 与 Igor 的“原始”演员阵容相比如何?
  • @merwaaan 实际上是一样的。只是 C 只有这个“括号强制转换”(type) 并且 C++ 有三个额外的名称来表达强制转换的原因是什么(以及一种非常新的第四种类型的强制转换,但在这里并不重要)
  • 几乎完全一样,但更明确一点。它在概念上“更安全”,因为它不会抛弃 constness
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-24
  • 2017-07-12
  • 1970-01-01
  • 2011-05-11
  • 1970-01-01
相关资源
最近更新 更多