2015-11-15 3 views
0

Мой профессор назначил нам домашнее задание, которое использует битовые поля, и дал нам три макросаC: Битовые поля и операторы битовые

# define SETBIT(A, k) { A[k >> 3] |= (01 << (k & 07)); } 
# define CLRBIT(A, k) { A[k >> 3] &= ~(01 << (k & 07)); } 
# define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 

, которые мы не требуется/Ожидается, что в полной мере понять, только использовать для выполнения задания. Каждый из этих макросов принимает unsigned int и индекс (0-8) и устанавливает/получает/очищает бит по этому индексу. Я получаю это, и я получаю, как его использовать.

Что я хочу знать, это именно то, что делает каждый из этих макросов. Может ли кто-нибудь объяснить это мне, как будто мне пять?

+1

Если вы знаете, что делают операции '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' x = x op y', вы можете понять это сами, работая над некоторыми примерами. Попробуйте k == 7 и k == 8 ... Подсказки: A - это массив из 8-битных байтов, правый сдвиг на три - это то же самое, что и деление на 8, а двоичное представление 7 равно 00000111. – keshlam

ответ

2

Что макросы делать

Игнорирование проблемы, описанные в следующем разделе, макросы рассматривать массив интегрального типа как массив 8-битных значений, и когда попросили работать на долото k, обрабатывает k%8th бит k/8th элемент массива.

Однако, вместо использования k % 8 или k/8, он использует сдвиги и маскировку.

# define SETBIT(A, k) { A[k >> 3] |= (01 << (k & 07)); } 
# define CLRBIT(A, k) { A[k >> 3] &= ~(01 << (k & 07)); } 
# define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
  • k >> 3 сдвигает значения вправо на 3 битовых позиций, эффективно деления на 8.
  • k & 07 извлекает 3 наименее значимых битов (так как 07 восьмеричное или 7 десятичное в двоичное 111), не обращая внимания на остальные ,
  • сдвигает значение 1 влево на 0..7 бит в зависимости от значения k & 07, производя одно из двоичных значений:

    0000 0001 
    0000 0010 
    0000 0100 
    0000 1000 
    0001 0000 
    0010 0000 
    0100 0000 
    1000 0000 
    

    Формально это на самом деле int значение, и, следовательно, возможно, имеет 32 бита, но биты верхнего порядка - все нули.

  • Оператор ~ преобразует каждый бит 0 в 1 и каждый 1 бит в 0.

  • The & оператора сочетает в себе два значения, получая 1 бит, где оба бита равны 1 и 0, где один или оба биты равны 0.
  • оператор | сочетает в себе два значения, получая 0 бит, где оба бита равны 0 и 1, где один или оба бита равны 1.
  • операторы присваивания |= и &= применить операнд на РИТ, чтобы переменная на LHS. Обозначение a |= b; эквивалентно a = a | b;, за исключением того, что a оценивается только один раз. Эта деталь здесь не имеет значения; это имеет значение интенсивно, если в выражении a есть приращение или что-то подобное.

Собираем все вместе:

  • SETBIT устанавливает kй бит (то есть устанавливает его в 1) в массиве 8-битовых значений, представленных A.
  • CLRBIT сбрасывает kth бит (значение устанавливает его на 0) в массиве 8-битных значений, представленных A.
  • GETBIT находит значение в бите kй в массиве 8-битовых значений, представленных A, и возвращает его в качестве либо 0 или 1 - это то, что конечный >> (k & 07) делает.

Номинально, элементы массива должны быть unsigned char, чтобы избежать проблем со значениями и неиспользуемого пространства, но любой интегральный тип может быть использован, более или менее расточительно. Вы получите интересные результаты, если тип signed char, а высокие биты установлены на значениях, или если тип равен char, а обычный char - это подписанный тип. Вы также можете получить интересные результаты от GETBIT, если тип A является целым типом, большим, чем char, а значения в массиве имеют биты, установленные за последние (наименее значимые) 8 бит числа.

Что макросы не делают

макросов, предоставленных профессором является предметным уроком в том, как не писать C препроцессор макросы. Они не учат вас писать хороший C; они учат, как писать ужасно ужасно C.

Каждый из этих макросов опасно сломан, потому что аргумент k не заключен в круглые скобки при его использовании. Нетрудно утверждать, что то же самое относится и к A. Использование 01 и 07 не совсем ошибочно, но октал 01 и 07 такие же, как десятичные 1 и 7.

Макрос GETBIT также нуждается в дополнительном уровне круглых скобок вокруг всего его тела. Учитывая

int y = 2; 
unsigned char array[32] = "abcdefghijklmnopqrstuvwxyz"; 

, то это не компилируется:

int x = GETBIT(array + 3, y + 2) + 13; 

Это компилировать (с предупреждениями), если ваши опции компилятора достаточно слабые, но будет производить эксцентричный результат:

int x = GETBIT(3 + array, y + 2) + 13; 

и это до того, как мы попытаемся обсудить:

int x = GETBIT(3 + array, y++) + 13; 

В CLRBIT и SetBit макросов использовать фигурные скобки, что означает, что вы не можете написать:

if (GETBIT(array, 13)) 
    SETBIT(array, 27); 
else 
    CLRBIT(array, 19); 

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

макросов может быть написан как это (с сохранением структуры блока операторов для SETBIT и CLRBIT макросов):

#define SETBIT(A, k) do { (A)[(k) >> 3] |= (1 << ((k) & 7)); } while (0) 
#define CLRBIT(A, k) do { (A)[(k) >> 3] &= ~(1 << ((k) & 7)); } while (0) 
#define GETBIT(A, k) (((A)[(k) >> 3] & (1 << ((k) & 7))) >> ((k) & 7)) 

do { … } while (0) обозначения является стандартным методом в макросах, позволяет обойти проблему взлома if/else заявлений.

Макросы также можно переписать так, потому что задания являются выражениями:

#define SETBIT(A, k) ((A)[(k) >> 3] |= (1 << ((k) & 7))) 
#define CLRBIT(A, k) ((A)[(k) >> 3] &= ~(1 << ((k) & 7))) 
#define GETBIT(A, k) (((A)[(k) >> 3] & (1 << ((k) & 7))) >> ((k) & 7)) 

Или, еще лучше, так как static inline функции, как это:

static inline void SETBIT(unsigned char *A, int k) { A[k >> 3] |= (1 << (k & 7)); } 
static inline void CLRBIT(unsigned char *A, int k) { A[k >> 3] &= ~(1 << (k & 7)); } 
static inline int GETBIT(unsigned char *A, int k) { return (A[k >> 3] & (1 << (k & 7))) >> (k & 7); } 

Все могут быть собраны в простой программа испытаний:

#if MODE == 1 

/* As provided */ 
#define SETBIT(A, k) { A[k >> 3] |= (01 << (k & 07)); } 
#define CLRBIT(A, k) { A[k >> 3] &= ~(01 << (k & 07)); } 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 

#elif MODE == 2 

/* As rewritten */ 
#define SETBIT(A, k) do { (A)[(k) >> 3] |= (1 << ((k) & 7)); } while (0) 
#define CLRBIT(A, k) do { (A)[(k) >> 3] &= ~(1 << ((k) & 7)); } while (0) 
#define GETBIT(A, k) (((A)[(k) >> 3] & (1 << ((k) & 7))) >> ((k) & 7)) 

#else 

/* As rewritten */ 
static inline void SETBIT(unsigned char *A, int k) { A[k >> 3] |= (1 << (k & 7)); } 
static inline void CLRBIT(unsigned char *A, int k) { A[k >> 3] &= ~(1 << (k & 7)); } 
static inline int GETBIT(unsigned char *A, int k) { return (A[k >> 3] & (1 << (k & 7))) >> (k & 7); } 

#endif 

int main(void) 
{ 
    int y = 2; 
    unsigned char array[32] = "abcdefghijklmnopqrstuvwxyz"; 
    int x = GETBIT(array + 3, y + 2) + 13; 
    int z = GETBIT(3 + array, y + 2) + 13; 

    if (GETBIT(array, 3)) 
     SETBIT(array, 22); 
    else 
     CLRBIT(array, 27); 

    return x + z; 
} 

При составлении с -DMODE=2 или -DMODE=0 или без каких-либо настроек -DMODE, тогда он чист. Когда скомпилировано -DMODE=1, есть недопустимое количество предупреждений (ошибки для меня, потому что я использую GCC и компилирую с -Werror, что делает любое предупреждение в ошибке).

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -DMODE=0 bits23.c -o bits23 
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -DMODE=2 bits23.c -o bits23 
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -DMODE=1 bits23.c -o bits23 
bits23.c: In function ‘main’: 
bits23.c:28:33: error: suggest parentheses around ‘+’ inside ‘>>’ [-Werror=parentheses] 
    int x = GETBIT(array + 3, y + 2) + 13; 
           ^
bits23.c:6:25: note: in definition of macro ‘GETBIT’ 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
         ^
bits23.c:6:24: error: subscripted value is neither array nor pointer nor vector 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
         ^
bits23.c:28:13: note: in expansion of macro ‘GETBIT’ 
    int x = GETBIT(array + 3, y + 2) + 13; 
      ^
bits23.c:28:33: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses] 
    int x = GETBIT(array + 3, y + 2) + 13; 
           ^
bits23.c:6:43: note: in definition of macro ‘GETBIT’ 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
             ^
bits23.c:28:33: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses] 
    int x = GETBIT(array + 3, y + 2) + 13; 
           ^
bits23.c:6:57: note: in definition of macro ‘GETBIT’ 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
                 ^
bits23.c:29:33: error: suggest parentheses around ‘+’ inside ‘>>’ [-Werror=parentheses] 
    int z = GETBIT(3 + array, y + 2) + 13; 
           ^
bits23.c:6:25: note: in definition of macro ‘GETBIT’ 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
         ^
bits23.c:29:33: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses] 
    int z = GETBIT(3 + array, y + 2) + 13; 
           ^
bits23.c:6:43: note: in definition of macro ‘GETBIT’ 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
             ^
bits23.c:29:22: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses] 
    int z = GETBIT(3 + array, y + 2) + 13; 
        ^
bits23.c:6:23: note: in definition of macro ‘GETBIT’ 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
        ^
bits23.c:29:33: error: suggest parentheses around ‘+’ in operand of ‘&’ [-Werror=parentheses] 
    int z = GETBIT(3 + array, y + 2) + 13; 
           ^
bits23.c:6:57: note: in definition of macro ‘GETBIT’ 
#define GETBIT(A, k) (A[k >> 3] & (01 << (k & 07))) >> (k & 07) 
                 ^
bits23.c:29:38: error: suggest parentheses around ‘+’ inside ‘>>’ [-Werror=parentheses] 
    int z = GETBIT(3 + array, y + 2) + 13; 
            ^
bits23.c:33:5: error: ‘else’ without a previous ‘if’ 
    else 
    ^
cc1: all warnings being treated as errors 
$ 
+0

Thank you так много для этого очень подробного объяснения, и особенно для объяснения, почему это не лучшая практика и как ее можно улучшить! Серьезно, хотелось бы, чтобы я дважды голосовал. –

Смежные вопросы