ビット演算とその実践的な使い方 | C言語

C言語では、四則演算以外にビット単位での演算も行うことができます。そのとき用いるのがビット演算子で、デジタル回路のAND回路やOR回路と同じ働きをします。

スクロールできます
演算子記述例内容
&A & B論理積(AND)。ビットが共に1なら1、そうでなければ0です。
|A | B論理和(OR)。いずれかのビットが1なら1、そうでなければ0です。
^A ^ B排他的論理和(XOR)。異なる値なら1、同じ値なら0です。
~~A否定(NOT)。ビットを反転します。
<< A << B左シフト。Bビット分左にシフトし、空いたビットは0で埋めます。
>> B >> B右シフト。Bビット分右にシフトし、空いたビットは0で埋めます。
  • ビット演算は符号なし整数に対してのみ行ってください。符号あり整数へのビット演算は処理系依存のため、誤動作する恐れがあります。

以降のサンプルコードに出てくる「uint8_t」などについては、下記ページの幅指定整数型を参照ください。

目次

論理積(AND)

論理積はビットが共に1なら1、そうでなければ0とする演算処理です。

AB結果
000
010
100
111
uint8_t a = 0x35;
uint8_t b = 0xC3;
uint8_t c;

// a = 00110101
// b = 11000011
// c = 00000001 => 0x01
c = a & b;

論理和(OR)

論理和はいずれかのビットが1なら1、そうでなければ0とする処理です。

AB結果
000
011
101
111
uint8_t a = 0x35;
uint8_t b = 0xC3;
uint8_t c;

// a = 00110101
// b = 11000011
// c = 11110111 => 0xF7
c = a | b;

排他的論理和(XOR)

排他的論理和は異なる値なら1、同じ値なら0とする処理です。主にビット値を反転するときに使用します。

AB結果
000
011
101
110
uint8_t a = 0x35;
uint8_t b = 0xC3;
uint8_t c;

// a = 00110101
// b = 11000011
// c = 11110110 => 0xF6
c = a ^ b;

否定(NOT)

否定はビットを反転する処理です。この場合の反転とは、1なら0、0なら1にすることを表します。

A結果
01
10
uint8_t a = 0x35;
uint8_t c;

// a = 00110101
// c = 11001010 => 0xCA
c = ~a;

左シフト

指定したビット分左にシフトし、空いたビットは0で埋める処理です。

uint8_t a = 0x35;
uint8_t c;

// a = 00110101
// c = 11010100 => 0xD4
c = a << 2;
  • 「1 << n」で左シフトできるのは、int型のサイズの範囲です。long型の領分まで左シフトする場合は「1UL << n」としてください。

右シフト

指定したビット分右にシフトし、空いたビットは0で埋める処理です。

uint8_t a = 0x35;
uint8_t c;

// a = 00110101
// c = 00000110 => 0x06
c = a >> 3;

実践的な使用例

組み込みソフト開発の分野では、レジスターやI/Oポートの制御などビット演算を多用します。その基本的な処理方法として、下記のことは覚えておくべきです。

#define BIT_N (1U << N)  // N=0~7

uint8_t b;
uint8_t status;

// Nビット目の値を取得する
b = (status >> N) & 1U;

// Nビット目を1にする
status |= BIT_N;

// Nビット目を0にする
status &= (uint8_t)~BIT_N;

// Nビット目を反転する
status ^= BIT_N;

偉い人の考えたビット演算

ぱっと見では何をやりたいのかわからないし、よく考えてもやっぱりわからない、そんなビット演算による高速処理です。

ビットの並びを左右反転する

CRCやFFTなどで出番のある、ビットの並びを左右反転する処理です。

[bitwise.c]

/** ----------------------------------------------------------------------------
 * @brief ビットの並びを左右反転する (8bit)
 * @param data : データ
 * @return 反転後のデータ
 */
uint8_t Reverse_Bit8(uint8_t data)
{
  data = ((uint8_t)(data & 0x55) << 1) | ((data >> 1) & 0x55);
  data = ((uint8_t)(data & 0x33) << 2) | ((data >> 2) & 0x33);

  return ((uint8_t)(data & 0x0f) << 4) | ((data >> 4) & 0x0f);
}

/** ----------------------------------------------------------------------------
 * @brief ビットの並びを左右反転する (16bit)
 * @param data : データ
 * @return 反転後のデータ
 */
uint16_t Reverse_Bit16(uint16_t data)
{
  data = ((uint16_t)(data & 0x5555) << 1) | ((data >> 1) & 0x5555);
  data = ((uint16_t)(data & 0x3333) << 2) | ((data >> 2) & 0x3333);
  data = ((uint16_t)(data & 0x0f0f) << 4) | ((data >> 4) & 0x0f0f);

  return ((uint16_t)(data & 0x00ff) << 8) | ((data >> 8) & 0x00ff);
}

/** ----------------------------------------------------------------------------
 * @brief ビットの並びを左右反転する (32bit)
 * @param data : データ
 * @return 反転後のデータ
 */
uint32_t Reverse_Bit32(uint32_t data)
{
  data = ((uint32_t)(data & 0x55555555) << 1) | ((data >> 1) & 0x55555555);
  data = ((uint32_t)(data & 0x33333333) << 2) | ((data >> 2) & 0x33333333);
  data = ((uint32_t)(data & 0x0f0f0f0f) << 4) | ((data >> 4) & 0x0f0f0f0f);
  data = ((uint32_t)(data & 0x00ff00ff) << 8) | ((data >> 8) & 0x00ff00ff);

  return ((uint32_t)(data & 0x0000ffff) << 16) | ((data >> 16) & 0x0000ffff);
}

1のビットをカウントする

ボタン、リレーやその他の状態などONとなっている数を数えたいときに使用する処理です。

[bitwise.c]

#include <stdint.h>

/** ----------------------------------------------------------------------------
 * @brief 1のビットをカウントする (8bit)
 * @param data : データ
 * @return 1のビット数
 */
uint8_t Count_Bit8(uint8_t data)
{
  data = (data & 0x55) + ((data >> 1) & 0x55);
  data = (data & 0x33) + ((data >> 2) & 0x33);

  return (data & 0x0f) + ((data >> 4) & 0x0f);
}

/** ----------------------------------------------------------------------------
 * @brief 1のビットをカウントする (16bit)
 * @param data : データ
 * @return 1のビット数
 */
uint8_t Count_Bit16(uint16_t data)
{
  data = (data & 0x5555) + ((data >> 1) & 0x5555);
  data = (data & 0x3333) + ((data >> 2) & 0x3333);
  data = (data & 0x0f0f) + ((data >> 4) & 0x0f0f);

  return ((data & 0x00ff) + ((data >> 8) & 0x00ff));
}

/** ----------------------------------------------------------------------------
 * @brief 1のビットをカウントする (32bit)
 * @param data : データ
 * @return 1のビット数
 */
uint8_t Count_Bit32(uint32_t data)
{
  data = (data & 0x55555555) + ((data >> 1) & 0x55555555);
  data = (data & 0x33333333) + ((data >> 2) & 0x33333333);
  data = (data & 0x0f0f0f0f) + ((data >> 4) & 0x0f0f0f0f);
  data = (data & 0x00ff00ff) + ((data >> 8) & 0x00ff00ff);

  return ((data & 0x0000ffff) + ((data >> 16) & 0x0000ffff));
}

サンプルコードの使用例

使用例代わりに、CppUTestによるテストコードを掲載しておきます。

#include "CppUTest/TestHarness.h"
#include "bitwise.h"

// clang-format off
TEST_GROUP(bitwise){
  TEST_SETUP(){
  }

  TEST_TEARDOWN(){
  }
};
// clang-format on

TEST(bitwise, Test_Reverse_Bit8)
{
  uint8_t reverse;

  reverse = Reverse_Bit8(0x00);
  UNSIGNED_LONGS_EQUAL(reverse, 0x00);

  reverse = Reverse_Bit8(0xff);
  UNSIGNED_LONGS_EQUAL(reverse, 0xff);

  reverse = Reverse_Bit8(0x01);
  UNSIGNED_LONGS_EQUAL(reverse, 0x80);

  reverse = Reverse_Bit8(0x12);
  UNSIGNED_LONGS_EQUAL(reverse, 0x48);
}

TEST(bitwise, Test_Reverse_Bit16)
{
  uint16_t reverse;

  reverse = Reverse_Bit16(0x0000);
  UNSIGNED_LONGS_EQUAL(reverse, 0x0000);

  reverse = Reverse_Bit16(0xffff);
  UNSIGNED_LONGS_EQUAL(reverse, 0xffff);

  reverse = Reverse_Bit16(0x0001);
  UNSIGNED_LONGS_EQUAL(reverse, 0x8000);

  reverse = Reverse_Bit16(0x1234);
  UNSIGNED_LONGS_EQUAL(reverse, 0x2c48);
}

TEST(bitwise, Test_Reverse_Bit32)
{
  uint32_t reverse;

  reverse = Reverse_Bit32(0x00000000);
  UNSIGNED_LONGS_EQUAL(reverse, 0x00000000);

  reverse = Reverse_Bit32(0xffffffff);
  UNSIGNED_LONGS_EQUAL(reverse, 0xffffffff);

  reverse = Reverse_Bit32(0x00000001);
  UNSIGNED_LONGS_EQUAL(reverse, 0x80000000);

  reverse = Reverse_Bit32(0x12345678);
  UNSIGNED_LONGS_EQUAL(reverse, 0x1e6a2c48);
}

TEST(bitwise, Test_Count_Bit8)
{
  uint8_t count;

  count = Count_Bit8(0x00);
  UNSIGNED_LONGS_EQUAL(count, 0);

  count = Count_Bit8(0x01);
  UNSIGNED_LONGS_EQUAL(count, 1);

  count = Count_Bit8(0x12);
  UNSIGNED_LONGS_EQUAL(count, 2);

  count = Count_Bit8(0xff);
  UNSIGNED_LONGS_EQUAL(count, 8);
}

TEST(bitwise, Test_Count_Bit16)
{
  uint8_t count;

  count = Count_Bit16(0x0000);
  UNSIGNED_LONGS_EQUAL(count, 0);

  count = Count_Bit16(0x0001);
  UNSIGNED_LONGS_EQUAL(count, 1);

  count = Count_Bit16(0x1234);
  UNSIGNED_LONGS_EQUAL(count, 5);

  count = Count_Bit16(0xffff);
  UNSIGNED_LONGS_EQUAL(count, 16);
}

TEST(bitwise, Test_Count_Bit32)
{
  uint8_t count;

  count = Count_Bit32(0x00000000);
  UNSIGNED_LONGS_EQUAL(count, 0);

  count = Count_Bit32(0x00000001);
  UNSIGNED_LONGS_EQUAL(count, 1);

  count = Count_Bit32(0x12345678);
  UNSIGNED_LONGS_EQUAL(count, 13);

  count = Count_Bit32(0xffffffff);
  UNSIGNED_LONGS_EQUAL(count, 32);
}

なお「bitwise.h」は下記のとおりです。

[bitwise.h]

#ifndef BITWISE_H
#define BITWISE_H

#ifdef __cplusplus
extern "C" {
#endif

//------------------------------------------------------------------------------
// include
//------------------------------------------------------------------------------
#include <stdint.h>

//------------------------------------------------------------------------------
// function
//------------------------------------------------------------------------------
uint8_t  Reverse_Bit8(uint8_t data);
uint16_t Reverse_Bit16(uint16_t data);
uint32_t Reverse_Bit32(uint32_t data);

uint8_t Count_Bit8(uint8_t data);
uint8_t Count_Bit16(uint16_t data);
uint8_t Count_Bit32(uint32_t data);

#ifdef __cplusplus
}
#endif

#endif
目次