【问题标题】:Is there a computationally efficient way to store values smaller than a byte in multidimensional arrays in C/C++?是否有一种计算有效的方法可以在 C/C++ 中的多维数组中存储小于一个字节的值?
【发布时间】:2019-01-19 23:50:59
【问题描述】:

我目前的项目涉及使用 5 维以上的数组,但数组的各个元素不需要有 256 个可能的值。我想知道是否可以通过使用自定义数据类型来节省内存空间,例如,只有 4 或 6 位来表示元素的值,以及这些内存节省是否会带来一些显着的性能成本。

【问题讨论】:

  • 还有 C 和 C++ 中内置的位域功能,在这里可能有用:en.wikipedia.org/wiki/Bit_field
  • @Jeremy 位域几乎没有“计算效率”,而且非常不便携。
  • @NeilButterworth 计算效率是相对的,位字段大致与整数一样可移植(也就是说,在尝试与具有不同表示的其他机器通信时,您需要适当的序列化)。
  • @NeilButterworth 你有理由相信位域比完成相同任务的其他可用方法效率低吗? (AFAIK,当您开始执行位移/掩码操作时,效率会受到影响,并且几乎任何将值压缩为少于一字节的解决方案都会调用这些,无论是程序员编写显式逻辑或 STL 或编译器在幕后生成代码来执行它)。至于可移植性,位域代码将在任何兼容的 C 或 C++ 编译器上正确编译和运行,不是吗?
  • @NeilButterworth- 我必须承认我厌倦了看到宗教“不要使用 XYZ”的声明,因为几乎总是存在 XYZ 真正有用的情况。 int 也有不可移植的用法(例如,可表示值的范围是实现定义的),但没有多少人因此建议避免使用 int。位域是这个问题的一个有效选项,但在决定使用它们之前需要考虑一系列可移植性问题。 “计算效率”也是相对的——什么是可接受的取决于需求、编译器的实现质量等。

标签: c++ c


【解决方案1】:

C 中的多维数组实际上基本上是数组的数组。 (它不能是任何其他方式,因为 RAM 本质上是线性的)。 您可以根据指针算法在线性数组上模拟它们:

#undef NDEBUG
#include <assert.h>
#include <stdint.h>
int main()
{
    typedef uint32_t TYPE;
    enum{A=3,B=4,C=5};
    TYPE a[A][B][C];

    assert((char*)&a[1][2][3] == ((char*)&a) + \
          3*sizeof(TYPE) + 2 *C*sizeof(TYPE) + 1 *B*C*sizeof(TYPE));
}

计算机不允许您处理 sub-char 类型,但不难想象 sub-char 类型。

上述用于寻址 a[1][2][3] 的字符偏移量计算可以重写为

char_ix = (3*sizeof(TYPE)*CHAR_BIT + 2 *C*sizeof(TYPE)*CHAR_BIT + 1 *B*C*sizeof(TYPE)*CHAR_BIT)/CHAR_BIT;

如果您想处理的不是字符(8 位),例如 4 位,您可以将其更改为

char_ix_of_4_bit = 
     (3*sizeof(TYPE)*CHAR_BIT/2 + 
      2 *C*sizeof(TYPE)*CHAR_BIT/2 + 
      1 *B*C*sizeof(TYPE)*CHAR_BIT/2) \
     / CHAR_BIT; //2 4-bits per octet

char_ix_of_4_bit_remainder = 
     (3*sizeof(TYPE)*CHAR_BIT/2 + 
      2 *C*sizeof(TYPE)*CHAR_BIT/2 + 
      1 *B*C*sizeof(TYPE)*CHAR_BIT/2) \
     % CHAR_BIT; //2 4-bits per octet

然后目的地的 4 位值将是

((unsigned char*)&a)[char_ix_of_4_bit] >> (4*char_ix_of_4_bit_remainder)

其他位组类似。

简而言之,您可以考虑多维位数组,将它们重新想象为线性位数组,然后使用常规索引和位移 寻址适当的位组或单个位(IIRC,C++ 的std::bitset/std::vector&lt;bool&gt; 隐藏位索引下的最后一部分 使用重载的 [] 运算符,但手动操作并不难(这在纯 C 中无论如何都需要这样做,因为纯 C 没有运算符重载)。

据说位操作比使用整个类型的操作更慢并且生成更大的代码,但这可能会被更好的缓存局部性所抵消,使用子字符位数组可能会根据您的数据购买(您会如果您尝试这样做,最好有大量数据)。

【讨论】:

  • 空间和速度之间存在永恒的权衡。
  • 我推荐(unsigned char*) 而不是(char*) 以避免常见的符号转换问题。
猜你喜欢
  • 2019-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多