C++のxorについて

AI実装検定のご案内

C++のxorは、ビット単位の排他的論理和(XOR)を行うための演算子です。

一般的には記号の^が使われますが、C++ではxorという代替表記も用意されています。

a ^ b

と、

a xor b

は同じ意味です。

XORは「Exclusive OR」の略で、日本語では「排他的論理和」と呼ばれます。

2つのビットを比較し、異なっていれば1、同じであれば0になるのが特徴です。

ABA XOR B
000
011
101
110
目次

C++でxorを使う基本的な方法

^を使ってXORを計算する

C++では、通常は^を使ってXORを記述します。

#include <iostream>

int main() {
    int a = 5;
    int b = 3;

    int result = a ^ b;

    std::cout << result << '\n';
}

実行結果は次のとおりです。

6

これは、53を2進数で表すと理解しやすくなります。

5 = 0101
3 = 0011

各ビットにXORを適用すると、

  0101
^ 0011
------
  0110

となります。

0110は10進数では6なので、5 ^ 3の結果は6です。

xorを使って書くこともできる

同じ処理は、xorを使って次のようにも書けます。

#include <iostream>

int main() {
    int a = 5;
    int b = 3;

    int result = a xor b;

    std::cout << result << '\n';
}

結果は^を使った場合と同じです。

6

C++ではxor^の代替トークンとして定義されているため、機能上の違いはありません。

C++では^とxorのどちらを使うべきか

一般的には^が使われる

実際のC++コードでは、

a ^ b

という書き方が一般的です。

xorでも問題なく動作しますが、多くのC++プログラムや技術資料では^が使用されています。

そのため、特別な理由がなければ^を使っておくと、ほかのプログラマーにも意図が伝わりやすいでしょう。

xorはC++の正式な代替表記

C++には、記号で表される一部の演算子について、文字による代替表記があります。

代表例は次のとおりです。

記号代替表記
&&and
`
!not
&bitand
``
^xor
~compl
!=not_eq
&=and_eq
`=`
^=xor_eq

たとえば、

if (a > 0 and b > 0) {
}

は、

if (a > 0 && b > 0) {
}

と同じ意味です。

同様に、

a xor b

は、

a ^ b

と同じ意味になります。

XORの基本的な性質

同じ値をXORすると0になる

XORには、

a ^ a == 0

という性質があります。

たとえば、

0101
0101
----
0000

となるため、

5 ^ 5

の結果は0です。

0とXORすると元の値になる

次の式では、

a ^ 0

結果は必ずaになります。

たとえば、

5 ^ 0

の結果は5です。

これはXORで0と比較したビットは変化しないためです。

同じ値で2回XORすると元に戻る

XORには、

(a ^ b) ^ b == a

という性質もあります。

たとえば、

int a = 5;
int b = 3;

int c = a ^ b;
int d = c ^ b;

とすると、dは再び5になります。

この性質は、データ変換やアルゴリズムなどで利用されることがあります。

XORには交換法則と結合法則がある

XORでは、値の順番を入れ替えても結果は変わりません。

a ^ b == b ^ a

また、

(a ^ b) ^ c == a ^ (b ^ c)

も成り立ちます。

この性質によって、複数の整数を順番にXORするアルゴリズムを簡潔に記述できます。

XORをビット操作で使う方法

特定のビットを反転する

XORは、特定のビットだけを反転させたい場合に便利です。

unsigned int value = 0b0101;
unsigned int mask  = 0b0010;

value ^= mask;

計算結果は次のようになります。

  0101
^ 0010
------
  0111

マスク側が1になっている位置だけが反転します。

XORでは、

0 XOR 0 = 0
1 XOR 0 = 1
0 XOR 1 = 1
1 XOR 1 = 0

となるため、マスクのビットが0なら元の値を維持し、1なら反転します。

この性質から、XORはビットのON・OFFを切り替えるトグル処理によく使われます。

同じマスクでもう一度XORすると元に戻る

たとえば、

unsigned int value = 0b0001;
unsigned int mask  = 0b0100;

value ^= mask;

とすると、

0101

になります。

もう一度、

value ^= mask;

を実行すると、

0001

に戻ります。

同じマスクによって繰り返しON・OFFを切り替えられるのがXORの特徴です。

AND・OR・XOR・NOTの違い

ビット演算子の主な用途

C++には、XOR以外にも複数のビット演算子があります。

操作演算子主な用途
AND&ビットの抽出・確認、クリア
OR``
XOR^特定ビットを反転する
NOT~全ビットを反転する

たとえば、特定のビットを立てたい場合はORを使います。

value |= mask;

特定のビットを反転したい場合はXORを使います。

value ^= mask;

特定のビットをクリアしたい場合には、反転したマスクとANDを組み合わせる方法があります。

value &= ~mask;

目的に応じて演算子を使い分けることが重要です。

XORとORの違い

両方が1の場合の結果が異なる

ORとXORは似ていますが、両方のビットが1の場合の結果が異なります。

ABORXOR
0000
0111
1011
1110

ORでは、

1 OR 1 = 1

ですが、XORでは、

1 XOR 1 = 0

です。

C++ではORを、

a | b

XORを、

a ^ b

と書きます。

XORとANDの違い

ANDは両方が1の場合だけ1になる

ANDでは、両方のビットが1の場合のみ結果が1になります。

ABANDXOR
0000
0101
1001
1110

C++ではANDを、

a & b

XORを、

a ^ b

と記述します。

ANDはビットの確認や抽出、XORはビットの反転などに向いています。

bool型でXORを使う方法

bool同士でも^を使用できる

bool型の値に対して^を使用すると、結果として論理XORのような動作になります。

#include <iostream>

int main() {
    bool a = true;
    bool b = false;

    bool result = a ^ b;

    std::cout << std::boolalpha << result << '\n';
}

実行結果は、

true

です。

一方だけがtrueであるため、XORの結果も真になります。

boolの^では整数昇格が行われる

注意したいのは、bool同士に^を使った場合、式そのものの型が必ずしもboolになるわけではない点です。

たとえば、

bool a = true;
bool b = false;

auto result = a ^ b;

とした場合、組み込みのビット演算では整数昇格が行われるため、resultintになります。

結果の値は0または1です。

次のようにboolへ代入すれば、

bool result = a ^ b;

0false1trueへ変換されます。

bool同士なら!=でも表現できる

2つのboolについて「値が異なっていればtrue」としたい場合は、

bool result = a != b;

と書くこともできます。

bool値に限れば、

a ^ b

と、

a != b

は真偽結果として同じになります。

ただし、

auto x = a ^ b;
auto y = a != b;

では、xintyboolになるため、式の型まで同じではありません。

単純な論理条件として使用する場合には、!=のほうが意図を読み取りやすいケースもあります。

C++には論理XOR専用演算子がない

^はビット単位XOR演算子

C++には、

&&

による論理ANDや、

||

による論理ORがあります。

しかし、これらに相当する論理XOR専用の演算子はありません。

^xorは、組み込み型では基本的にビット単位の排他的論理和を行う演算子です。

なお、組み込みの^では整数型だけでなく、スコープなし列挙型も対象になります。

xor_eqとは

xor_eqは^=の代替表記

C++には、^=の代替表記としてxor_eqも用意されています。

a ^= b;

と、

a xor_eq b;

は同じ意味です。

たとえば、

int a = 5;

a ^= 3;

と、

int a = 5;

a xor_eq 3;

は同じ計算を行います。

実際のコードでは^=のほうが一般的ですが、xor_eqも正式なC++構文です。

XORを使って重複していない値を探す

1つだけ異なる値を取得できる

XORの性質は、アルゴリズムでも利用されます。

たとえば、「すべての整数が2回ずつ登場し、1つだけ1回しか登場しない」という配列を考えます。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {4, 1, 2, 1, 2};

    int result = 0;

    for (int value : numbers) {
        result ^= value;
    }

    std::cout << result << '\n';
}

実行結果は、

4

です。

XORでは、

1 ^ 1 == 0
2 ^ 2 == 0

となります。

そのため、

4 ^ 1 ^ 2 ^ 1 ^ 2

は、交換法則や結合法則を利用すると、

4 ^ (1 ^ 1) ^ (2 ^ 2)

と考えられます。

結果として、

4 ^ 0 ^ 0

となり、4だけが残ります。

XORを使って値を交換する方法

一時変数なしで整数を交換できる

XORの性質を利用すると、一時変数を使わずに2つの整数を交換できます。

int a = 10;
int b = 20;

a ^= b;
b ^= a;
a ^= b;

処理後は、

a = 20
b = 10

となります。

実用コードではstd::swapを使う

XORによる値交換は仕組みを理解する例としては興味深いものの、通常のC++コードでは推奨されません。

現代のC++では、

#include <utility>

std::swap(a, b);

と書いたほうが読みやすく、意図も明確です。

また、XORによる交換は同じオブジェクトを交換しようとすると問題が発生します。

たとえば、

x ^= x;

を実行すると、x0になります。

そのため、実際のプログラムではstd::swapを利用するのが基本です。

XORによる可逆変換

同じ値でもう一度XORすると元に戻る

XORには、

(data ^ key) ^ key == data

という性質があります。

たとえば、

#include <iostream>

int main() {
    int data = 42;
    int key = 123;

    int transformed = data ^ key;
    int restored = transformed ^ key;

    std::cout << restored << '\n';
}

実行結果は、

42

です。

この性質によって、XORを使った可逆的なデータ変換ができます。

ただし、単純に固定値とXORするだけでは安全な暗号方式にはなりません。

セキュリティが必要な用途では、十分に検証された暗号アルゴリズムやライブラリを使用する必要があります。

^をべき乗と間違えないようにする

C++の^はべき乗演算子ではない

C++初心者が特に注意したいのが、^をべき乗演算子だと勘違いするケースです。

たとえば、

2 ^ 3

は「2の3乗」ではありません。

2進数にすると、

2 = 10
3 = 11

なので、

10 XOR 11 = 01

となり、結果は1です。

べき乗にはstd::powなどを使用する

べき乗を計算したい場合は、用途に応じてstd::powなどを利用します。

#include <cmath>

double result = std::pow(2.0, 3.0);

この場合、結果は8.0です。

ただし、整数だけで厳密な整数演算を行いたい場合には、用途に応じて整数向けの実装を検討することもあります。

xorを使うときの注意点

演算子の優先順位を意識する

ビット演算子を複数組み合わせる場合は、演算子の優先順位に注意が必要です。

たとえば、

int result = (a ^ b) & mask;

のように括弧を付けることで、どの演算を先に行うのか明確になります。

複雑な式では、演算子の優先順位を暗記するよりも、括弧を使って意図を明示したほうが読みやすいコードになります。

ビット操作ではunsigned型も検討する

ビットパターンそのものを扱う場合は、

unsigned int

などの符号なし整数型を利用すると、処理の意図を明確にしやすくなります。

たとえば、

unsigned int flags = 0b0010;
unsigned int mask  = 0b0100;

flags ^= mask;

のように記述できます。

特にシフト演算など、ほかのビット演算と組み合わせる場合は、符号付き整数と符号なし整数の違いも意識するとよいでしょう。

C++のxorを理解するポイント

C++のxorについて、まず覚えておきたいポイントは次のとおりです。

  • xor^の代替表記である
  • XORでは異なるビットが1、同じビットが0になる
  • a ^ a0になる
  • a ^ 0aになる
  • 同じ値を2回XORすると元の値に戻る
  • ^は特定ビットの反転に利用できる
  • xor_eq^=と同じ意味である
  • ^はべき乗演算子ではない

実際のC++コードでは、

a ^ b

という記号表記が一般的です。

一方、

a xor b

も正式なC++の構文であり、追加のヘッダーを読み込む必要はありません。

XORは、ビットフラグの反転、状態の切り替え、重複データを扱うアルゴリズムなど、さまざまな場面で利用されます。

特にビット操作を理解するうえでは、ANDやORと並んで押さえておきたい重要な演算です。

以上、C++のxorについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次