簡體   English   中英

是否有辦法通過插入新的類型名稱而不是int來使用2位大小的類型而不是int?

[英]Is there a way I can use a 2-bit size type instead of an int, by just plugging in the new type name instead of int?

我有一個應用程序,我需要在其中節省盡可能多的內存。 我需要存儲大量數據,這些數據可以正好取三個可能的值。 因此,我一直在嘗試使用2位大小的類型。

一種可能性是使用位字段。 我可以做

struct myType {
    uint8_t twoBits : 2;
}

這是該主題的建議。

但是,在此之前使用int變量的任何地方,我都需要通過附加.twoBits來更改其用法。 我檢查了是否可以在struct之外創建位字段,例如

uint8_t twoBits : 2;

但是這個線程說不可能。 但是,該線程特定於C,因此我不確定它是否適用於C ++。

有沒有一種干凈的方法可以定義2位類型,因此只需將int替換為我的類型,即可正確運行程序? 還是使用位字段是唯一可能的方法?

CPU,以及內存,總線和編譯器也僅使用字節或字節組。 如果不同時存儲其余6位,就無法存儲2位類型。

您可以這樣定義一個僅使用一些位的結構。 但是我們知道它不會節省內存。

如您所知,您可以在結構中打包幾種x位類型。 或者,您可以執行位操作以將它們打包/解包為整數類型。

有沒有一種干凈的方法可以定義2位類型,因此只需將int替換為my類型,即可正確運行程序? 還是使用位字段是唯一可能的方法?

您可以嘗試通過提供隱式轉換運算符和構造函數來使結構盡可能透明:

#include <cstdint>
#include <iostream>

template <std::size_t N, typename T = unsigned>
struct bit_field {
    T rep : N;
    operator T() { return rep; }
    bit_field(T i) : rep{ i } { }
    bit_field() = default;
};

using myType = bit_field<2, std::uint8_t>;

int main() {
    myType mt;
    mt = 3;
    std::cout << mt << "\n";
}

因此,盡管my_type類型的對象具有3位以上的對象,但它們的行為有點像真實的3位無符號整數。 當然,剩余的位未被使用,但是由於單個位在大多數系統上都無法尋址,因此這是最好的選擇。

我不相信您會使用現有結構保存任何內容,因為周圍的結構仍會舍入為整數。

您可以編寫以下代碼將4個2位計數器壓縮為1個字節,但是正如您所說的,您必須將它們命名為myInst.f0:

struct MyStruct
{
  ubyte_t  f0:2,
           f1:2,
           f2:2,
           f3:2;
} myInst;

在c和c ++ 98中,可以聲明此匿名名稱,但不建議使用此用法。 現在,您可以按名稱直接訪問4個值:

struct 
{ // deprecated!
  ubyte_t  f0:2,
           f1:2,
           f2:2,
           f3:2;
};

您可以聲明某種模板,該模板使用運算符int和operator =(int)包裝單個實例,然后定義一個並集以將這4個實例放在同一位置,但是不建議使用匿名並集。 但是,您可以隨后聲明對您的4個值的引用,但是您要為引用付出代價,該引用大於您嘗試保存的字節!

template <class Size,int offset,int bits>
struct Bitz
{
    Size ignore : offset,
         value : bits;
    operator Size()const { return value; }
    Size operator = (Size val) { return (value = val); }
};
template <class Size,int bits>
struct Bitz0
{   // I know this can be done better
    Size value : bits;
    operator Size()const { return value; }
    Size operator = (Size val) { return (value = val); }
};

static union
{   // Still deprecated!
    Bitz0<char, 2> F0;
    Bitz<char, 2, 2> F1;
    Bitz<char, 4, 2> F2;
    Bitz<char, 6, 2> F3;
};

union
{
    Bitz0<char, 2> F0;
    Bitz<char, 2, 2> F1;
    Bitz<char, 4, 2> F2;
    Bitz<char, 6, 2> F3;
} bitz;
Bitz0<char, 2>& F0 = bitz.F0; /// etc...

或者,您可以簡單地聲明宏,以簡單的名稱替換點分的名稱(1970年代):

#定義myF0 myInst.f0

注意,不能通過引用或指針傳遞位域,因為它們沒有字節地址,只能通過值和賦值傳遞。

一個帶有代理類的位數組的極小示例,(在大多數情況下)看起來就像您正在處理非常小的整數數組。

#include <cstdint>
#include <iostream>
#include <vector>

class proxy
{
    uint8_t & byte;
    unsigned int shift;
public:
    proxy(uint8_t & byte,
          unsigned int shift):
              byte(byte),
              shift(shift)
    {

    }
    proxy(const proxy & src):
        byte(src.byte),
        shift(src.shift)
    {

    }
    proxy & operator=(const proxy &) = delete;
    proxy & operator=(unsigned int val)
    {
        if (val <=3)
        {
            uint8_t wipe = 3 << shift;
            byte &= ~wipe;
            byte |= val << shift;
        }
        // might want to throw std::out_of_range here
        return *this;
    }
    operator int() const
    {
        return (byte >> shift) &0x03;
    }
};

代理持有一個字節的引用,並且知道如何提取兩個特定的位,並且對使用它的任何人都像一個int

如果我們使用一個類包裝返回到字節中的位數組,該類返回將該代理對象包裝在適當的字節周圍,則現在看起來很像一個非常小的int數組。

class bitarray
{
    size_t size;
    std::vector<uint8_t> data;
public:
    bitarray(size_t size):
        size(size),
        data((size + 3) / 4)
    {

    }
    proxy operator[](size_t index)
    {
        return proxy(data[index/4], (index % 4) * 2);
    }
};

如果您想擴展它並走遠一點, 編寫自己的STL容器應該可以幫助您制作一個全副武裝且可操作的小型陣列。

這里有虐待的余地。 呼叫者可以保留proxy並采取任何允許這種邪惡的方式。

使用此原始示例:

int main()
{
    bitarray arr(10);

    arr[0] = 1;
    arr[1] = 2;
    arr[2] = 3;
    arr[3] = 1;
    arr[4] = 2;
    arr[5] = 3;
    arr[6] = 1;
    arr[7] = 2;
    arr[8] = 3;
    arr[9] = 1;

    std::cout << arr[0] << std::endl;
    std::cout << arr[1] << std::endl;
    std::cout << arr[2] << std::endl;
    std::cout << arr[3] << std::endl;
    std::cout << arr[4] << std::endl;
    std::cout << arr[5] << std::endl;
    std::cout << arr[6] << std::endl;
    std::cout << arr[7] << std::endl;
    std::cout << arr[8] << std::endl;
    std::cout << arr[9] << std::endl;

}

只需在位集上構建,例如:

#include<bitset>
#include<iostream>
using namespace std;
template<int N> 
class mydoublebitset 
{
public: 
    uint_least8_t operator[](size_t index)
    {
        return 2 * b[index * 2 + 1] + b[index * 2 ];
    }

    void set(size_t index, uint_least8_t store)
    {
        switch (store)
        {
        case 3:
            b[index * 2] = 1;
            b[index * 2 + 1] = 1;
            break;
        case 2:
            b[index * 2] = 0;
            b[index * 2 + 1] = 1;
            break;
        case 1:
            b[index * 2] = 0;
            b[index * 2 + 1] = 1;
            break;
        case 0:
            b[index * 2] = 0;
            b[index * 2 + 1] = 0;
            break;
        default:
            throw exception();
        }
    }
private:
    bitset<N * 2> b;
};

int main()
{

    mydoublebitset<12> mydata; 

    mydata.set(0, 0);
    mydata.set(1, 2);
    mydata.set(2, 2);

    cout << (unsigned int)mydata[0] << (unsigned int)mydata[1] << (unsigned int)mydata[2] << endl;
    system("pause");
    return 0;
}

基本上使用大小為兩倍的位集並相應地對其進行索引。 根據您的要求,它更簡單,存儲效率更高。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM