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」などについては、下記ページの幅指定整数型を参照ください。
整数型は単純なようで難しい | C言語
整数型は単純なようで処理系によって型のサイズが異なったり、演算では拡張されたりなどやっかいな性質をもっています。これらの性質を正しく理解したうえで使用しない…
目次
論理積(AND)
論理積はビットが共に1なら1、そうでなければ0とする演算処理です。
A | B | 結果 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
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とする処理です。
A | B | 結果 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
uint8_t a = 0x35;
uint8_t b = 0xC3;
uint8_t c;
// a = 00110101
// b = 11000011
// c = 11110111 => 0xF7
c = a | b;
排他的論理和(XOR)
排他的論理和は異なる値なら1、同じ値なら0とする処理です。主にビット値を反転するときに使用します。
A | B | 結果 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
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 | 結果 |
---|---|
0 | 1 |
1 | 0 |
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
CppUTestでC/C++コードをテストする
CppUTestは、単体テストを自動化するためのフリーソフトウェアです。 C/C++は非常に自由度が高く何でもできる反面、ちょっとしたミスですぐに不具合を起こします。この…