Работа с битовым массивом на Си

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by slesh, 29 Mar 2011.

  1. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    Собственно говоря столкнулся с одной вещью. Требовалось реализовать быструю работу с битами для большого массива. т.е. зная начало памяти и индекс бита, уметь проверять его установленность, сбрасывать и устанавливать. Очень удобно использовать такие массивы когда требуется хранить большое кол-во значений true / false. т.е. по сравнению с обычными байтовыми массивами - используется в 8 раз меньше памяти.

    И вот написал 3 макроса, может кому нибудь будут полезны:
    Code:
    #define SET_BIT(mem, index)(((DWORD*)mem)[index >> 5] |= (1 << (index & 0x1F)))
    
    #define UNSET_BIT(mem, index)(((DWORD*)mem)[index >> 5] ^= (1 << (index & 0x1F)))
    
    #define ISSET_BIT(mem, index)(((DWORD*)mem)[index >> 5] & (1 << (index & 0x1F)))
    
    Сделал в таком виде для того, чтобы код функций всегда был подставляемым, а не вызывался каждый раз.
    К тому же VS(да и другие компиляторы) довольно хорошо умеет оптимизировать функции в таком виде, нежели обычные (порой даже __inline и __declspec(naked) такого результата не дадут).

    Использование функции:
    Первый параметр - адрес памяти
    Второй параметр - индекс бита от начала памяти.


    Функция ISSET_BIT возвращает значение 0 если бит не установлен и не нулевое значение если установлен.

    Таким образом можно адресовать массив до 4 миллиардов битов который будет занимать в памяти 512 мегобайт.

    Память желательно чтобы была выравненная на 4 байта. Так быстрее работать будет.
     
    #1 slesh, 29 Mar 2011
    Last edited: 30 Mar 2011
    2 people like this.
  2. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    2slesh
    Code:
    #define   SET_BIT(mem, index)(((DWORD*)(mem))[(index) >> 5] |= (1 << ((index) & 0x1F)))
    #define UNSET_BIT(mem, index)(((DWORD*)(mem))[(index) >> 5] ^= (1 << ((index) & 0x1F)))
    #define ISSET_BIT(mem, index)(((DWORD*)(mem))[(index) >> 5] &  (1 << ((index) & 0x1F)))
    
    в макросе можно ожидать подлости от такого i++
    но не от такого expr ^ expr или expr | expr
    или expr + expr без скобочек в каждом случае
    даст неправильный результат
     
    1 person likes this.
  3. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    2 greki_hoy Ну как бы если юзаешь, то должен знать это.
    Да и нагромождать код зачем. Также запросто можно выйти за пределы массива. но это уже другая история.
    Но для обычных операций пойдет.
     
  4. Jakeroid

    Jakeroid Member

    Joined:
    9 May 2009
    Messages:
    198
    Likes Received:
    12
    Reputations:
    1
    to slesh
    <офтоп>
    Сколько лет программируешь? И со скольки начал?
    </офтоп>
     
  5. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    2 Jakeroid начал программировать летом 2003 года. Так что почти уже 8 лет пишу, каждый день.
     
  6. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    2 slesh
    для лучшей защиты также можно рассматривать
    индекс как беззнаковое при сдвиге
    Code:
    #define SET_BIT(mem, index) \
    	(((DWORD*)(mem))[(DWORD)(index) >> 5] |= (1 << ((index) & 0x1F)))
    #define UNSET_BIT(mem, index) \
    	(((DWORD*)(mem))[(DWORD)(index) >> 5] ^= (1 << ((index) & 0x1F)))
    #define ISSET_BIT(mem, index) \
    	(((DWORD*)(mem))[(DWORD)(index) >> 5] &  (1 << ((index) & 0x1F)))