C++のデフォルト引数とビット演算:関数の柔軟な使い方と低レベル操作

C++には、関数の利便性を高める「デフォルト引数」や、コンピュータの内部表現(ビット)を直接操作する「ビット演算」といった機能が備わっています。この記事では、これらの機能の基本的な使い方と活用例を解説します。

目次

デフォルト引数 (Default Argument)

関数の仮引数を宣言または定義する際に、= デフォルト値 の形でデフォルト実引数を指定できます。

関数を呼び出す際に該当する実引数が省略された場合、コンパイラは自動的に指定されたデフォルト値で仮引数を初期化します。これにより、関数呼び出しの柔軟性が向上します。

デフォルト引数のサンプルコード

例えば、指定された文字で特定の長さの罫線を表示する関数を考えます。文字や長さを省略した場合は、デフォルトの文字(例: -)と長さ(例: 20)が使われるように設計できます。

#include <iostream>

/**
 * @brief 指定された文字で指定された長さの線を描画する
 * @param length 線の長さ (デフォルトは 20)
 * @param fillChar 描画に使用する文字 (デフォルトは '=')
 */
// デフォルト引数は、関数宣言(または定義)で指定します
void drawLine(int length = 20, char fillChar = '=') {
    for (int i = 0; i < length; ++i) {
        std::cout << fillChar;
    }
    std::cout << "\n";
}

int main() {
    std::cout << "--- デフォルト引数を使用 ---\n";
    // 引数を両方省略 (length=20, fillChar='=')
    drawLine(); 

    std::cout << "\n--- 長さのみ指定 ---\n";
    // 第1引数のみ指定 (length=30, fillChar='=')
    // (注意) デフォルト引数は末尾からしか省略できません
    drawLine(30);

    std::cout << "\n--- 両方指定 ---\n";
    // 両方の引数を指定 (length=10, fillChar='*')
    drawLine(10, '*');

    return 0;
}

実行結果:

--- デフォルト引数を使用 ---
====================

--- 長さのみ指定 ---
==============================

--- 両方指定 ---
**********

ビット単位の論理演算

コンピュータは、数値を内部的に0と1のビット列として扱います。ビット演算は、このビット列を直接操作するための低レベルな演算です。主に、フラグ管理、ハードウェア制御、パフォーマンスが重要な場面などで使用されます。

ビット論理演算子(AND, OR, XOR, NOT)

これらは、2つの数値の対応する各ビットに対して論理演算を行います。

  • &(ビット単位AND)
    • 両方のビットが 1 の場合のみ 1 になります。
  • |(ビット単位OR)
    • 少なくとも一方のビットが 1 の場合に 1 になります。
  • ^(ビット単位XOR)
    • 2つのビットが異なる(10)場合に 1 になります。
  • ~(ビット単位NOT)
    • ビットを反転します(10 に、01 に)。単項演算子です。

ビット論理演算のサンプルコード(フラグ管理)

ビット演算の典型的な用途は、複数の状態(フラグ)を1つの整数で管理することです。

#include <iostream>
#include <bitset> // ビット表示用

int main() {
    // 0001 (2進数)
    constexpr unsigned int FLAG_READ = 1; 
    // 0010 (2進数)
    constexpr unsigned int FLAG_WRITE = 2; 
    // 0100 (2進数)
    constexpr unsigned int FLAG_EXECUTE = 4;

    // 現在の権限 (読み取り と 実行 が可能)
    unsigned int permissions = FLAG_READ | FLAG_EXECUTE; // 0001 | 0100 = 0101 (10進数で 5)

    std::cout << "初期権限: " << std::bitset<4>(permissions) << " (=" << permissions << ")\n";

    // 1. 権限の追加 (OR: |)
    // '書き込み' 権限を追加する
    permissions = permissions | FLAG_WRITE; // 0101 | 0010 = 0111 (10進数で 7)
    std::cout << "書き込み追加: " << std::bitset<4>(permissions) << " (=" << permissions << ")\n";

    // 2. 権限のチェック (AND: &)
    // '読み取り' 権限があるか?
    if ((permissions & FLAG_READ) != 0) {
        std::cout << "-> 読み取り権限があります。\n";
    }

    // 3. 権限の削除 (AND: & と NOT: ~)
    // '実行' 権限を削除する
    // FLAG_EXECUTE (0100) のNOT (~) は (...11111011)
    permissions = permissions & (~FLAG_EXECUTE); // 0111 & (...1011) = 0011 (10進数で 3)
    std::cout << "実行削除後: " << std::bitset<4>(permissions) << " (=" << permissions << ")\n";

    // 4. 権限のトグル (XOR: ^)
    // '書き込み' 権限を反転する (あれば削除、なければ追加)
    permissions = permissions ^ FLAG_WRITE; // 0011 ^ 0010 = 0001 (10進数で 1)
    std::cout << "書き込みトグル: " << std::bitset<4>(permissions) << " (=" << permissions << ")\n";
    
    return 0;
}

ビットシフト演算

ビット列全体を指定したビット数だけ左または右に移動させます。

シフト演算子 (<<, >>)

  • <<(左シフト)
    • x << nx のビット列を n ビット左に移動します。空いた右側には 0 が入ります。数値的には $x \times 2^n$ とほぼ同等です。
  • >>(右シフト)
    • x >> nx のビット列を n ビット右に移動します。左側に何が入るかは x の型(signedunsignedか)によります。数値的には $x / 2^n$ (整数除算)とほぼ同等です。unsigned 型での使用が推奨されます。

シフト演算のサンプルコード(RGBカラーの合成・分離)

シフト演算は、複数の小さな値を1つの大きな整数に詰め込んだり(合成)、それを取り出したり(分離)するのによく使われます。

#include <iostream>
#include <iomanip> // std::hex, std::setw, std::setfill

int main() {
    // 8ビット (0-255) のカラー値
    unsigned int r = 255; // 赤
    unsigned int g = 128; // 緑
    unsigned int b = 64;  // 青

    // --- 合成 (<< と |) ---
    // R (16-23ビット目), G (8-15ビット目), B (0-7ビット目) の 32ビット整数に合成
    // r << 16 : (255) ... 111111110000000000000000
    // g << 8  : (128) ...         1000000000000000
    // b       : (64)  ...                 01000000
    unsigned int color = (r << 16) | (g << 8) | b;

    std::cout << std::hex << std::setfill('0') << std::uppercase; // 16進数表示設定
    std::cout << "合成されたカラー値: 0x" << std::setw(6) << color << "\n";
    // 期待値: 0xFF8040 (255, 128, 64)

    // --- 分離 (>> と &) ---
    // 0xFF (11111111) は、下位8ビットだけを取り出すための「マスク」
    unsigned int extracted_r = (color >> 16) & 0xFF;
    unsigned int extracted_g = (color >> 8) & 0xFF;
    unsigned int extracted_b = (color) & 0xFF;

    std::cout << std::dec; // 10進数表示に戻す
    std::cout << "分離されたR値: " << extracted_r << "\n"; // 255
    std::cout << "分離されたG値: " << extracted_g << "\n"; // 128
    std::cout << "分離されたB値: " << extracted_b << "\n"; // 64

    return 0;
}

整数型のビット数の確認

C++の intlong などのビット数は、環境(OSやCPU)によって異なる場合があります。現在の環境での正確なビット数を調べるには、<limits> ヘッダの std::numeric_limits を使用します。

digits は、その型が(符号ビットを除いて)値を表現するために使用するビット数(2進数の桁数)を返します。unsigned 型の場合は、これがその型の全ビット数と一致します。

コード例:std::numeric_limits の使用

#include <iostream>
#include <limits> // std::numeric_limits のために必要

int main() {
    std::cout << "--- この処理系の整数型のビット数 ---\n";
    
    // 8ビット (通常)
    std::cout << "unsigned char 型のビット数: " 
              << std::numeric_limits<unsigned char>::digits << "\n";
              
    // 16ビット (通常)
    std::cout << "unsigned short 型のビット数: " 
              << std::numeric_limits<unsigned short>::digits << "\n";
              
    // 32ビット (一般的)
    std::cout << "unsigned int 型のビット数: " 
              << std::numeric_limits<unsigned int>::digits << "\n";
              
    // 32ビットまたは64ビット
    std::cout << "unsigned long 型のビット数: " 
              << std::numeric_limits<unsigned long>::digits << "\n";
              
    // 64ビット (一般的) (C++11以降)
    std::cout << "unsigned long long 型のビット数: " 
              << std::numeric_limits<unsigned long long>::digits << "\n";
              
    return 0;
}

まとめ(総括)

この記事では、C++の関数をより便利にする「デフォルト引数」と、低レベルなデータ操作を可能にする「ビット演算」について解説しました。

  • デフォルト引数は、関数を呼び出す際の引数の省略を許可し、関数の柔軟性と利便性を高めます。
  • **ビット演算(論理演算、シフト演算)**は、メモリ効率の最適化やハードウェアに近い制御など、特定の状況で非常に強力なツールとなります。

ビット演算はコードの可読性を低下させる可能性があるため、フラグ管理やパフォーマンスが重要となる場面に限定して使用し、それ以外の場面では bool 型や std::vector など、より抽象的で安全な機能を使うことが推奨されます。

これらの機能を適切に使い分けることが、効率的で保守性の高いC++プログラムを作成する鍵となります。

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

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次