4

我想定义我自己的数据类型,它可以保存六个可能值中的一个,以便了解更多关于 C++ 中的内存管理的信息。在数字上,我希望能够保持 0 到 5。二进制,三个位(101 = 5)就足够了,尽管不会使用一些(6 和 7)。数据类型也应该消耗尽可能少的内存。

我不确定如何做到这一点。首先,我尝试了一个为所有字段定义值的枚举。据我所知,那里的值是十六进制的,所以一个“hexbit”应该允许我存储 0 到 15。但是将它与一个字符(使用 sizeof)进行比较,它表明它是字符大小的 4 倍,并且如果我没记错的话,一个 char 包含 0 到 255。

#include <iostream>

enum Foo
{
    a = 0x0, 
    b = 0x1,
    c = 0x2,
    d = 0x3,
    e = 0x4,
    f = 0x5,
};

int main()
{
    Foo myfoo = a;
    char mychar = 'a';

    std::cout << sizeof(myfoo); // prints 4
    std::cout << sizeof(mychar); // prints 1

    return 1;
}

我显然误解了一些东西,但看不到是什么,所以我转向 SO。:)

此外,在写这篇文章时,我意识到我显然缺乏某些词汇。我已将此帖子设为社区 wiki,请对其进行编辑,以便我可以学习所有内容的正确单词。

4

10 回答 10

10

Achar是可能的最小类型。

如果您碰巧知道在一个地方需要多个这样的 3 位值,则可以使用具有位域语法的结构:

struct foo {
  unsigned int val1:3;
  unsigned int val2:3;
};

因此在一个字节内得到其中的 2 个。理论上,您可以将 10 个这样的字段打包成一个 32 位的“int”值。

于 2009-04-11T14:57:45.380 回答
3

C++ 0x 将包含强类型枚举,您可以在其中指定基础数据类型(在您的示例 char 中),但当前的 C++ 不支持这一点。该标准并不清楚此处是否使用 char(示例为 int、short 和 long),但它们提到了基础整数类型,并且也包括 char。

截至今天,Neil Butterworth 为您的问题创建一个类的答案似乎是最优雅的,因为如果您想要值的符号名称,您甚至可以将其扩展为包含嵌套枚举。

于 2009-04-11T16:43:54.860 回答
2

C++ 不表示小于字节的内存单位。如果你一次生产一个,那是你能做的最好的。您自己的示例效果很好。如果您只需要一些,您可以使用 Alnitak 建议的位域。如果您打算一次分配一个,那么您的情况会更糟。大多数架构分配页面大小单位,16 字节是常见的。

另一种选择可能是包装 std::bitset 来做你的投标。这将浪费很少的空间,如果你需要很多这样的值,每 8 只需要大约 1 位。

如果您将问题视为一个以 6 为底的数字,并将该数字转换为以 2 为底的数字,可能使用无限精度整数(例如 GMP),您根本不会浪费任何位。

当然,这假设您的值具有均匀的随机分布。如果它们遵循不同的发行版,那么最好使用 gzip 之类的第一个示例的一般压缩。

于 2009-04-11T15:06:24.250 回答
2

您可以存储小于 8 位或 32 位的值。您只需要将它们打包成一个结构(或类)并使用位字段

例如:

struct example
{
    unsigned int a : 3; //<Three bits, can be 0 through 7.
            bool b : 1; //<One bit, the stores 0 or 1.
    unsigned int c : 10; //<Ten bits, can be 0 through 1023.
    unsigned int d : 19; //<19 bits, can be 0 through 524287.
}

在大多数情况下,您的编译器会将结构的总大小四舍五入到 32 位平台上的 32 位。另一个问题是,就像您指出的那样,您的值可能没有两个范围的幂。这会浪费空间。如果您将整个结构读取为一个数字,如果您的输入范围不是 2 的所有幂,您会发现无法设置的值。

您可能会发现另一个有趣的功能是union。它们像结构一样工作,但共享内存。因此,如果您写入一个字段,它会覆盖其他字段。

现在,如果您的空间真的很紧,并且想将每个位推到最大,那么有一种简单的编码方法。假设您要存储 3 个数字,每个数字可以从 0 到 5。位字段是浪费的,因为如果您每个使用 3 个位,您将浪费一些值(即您永远不能设置 6 或 7,即使您有存放它们的空间)。所以,让我们做一个例子:

//Here are three example values, each can be from 0 to 5:
const int one = 3, two = 4, three = 5;

为了最有效地将它们打包在一起,我们应该考虑以 6 为基数(因为每个值都是从 0 到 5)。所以装进尽可能小的空间是:

//This packs all the values into one int, from 0 - 215.
//pack could be any value from 0 - 215. There are no 'wasted' numbers.
int pack = one + (6 * two) + (6 * 6 * three);

看看我们以六为基数编码的样子?每个数字乘以它的位置,例如 6^n,其中 n 是位置(从 0 开始)。

然后解码:

const int one = pack % 6;
pack /= 6;
const int two = pack % 6;
pack /= 6;
const int three = pack;

当您必须对条形码或字母数字序列中的某些字段进行编码以供人工输入时,这些方案非常方便。仅仅说出这几个部分就可以产生巨大的影响。此外,这些字段不必都具有相同的范围。如果一个字段是从 0 到 7,则在适当的位置使用 8 而不是 6。不要求所有字段具有相同的范围。

于 2009-04-11T22:26:39.980 回答
1

您可以使用的最小大小 - 1 个字节。

但是,如果您使用一组枚举值(写入文件或存储在容器中,..),您可以打包这个组 - 每个值 3 位。

于 2009-04-11T15:00:43.423 回答
1

最好的解决方案是创建您自己的使用 char 实现的类型。这应该有 sizeof(MyType) == 1,尽管这不能保证。

#include <iostream>
using namespace std;

class MyType {

    public:

        MyType( int a ) : val( a ) {
            if ( val < 0 || val > 6 ) {
                throw( "bad value" );
            }
        }

        int Value() const {
            return val;
        }

    private:

        char val;
};

int main() {

    MyType v( 2 );
    cout << sizeof(v) << endl;
    cout << v.Value() << endl;
}
于 2009-04-11T15:02:19.720 回答
1

您不必枚举枚举的值:

enum Foo
{
    a, 
    b,
    c,
    d,
    e,
    f,
};
Foo myfoo = a;

Foo是 的别名int,在您的机器上占用 4 个字节。

最小的类型是char,它被定义为目标机器上最小的可寻址数据。宏产生 a 中的CHAR_BIT位数,char并在 中定义limits.h

[编辑]

请注意,一般来说,您不应该问自己这样的问题。如果足够,请始终使用[unsigned] int,除非您分配了大量内存(例如int[100*1024]vs char[100*1024],但考虑std::vector改为使用)。

于 2009-04-11T15:03:21.933 回答
1

由于架构不支持位级操作(因此每个操作需要多个处理器指令),将奇数大小的值打包到位域中可能会导致相当大的性能损失。在你实现这种类型之前,问问自己是否真的需要尽可能少地使用空间,或者你是否犯了过早优化的编程大罪。最多,我会将值封装在一个类中,如果您出于某种原因确实需要压缩每个最后一个字节,则该类的后备存储可以透明地更改。

于 2009-04-11T22:36:22.703 回答
0

您可以使用无符号字符。可能将其 typedef 为一个 BYTE。它只占用一个字节。

于 2009-04-11T14:56:32.823 回答
0

枚举的大小被定义为与 int 相同。但是根据您的编译器,您可以选择创建更小的枚举。例如,在 GCC 中,您可以声明:

enum Foo {
    a, b, c, d, e, f
}
__attribute__((__packed__));

现在,sizeof(Foo) == 1。

于 2009-04-11T17:02:18.200 回答