C++プログラミングの基礎固め:Null文字バイト文字列とstd::vectorで文字列操作を習得


動作原理

  • 文字列の終端には必ずヌル文字が置かれ、文字列の長さはヌル文字までのバイト数で決まります。
  • ヌル文字は、ASCIIコード0に対応するバイト値'\0'です。
  • 各バイトは、1文字分のデータを保持します。
  • 文字列は、連続したメモリ領域に格納された一連のバイトとして表現されます。

利点

  • 多くのライブラリ関数で使用されている
  • メモリ管理が容易
  • シンプルで理解しやすい表現方法

char str[] = "Hello, World!"; // 文字列の宣言と初期化
char *ptr = str; // 文字列へのポインタ

while (*ptr != '\0') { // ヌル文字が見つかるまでループ
  putchar(*ptr); // 文字を出力
  ptr++; // ポインタを次の文字へ移動
}

putchar('\n'); // 改行を出力

上記のコードでは、"Hello, World!"という文字列をchar型の配列 str に格納しています。その後、ptr というポインタを使って str を指し示し、文字列を1文字ずつ出力しています。ヌル文字が見つかった時点でループを終了し、改行を出力します。

  • ヌル文字の存在を前提としたライブラリ関数を使用する場合は、引数として渡す文字列が確実にヌル文字で終端されていることを確認する必要があります。
  • 文字列を初期化する際には、必ず末端にヌル文字を置く必要があります。明示的にヌル文字を挿入するか、ヌル文字で初期化された文字列リテラルを使用する必要があります。
  • ヌル文字は文字列に含めることはできません。ヌル文字は終端を示すための特別なバイト値であり、文字として使用することはできません。
  • しかし、Null文字で終端されたバイト文字列は、C言語との互換性やパフォーマンス上の理由から、依然として広く使用されています。
  • C++には、std::stringと呼ばれる標準的な文字列型も用意されています。std::stringは、ヌル文字管理やメモリ管理などの煩雑な処理を自動的に行ってくれるため、より安全で使いやすいと言えます。


文字列の長さを取得する

#include <iostream>

int main() {
  char str[] = "Hello, World!"; // 文字列の宣言と初期化
  int len = 0;

  while (str[len] != '\0') {
    len++;
  }

  std::cout << "文字列の長さは: " << len << std::endl;

  return 0;
}

このコードでは、strlen()関数を使用せずに、ループを使って文字列の長さを取得しています。

文字列をコピーする

#include <iostream>

int main() {
  char str1[] = "Hello, World!"; // 元の文字列
  char str2[13]; // コピー先の文字列

  // str1をstr2にコピー
  for (int i = 0; str1[i] != '\0'; i++) {
    str2[i] = str1[i];
  }

  // ヌル文字を追加
  str2[12] = '\0';

  std::cout << "コピーされた文字列: " << str2 << std::endl;

  return 0;
}

このコードでは、ループを使って文字列を1文字ずつコピーしています。

#include <iostream>

int main() {
  char str1[] = "Hello, World!"; // 文字列1
  char str2[] = "Hello, World!"; // 文字列2

  int i = 0;
  while (str1[i] != '\0' && str2[i] != '\0') {
    if (str1[i] != str2[i]) {
      std::cout << "文字列は一致しません" << std::endl;
      return 1;
    }
    i++;
  }

  if (str1[i] == '\0' && str2[i] == '\0') {
    std::cout << "文字列は一致します" << std::endl;
  } else {
    std::cout << "文字列は一致しません" << std::endl;
  }

  return 0;
}


std::string

  • 安全性と使いやすさに優れている
  • メモリ管理、ヌル文字処理などを自動的に行う
  • C++標準ライブラリで提供される標準的な文字列型

利点

  • 使いやすい:豊富な機能と直感的な操作方法を提供
  • 安全性が高い:範囲外のアクセスやメモリリークなどのリスクが低い
  • ヌル文字処理が不要:ヌル文字の存在を意識する必要がない
  • メモリ管理が容易:メモリ確保や解放を自分で行う必要がない

欠点

  • 習得コストがかかる:独自の構文や機能を覚える必要がある
  • パフォーマンスが若干劣る場合がある:Null文字で終端されたバイト文字列よりもメモリ使用量が多くなる可能性がある


#include <iostream>

int main() {
  std::string str = "Hello, World!"; // 文字列の宣言と初期化

  std::cout << str << std::endl; // 文字列を出力

  // 文字列の長さを取得
  int len = str.length();
  std::cout << "文字列の長さは: " << len << std::endl;

  // 文字列をコピー
  std::string str2 = str;
  std::cout << "コピーされた文字列: " << str2 << std::endl;

  // 文字列を比較
  if (str == str2) {
    std::cout << "文字列は一致します" << std::endl;
  } else {
    std::cout << "文字列は一致しません" << std::endl;
  }

  return 0;
}

使用例

  • 標準ライブラリとの連携が容易
  • 安全性と使いやすさを重視するアプリケーションに適している
  • 多くの場合、std::stringがNull文字で終端されたバイト文字列よりも推奨される

std::array

  • メモリ管理と要素へのアクセスを効率的に行う
  • 固定長の要素を持つ固定長のコンテナ

利点

  • Null文字処理が不要:ヌル文字の存在を意識する必要がない
  • 範囲外のアクセスに対する安全性が高い
  • メモリ管理が容易:メモリ確保や解放を自分で行う必要がない

欠点

  • 可変長の文字列の処理には不向き
  • 文字列の長さを変更できない:固定長の配列であるため


#include <iostream>
#include <array>

int main() {
  std::array<char, 13> str = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '\0'}; // 文字列の宣言と初期化

  std::cout << str << std::endl; // 文字列を出力

  // 文字列の長さを取得
  int len = str.size();
  std::cout << "文字列の長さは: " << len << std::endl;

  // 文字列をコピー
  std::array<char, 13> str2 = str;
  std::cout << "コピーされた文字列: " << str2 << std::endl;

  // 文字列を比較
  if (str == str2) {
    std::cout << "文字列は一致します" << std::endl;
  } else {
    std::cout << "文字列は一致しません" << std::endl;
  }

  return 0;
}

使用例

  • 組み込み配列よりも安全性と効率性に優れている
  • 固定長の文字列を扱う場合に適している
  • 柔軟性と汎用性に優れている
  • 動的な要素を持つ可変長のコンテナ

利点

  • メモリ管理が比較的容易:自動的にメモリ確保・解放が行われる
  • 可変長の文字列の処理に適している
  • 文字列の長さを自由に変更できる:要素を追加・削除できる
  • パフォーマンスが若干劣る場合がある:std::stringよりもメモリ使用量が多
  • Null文字処理が必要:手動でヌル文字を挿入する必要がある