Есть ли способ, которым я могу использовать 2-битный тип размера вместо int, просто подключив новое имя типа вместо int? - PullRequest
0 голосов
/ 08 июня 2018

У меня есть приложение, в котором мне нужно максимально сэкономить память.Мне нужно хранить большое количество данных, которые могут принимать ровно три возможных значения.Итак, я пытался использовать тип размером 2 бита.

Одна из возможностей - использование битовых полей.Я мог бы сделать

struct myType {
    uint8_t twoBits : 2;
}

Это предложение от этой темы .

Однако везде, где я использовал int переменные до этого, мне понадобилось быизменить их использование, добавив .twoBits.Я проверил, могу ли я создать битовое поле вне struct, например

uint8_t twoBits : 2;

, но этот поток говорит, что это невозможно.Однако этот поток специфичен для C, поэтому я не уверен, применился ли он к C ++.

Есть ли чистый способ определить 2-битный тип, просто заменив int на мойтипа, я могу правильно запустить программу?Или использование битовых полей является единственно возможным способом?

Ответы [ 5 ]

0 голосов
/ 11 июня 2018

Проще говоря, построите поверх набора битов, что-то вроде:

#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;
}

В основном используйте набор битов с удвоенным размером и соответствующим образом индексируйте его.он проще и эффективнее с точки зрения памяти.

0 голосов
/ 10 июня 2018

Очень минимальный пример массива битов с прокси-классом, который выглядит (по большей части) так, как если бы вы имели дело с массивом очень маленьких целых чисел.

#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;

}
0 голосов
/ 08 июня 2018

CPU и, следовательно, память, шина и компилятор также используют только байты или группы байтов.Нет способа сохранить 2-битный тип без сохранения также остальных 6 оставшихся битов.

Что вы можете сделать, так это определить структуру, которая использует только некоторые биты.Но мы знаем, что это не сэкономит память.

Вы можете упаковать несколько x-битных типов в структуру, как вы уже знаете.Или вы можете выполнять битовые операции, чтобы упаковать / распаковать их в целочисленный тип.

0 голосов
/ 08 июня 2018

Я не уверен, что вы сохраните что-либо с вашей существующей структурой, поскольку окружающая структура все еще округляется до целого числа байтов.

Вы можете написать следующее для сжатия 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-е годы):

# define myF0 myInst.f0

Обратите внимание, что вы не можете передавать битовые поля по ссылке или по указателю, поскольку они не имеютадрес байта, только по значению и присваиванию.

0 голосов
/ 08 июня 2018

Есть ли чистый способ определить 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 бит.Конечно, остаточные биты не используются, но поскольку в большинстве систем отдельные биты не адресуемы, это лучший способ.

...