9.

C++ の文字列データ型まとめ(char / char[] / char* / std::string の違いと使い分け)

編集
この記事の要点
  • C++ で文字列を扱う型は char / char[] / char* / std::string の 4 つが基本
  • char1文字だけ保持。シングルクォート 'a' で書く(ダブルクォートはコンパイルエラー)
  • char[] は配列に C 文字列をコピーして持つ。再代入不可だが要素単位の書き換えは可能
  • const char*文字列リテラルへのポインタconst を付けないと警告が出る
  • 通常は std::string を使う。長さの変更・再代入・連結が自由で他言語の文字列型と同じ感覚で扱える

C++ で文字列を表す 4 つの型

C++ の文字列まわりは歴史的事情から複数の型が混在しており、初心者が混乱しやすいポイントです。実務では std::string を使うのが原則ですが、ライブラリや API の引数として const char* が現れることも多いため、それぞれの特徴を理解しておく必要があります。

役割再代入備考
char1 文字シングルクォート必須
char[]固定長の C 文字列不可(配列自体)要素単位の書き換えは可
const char*文字列リテラルへのポインタポインタは可const 必須
std::string可変長の文字列クラス標準ライブラリ。実務はこれ

char(1 文字)

char1 文字だけを保持する型です。整数として 1 バイトを表現する型でもあり、ASCII コードで扱われます。

char a = 'a';   // OK
char b = "a";   // エラー: ダブルクォートは const char* なので不可
char c = 65;    // 'A' と等価(ASCII 65)

リテラルはシングルクォートで書きます。ダブルクォートで書くと文字列リテラル(const char*)として解釈されてしまうため、char への代入はコンパイルエラーになります。

char[](固定長配列)

複数の文字を持ちたい場合の最も素朴な方法が char[] です。ヌル終端(\0)を含めた C 文字列が配列にコピーされます。

char a[] = "abc";   // 'a','b','c','\0' の 4 要素
a[0] = 'A';         // OK: 要素単位の書き換え
a    = "xyz";       // エラー: 配列自体への再代入は不可

配列名は定数扱いなので、別の文字列を丸ごと代入することはできません。ただし、要素単位 (a[0] など) なら書き換え可能で、ここが const char* との大きな違いです。

const char*(文字列リテラルへのポインタ)

ポインタ変数に文字列リテラルのアドレスを格納する形式です。リテラルは読み取り専用領域に置かれるため、書き換えようとすると未定義動作になります。

const char* a = "abc";  // OK
a = "def";              // OK: ポインタ先を変更
a[0] = 'X';             // エラー: const なので書き換え不可

char* b = "abc";        // 警告(C++11 以降は非推奨/エラー)

const を付けずに書くと、コンパイラが警告(環境によってはエラー)を出します。C++11 以降、文字列リテラルは const char[] として扱われるため、const char* として受けるのが正しい書き方です。

std::string(実務で使う型)

標準ライブラリの <string> ヘッダで提供されるクラスです。長さの変更・連結・部分文字列の取得などが直感的にでき、メモリ管理も自動です。

#include <string>
using namespace std;

string a = "abc";
a = "def";          // 再代入 OK
a += "ghi";         // 連結 OK
cout << a.length(); // 長さ取得
cout << a[0];       // 添字アクセス

基本的には std::string を使えば問題ありません。Java / C# / Python / PHP などで言う「文字列型」に最も近いのがこの型です。

std::string と const char* の相互変換

レガシーな C API(printf 系・fopen など)は const char* を要求するため、std::string からの変換が必要になります。

string s = "hello";

// string -> const char*
const char* p = s.c_str();
printf("%s\n", s.c_str());

// const char* -> string
const char* cs = "world";
string t = cs;

比較・連結・検索の基本操作

std::string は演算子オーバーロードによって、整数や他の文字列クラスと同じ感覚で扱えます。代表的な操作をまとめます。

string a = "Hello";
string b = "World";

// 連結
string c = a + " " + b;     // "Hello World"
a += "!";                    // "Hello!"

// 比較
if (a == b) { /* 等価比較 */ }
if (a < b)  { /* 辞書順比較 */ }

// 検索
size_t pos = c.find("World");      // 6
size_t notfound = c.find("xyz");   // string::npos

// 部分取り出し
string sub = c.substr(0, 5);       // "Hello"

// 置換
c.replace(6, 5, "C++");            // "Hello C++"

// 長さと空判定
size_t n   = c.length();
bool   ok  = c.empty();

find が見つからなかったときの戻り値は std::string::npos という特別な値で、これを -1 と比較してはいけません(実体は最大値の size_t)。

文字列リテラルと std::string の混在

関数引数を const std::string& として受けると、文字列リテラルからの暗黙変換が起きます。これは便利ですが、毎回コピーが発生するため、頻度の高い API では C++17 で追加された std::string_view を使うのが現代的です。

#include <string_view>

// 古い書き方(コピーが起きる)
void log_old(const std::string& s) { /* ... */ }

// 推奨(コピーなし、リテラルも string も受けられる)
void log_new(std::string_view s) { /* ... */ }

log_new("literal");
log_new(std::string("hello"));

選び方のまとめ

シーン推奨
新規コードで文字列を持つ変数std::string
関数引数(読み取り専用、頻度高)std::string_view(C++17)
C ライブラリへの受け渡しconst char* (.c_str() で取得)
1 文字だけ持ちたいchar
固定サイズのバッファが必要char[N] または std::array<char,N>

関連

  • 文法 — 親カテゴリ
  • C++ — 言語トップ
  • std::string — 標準ライブラリの文字列クラス
  • char / wchar_t / char16_t / char32_t — 文字型のバリエーション
  • std::string_view — C++17 で追加された軽量参照型
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. コメントアウト
  2. 文字列の結合/連結
  3. 変数の宣言
  4. 定数の宣言
  5. if文
  6. if文の論理演算子
  7. for文
  8. データ型(文字列以外)
  9. データ型(文字列)
  10. 配列とfor文
  11. 配列の要素数
  12. 多次元配列とfor文
  13. 多次元配列の要素数
  14. 関数の定義と呼び出し

最近更新/作成されたページ