Главная страница
Навигация по странице:

  • Поразрядные операторы И, ИЛИ, исключающее ИЛИ и НЕ

  • ВАЖНО! 7.8. Операторы сдвига

  • Сдвиг влево переменной i на 1 позицию. } cout > 1; // ← Сдвиг вправо переменной i на 1 позицию.

  • Проект 7.1. Создание функций поразрядного циклического сдвига

  • Последовательность действий 1.

  • Проект 7.1 Создание функций поразрядного циклического сдвига 334

  • Справочник по C, Полный справочник по C


    Скачать 393.9 Kb.
    НазваниеСправочник по C, Полный справочник по C
    Анкорaboba
    Дата09.07.2021
    Размер393.9 Kb.
    Формат файлаpdf
    Имя файлаcrukovodstvodlyanachinayushchih.pdf
    ТипСправочник
    #223848
    страница4 из 5
    1   2   3   4   5

    ВАЖНО!
    7.7.
    Поразрядные операторы
    Поскольку язык C++ сориентирован так, чтобы позволить полный доступ к аппаратным средствам компьютера, важно, чтобы он имел возможность непо-

    С++: руководство для начинающих
    323
    Еще о типах данных и операторах
    7
    средственно воздействовать на отдельные биты в рамках байта или машинного слова. Именно поэтому C++ и содержит поразрядные операторы. Поразрядные
    операторы предназначены для тестирования, установки или сдвига реальных битов в байтах или словах, которые соответствуют символьным или целочислен- ным C++-типам. Поразрядные операторы не используются для операндов типа bool
    , float, double, long double, void или других еще более сложных типов данных. Поразрядные операторы (табл. 7.1) очень часто используются для реше- ния широкого круга задач программирования системного уровня, например, при опросе информации о состоянии устройства или ее формировании. Теперь рас- смотрим каждый оператор этой группы в отдельности.
    Вопросы для текущего контроля
    1. Перечисление — это список именованных ________ констант.
    2. Какое целочисленное значение по умолчанию имеет первый символ пере- числения?
    3. Покажите, как объявить идентификатор BigInt, чтобы он стал еще од- ним именем для типа long int.*
    Таблица 7.1. Поразрядные операторы
    Оператор Значение
    &
    Поразрядное И (AND)
    |
    Поразрядное ИЛИ (OR)
    ^
    Поразрядное исключающее ИЛИ (XOR)


    Дополнение до 1 (унарный оператор НЕ)
    >>
    Сдвиг вправо
    <<
    Сдвиг влево
    Поразрядные операторы И, ИЛИ,
    исключающее ИЛИ и НЕ
    Поразрядные операторы И, ИЛИ, исключающее ИЛИ и НЕ (обозначаемые символами &, |, ^ и соответственно) выполняют те же операции, что и их ло-
    1. Перечисление — это список именованных целочисленных констант.
    2. По умолчанию первый символ перечисления имеет значение 0.
    3. typedef long int BigInt;.

    324
    Модуль 7. Еще о типах данных и операторах гические эквиваленты (т.е. они действуют согласно той же таблице истинности).
    Различие состоит лишь в том, что поразрядные операции работают на побитовой основе. В следующей таблице показан результат выполнения каждой поразряд- ной операции для всех возможных сочетаний операндов (нулей и единиц).
    p q
    p & q p | q p ^ q
    p
    0 0
    0 0
    0 1
    1 0
    0 1
    1 0
    0 1
    0 1
    1 1
    1 1
    1 1
    0 0
    Как видно из таблицы, результат применения оператора XOR (исключающее
    ИЛИ ) будет равен значению ИСТИНА (1) только в том случае, если истинен
    (равен значению 1) лишь один из операндов; в противном случае результат при- нимает значение ЛОЖЬ (0).
    Поразрядный оператор И можно представить как способ подавления битовой информации. Это значит, что 0 в любом операнде обеспечит установку в 0 соот- ветствующего бита результата. Вот пример.
    1101 0011
    & 1010 1010
    ----------
    1000 0010
    Использование оператора “&” демонстрируется в следующей программе. Она преобразует любой строчный символ в его прописной эквивалент путем установ- ки шестого бита равным значению 0. Набор символов ASCII определен так, что строчные буквы имеют почти такой же код, что и прописные, за исключением того, что код первых отличается от кода вторых ровно на 32. Следовательно, как показано в этой программе, чтобы из строчной буквы сделать прописную, доста- точно обнулить ее шестой бит.
    // Получение прописных букв с использованием поразрядного
    // оператора “И”.
    #include
    using namespace std;
    int main()
    {
    char ch;
    for(int i = 0 ; i < 10; i++) {

    С++: руководство для начинающих
    325
    Еще о типах данных и операторах
    7
    ch = 'a' + i; cout << ch;
    // Эта инструкция обнуляет 6-й бит.
    ch = ch & 223; // В переменной ch теперь
    // прописная буква. cout << ch << " ";
    } cout << "\n"; return 0;
    }
    Вот как выглядят результаты выполнения этой программы.
    aA bB cC dD eE fF gG hH iI jJ
    Значение 223, используемое в инструкции поразрядного оператора “И”, явля- ется десятичным представлением двоичного числа 1101 1111. Следовательно, эта операция “И” оставляет все биты в переменной ch нетронутыми, за исключением шестого (он сбрасывается в нуль).
    Оператор “И” также полезно использовать, если нужно определить, установ- лен интересующий вас бит (т.е. равен ли он значению 1) или нет. Например, при выполнении следующей инструкции вы узнаете, установлен ли 4-й бит в пере- менной status.
    if(status & 8) cout << "Бит 4 установлен";
    Чтобы понять, почему для тестирования четвертого бита используется чис- ло 8, вспомните, что в двоичной системе счисления число 8 представляется как
    0000 1000, т.е. в числе 8 установлен только четвертый разряд. Поэтому условное выражение инструкции if даст значение ИСТИНА только в том случае, если четвертый бит переменной status также установлен (равен 1). Интересное ис- пользование этого метода показано на примере функции disp_binary(). Она отображает в двоичном формате конфигурацию битов своего аргумента. Мы бу- дем использовать функцию show_binary() ниже в этой главе для исследова- ния возможностей других поразрядных операций.
    // Отображение конфигурации битов в байте.
    void show_binary(unsigned int u)
    {

    326
    Модуль 7. Еще о типах данных и операторах int t; for(t=128; t > 0; t = t/2) if(u & t) cout << "1 "; else cout << "0 "; cout << "\n";
    }
    Функция show_binary(), используя поразрядный оператор “И”, последова- тельно тестирует каждый бит младшего байта переменной u, чтобы определить, установлен он или сброшен. Если он установлен, отображается цифра 1, в про- тивном случае — цифра 0.
    Поразрядный оператор “ИЛИ” (в противоположность поразрядному “И”) удобно использовать для установки нужных битов равными единице. При вы- полнении операции “ИЛИ” наличие в любом операнде бита, равного 1, означает, что в результате соответствующий бит также будет равен единице. Вот пример.
    1101 0011
    | 1010 1010
    ----------
    1111 1011
    Оператор “ИЛИ” можно использовать для превращения рассмотренной выше программы (которая преобразует строчные символы в их прописные эквивален- ты) в ее “противоположность”, т.е. теперь, как показано ниже, она будет преоб- разовывать прописные буквы в строчные.
    // Получение строчных букв с использованием поразрядного
    // оператора “ИЛИ”.
    #include using namespace std; int main()
    { char ch; for(int i = 0 ; i < 10; i++) { ch = 'A' + i; cout << ch;

    С++: руководство для начинающих
    327
    Еще о типах данных и операторах
    7
    // Эта инструкция делает букву строчной,
    // устанавливая ее 6-й бит. ch = ch | 32; // В переменной ch теперь
    // строчная буква. cout << ch << " ";
    } cout << "\n"; return 0;
    }
    Вот результаты выполнения этой программы.
    Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj
    Итак, вы убедились, что установка шестого бита превращает прописную букву в ее строчный эквивалент.
    Поразрядное исключающее “ИЛИ” (XOR ) устанавливает бит результата рав- ным единице только в том случае, если соответствующие биты операндов отли- чаются один от другого, т.е. не равны. Вот пример:
    0111 1111
    ^ 1011 1001
    ----------
    1100 0110
    Оператор “исключающее ИЛИ” (XOR) обладает интересным свойством, кото- рое дает нам простой способ кодирования сообщений. Если применить операцию
    “исключающее ИЛИ” к некоторому значению X и заранее известному значению
    Y, а затем проделать то же самое с результатом предыдущей операции и значени- ем Y, то мы снова получим значение X. Это означает, что после выполнения этих операций
    R1 = X ^ Y;
    R2 = R1 ^ Y;
    R2 будет иметь значение X. Таким образом, результат последовательного выпол- нения двух операций “XOR” с использованием одного и того же значения дает исходного значение. Этот принцип можно применить для создания простой шиф- ровальной программы, в которой некоторое целое число используется в качестве ключа для шифрования и дешифровки сообщения путем выполнения операции
    XOR над символами этого сообщения. Первый раз мы используем операцию
    XOR, чтобы закодировать сообщение, а после второго ее применения мы получа-

    328
    Модуль 7. Еще о типах данных и операторах ем исходное (декодированное) сообщение. Рассмотрим простой пример исполь- зования этого метода для шифрования и дешифровки короткого сообщения.
    // Использование оператора XOR для кодирования и
    // декодирования сообщения.
    #include using namespace std; int main()
    { char msg[] = "Это простой тест"; char key = 88; cout << "Исходное сообщение: " << msg << "\n"; for(int i = 0 ; i < strlen(msg); i++) msg[i] = msg[i] ^ key; cout << "Закодированное сообщение: " << msg << "\n"; for(int i = 0 ; i < strlen(msg); i++) msg[i] = msg[i] ^ key; cout << "Декодированное сообщение: " << msg << "\n"; return 0;
    }
    Эта программа генерирует такие результаты.
    Исходное сообщение: Это простой тест
    Закодированное сообщение: +¦Ўxў¬Ў¦¦Ўёx¦¤¦¦
    Декодированное сообщение: Это простой тест
    Унарный оператор “НЕ” (или оператор дополнения до 1 ) инвертирует состоя- ние всех битов своего операнда. Например, если целочисленное значение (храни- мое в переменной A) представляет собой двоичный код 1001 0110, то в результате операции A получим двоичный код 0110 1001.
    В следующей программе демонстрируется использование оператора “НЕ” по- средством отображения некоторого числа и его дополнения до 1 в двоичном коде с помощью приведенной выше функции show_binary().

    С++: руководство для начинающих
    329
    Еще о типах данных и операторах
    7
    #include
    using namespace std;
    void show_binary(unsigned int u);
    int main()
    {
    unsigned u;
    cout << "Введите число между 0 и 255: ";
    cin >> u;
    cout << "Исходное число в двоичном коде: ";
    show_binary(u);
    cout << "Его дополнение до единицы: ";
    show_binary(u);
    return 0;
    }
    // Отображение битов, составляющих байт.
    void show_binary(unsigned int u)
    {
    register int t;
    for(t=128; t>0; t = t/2)
    if(u & t) cout << "1 ";
    else cout << "0 ";
    cout << "\n";
    }
    Вот как выглядят результаты выполнения этой программы.
    Введите число между 0 и 255: 99
    Исходное число в двоичном коде: 0 1 1 0 0 0 1 1
    Его дополнение до единицы: 1 0 0 1 1 1 0 0
    Операторы &, | и применяются непосредственно к каждому биту значения в отдельности. Поэтому поразрядные операторы нельзя использовать вместо их

    330
    Модуль 7. Еще о типах данных и операторах логических эквивалентов в условных выражениях. Например, если значение x равно 7, то выражение x && 8 имеет значение ИСТИНА, в то время как выраже- ние x & 8 дает значение ЛОЖЬ.
    ВАЖНО!
    7.8.
    Операторы сдвига
    Операторы сдвига, “>>” и “<<”, сдвигают все биты в значении переменной вправо или влево. Общий формат использования оператора сдвига вправо вы- глядит так.
    переменная >> число_битов
    А оператор сдвига влево используется так.
    переменная << число_битов
    Здесь элемент число_битов указывает, на сколько позиций должно быть сдви- нуто значение элемента переменная. При каждом сдвиге влево все биты, со- ставляющие значение, сдвигаются влево на одну позицию, а в младший разряд записывается нуль. При каждом сдвиге вправо все биты сдвигаются, соответ- ственно, вправо. Если сдвигу вправо подвергается значение без знака, в старший разряд записывается нуль. Если же сдвигу вправо подвергается значение со зна- ком, значение знакового разряда сохраняется. Как вы помните, отрицательные целые числа представляются установкой старшего разряда числа равным едини- це. Таким образом, если сдвигаемое значение отрицательно, при каждом сдвиге вправо в старший разряд записывается единица, а если положительно — нуль. Не забывайте: сдвиг, выполняемый операторами сдвига, не является циклическим, т.е. при сдвиге как вправо, так и влево крайние биты теряются, и содержимое по- терянного бита узнать невозможно.
    Операторы сдвига работают только со значениями целочисленных типов, напри- мер, символами, целыми числами и длинными целыми числами (int, char, long int или short int). Они не применимы к значениям с плавающей точкой.
    Побитовые операции сдвига могут оказаться весьма полезными для декоди- рования входной информации, получаемой от внешних устройств (например, цифроаналоговых преобразователей), и обработки информации о состоянии устройств. Поразрядные операторы сдвига можно также использовать для вы- полнения ускоренных операций умножения и деления целых чисел. С помощью сдвига влево можно эффективно умножать на два, сдвиг вправо позволяет не ме- нее эффективно делить на два.
    Следующая программа наглядно иллюстрирует результат использования опе- раторов сдвига.

    С++: руководство для начинающих
    331
    Еще о типах данных и операторах
    7
    // Демонстрация выполнения поразрядного сдвига.
    #include
    using namespace std;
    void show_binary(unsigned int u);
    int main()
    { int i=1, t;
    // Сдвиг влево.
    for(t=0; t < 8; t++) { show_binary(i); i = i << 1; //
    Сдвиг влево переменной i на 1 позицию.
    } cout << "\n";
    // Сдвиг вправо.
    for(t=0; t < 8; t++) { i = i >> 1; //
    Сдвиг вправо переменной i на 1 позицию.
    show_binary(i);
    } return 0;
    }
    // Отображение битов, составляющих байт. void show_binary(unsigned int u)
    { int t; for(t=128; t>0; t=t/2) if(u & t) cout << "1 "; else cout << "0 "; cout << "\n";
    }

    332
    Модуль 7. Еще о типах данных и операторах
    Результаты выполнения этой программы таковы.
    0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1
    Вопросы для текущего контроля
    1. Какими символами обозначаются поразрядные операторы И, ИЛИ, ис- ключающее ИЛИ и НЕ?
    2. Поразрядный оператор работает на побитовой основе. Верно ли это?
    3. Покажите, как выполнить сдвиг значения переменной x влево на два разряда.*
    Проект_7.1.__Создание_функций_поразрядного_циклического_сдвига'>Проект 7.1.
    Создание функций поразрядного
    циклического сдвига
    Несмотря на то что язык C++ обеспечивает два оператора сдвига, в нем не определен оператор циклического сдвига. Оператор ци-
    1. Поразрядные операторы обозначаются такими символами: &, |, ^ и .
    2. Верно, поразрядный оператор работает на побитовой основе.
    3. x << 2
    rotate.cpp

    С++: руководство для начинающих
    333
    Еще о типах данных и операторах
    7
    клического сдвига отличается от оператора обычного сдвига тем, что бит, выдвигае- мый с одного конца, “вдвигается” в другой. Таким образом, выдвинутые биты не те- ряются, а просто перемещаются “по кругу”. Возможен циклический сдвиг как вправо, так и влево. Например, значение 1010 0000 после циклического сдвига влево на один разряд даст значение 0100 0001, а после сдвига вправо — число 0101 0000. В каждом случае бит, выдвинутый с одного конца, “вдвигается” в другой. Хотя отсутствие опе- раторов циклического сдвига может показаться упущением, на самом деле это не так, поскольку их очень легко создать на основе других поразрядных операторов.
    В этом проекте предполагается создание двух функций: rrotate() и lro- tate()
    , которые сдвигают байт вправо или влево. Каждая функция принима- ет два параметра и возвращает результат. Первый параметр содержит значение, подлежащее сдвигу, второй — количество сдвигаемых разрядов. Этот проект включает ряд действий над битами и отображает возможность применения по- разрядных операторов.
    Последовательность действий
    1. Создайте файл с именем rotate.cpp.
    2. Определите функцию lrotate(), предназначенную для выполнения ци- клического сдвига влево.
    // Функция циклического сдвига влево байта на n разрядов. unsigned char lrotate(unsigned char val, int n)
    { unsigned int t; t = val; for(int i=0; i < n; i++) { t = t << 1;
    /* Если выдвигаемый бит (8-й разряд значения t) содержит единицу, то устанавливаем младший бит. */ if(t & 256) t = t | 1; // Устанавливаем 1 на правом конце.
    } return t; // Возвращаем младшие 8 бит.
    }
    Проект
    7.1
    Создание функций поразрядного циклического сдвига

    334
    Модуль 7. Еще о типах данных и операторах
    Вот как работает функция lrotate(). Ей передается значение, которое нужно сдвинуть, в параметре val, и количество разрядов, на которое нуж- но сдвинуть, в параметре n. Функция присваивает значение параметра val переменной t, которая имеет тип unsigned int. Необходимость присва- ивания unsigned char-значения переменной типа unsigned int об- условлена тем, что в этом случае мы не теряем биты, выдвинутые влево.
    Дело в том, что бит, выдвинутый влево из байтового значения, просто ста- новится восьмым битом целочисленного значения (поскольку его длина больше длины байта). Значение этого бита можно затем скопировать в ну- левой бит байтового значения, тем самым выполнив циклический сдвиг.
    В действительности циклический сдвиг выполняется следующим образом.
    Настраивается цикл на количество итераций, равное требуемому числу сдвигов. В теле цикла значение переменной t сдвигается влево на один разряд. При этом справа будет “вдвинут” нуль. Но если значение восьмого бита результата (т.е. бита, который был выдвинут из байтового значения) равно единице, то и нулевой бит необходимо установить равным 1. В про- тивном случае нулевой бит остается равным 0.
    Значение восьмого бита тестируется с использованием if-инструкции:
    if(t & 256)
    Значение 256 представляет собой десятичное значение, в котором установ- лен только 8-й бит. Таким образом, выражение t & 256 будет истинным лишь в случае, если в переменной t 8-й бит равен единице.
    После выполнения циклического сдвига функция lrotate() возвращает значение t. Но поскольку она объявлена как возвращающая значение типа unsigned char
    , то фактически вернутся только младшие 8 бит значения t.
    1   2   3   4   5


    написать администратору сайта