目次
はじめに
C++やC言語でC言語スタイルの文字列を扱う際、非常によく似た2つの方法があります。
- ポインタを使う方法:
const char* ptr = "テキスト";
- 配列を使う方法:
char arr[100];
これらは似て非なるもので、その挙動の違いを理解しないまま使うと、予期せぬエラーやバグの原因となります。特に、「再代入」の可否と、「内容の変更」の可否という点で、両者には決定的な違いがあります。
この記事では、文字列を扱う際のポインタと配列の根本的な違いについて、図解を交えながら分かりやすく解説します。
1. ポインタ (const char*
) の場合
const char*
は、「文字列リテラル(定数)が格納されている、読み取り専用のメモリ領域の先頭アドレス」を指し示すポインタです。
特徴
- 指し示す先を変更できる: ポインタ変数自体は、別のアドレスを指すように再代入できます。
- 指し示している先の内容は変更できない: 文字列リテラルは読み取り専用のため、ポインタ経由で中身を書き換えようとすることは禁止されています。
サンプルコード
#include <iostream>
using namespace std;
int main() {
// messageポインタは、"Initial Text" という文字列リテラルの場所を指している
const char* message = "Initial Text";
cout << "最初のポインタが指す内容: " << message << endl;
// OK: messageポインタが、別の文字列リテラル "New Text" の場所を指すように変更
message = "New Text";
cout << "変更後のポインタが指す内容: " << message << endl;
// NG: 指している先の内容を変更しようとすると、コンパイルエラーまたは実行時エラーになる
// message[0] = 'h';
return 0;
}
イメージ図 message = "New Text";
の操作は、message
という矢印の向きを、別の場所にある "New Text"
という塊に向けるようなイメージです。元の "Initial Text"
はそのまま残っています。
2. 配列 (char[]
) の場合
char arr[100];
は、「読み書き可能な、100文字分のメモリ領域を確保」する宣言です。
特徴
- 指し示す先を変更できない: 配列の変数名(
arr
)は、確保されたメモリ領域の先頭アドレスを示す「定数」のようなものです。別の場所を指すように再代入することはできません。 - 確保した領域の内容は自由に変更できる:
cin
やstrcpy
などを使って、確保したメモリ領域に新しい文字列をコピー(上書き)することができます。
サンプルコード
#include <iostream>
#include <cstring> // strcpy を使うために必要
using namespace std;
int main() {
// 読み書き可能な100文字分の領域を確保
char buffer[100];
// OK: strcpyを使って、配列の中身を "First message" で上書きする
strcpy(buffer, "First message");
cout << "最初の配列の内容: " << buffer << endl;
// OK: cinを使って、キーボードからの入力で配列の中身を上書きする
cout << "新しいメッセージを入力してください: ";
cin >> buffer;
cout << "入力後の配列の内容: " << buffer << endl;
// NG: 配列変数自体に、別の文字列リテラルを代入(再代入)することはできない
// buffer = "Another message"; // コンパイルエラー!
return 0;
}
イメージ図 strcpy(buffer, ...)
の操作は、buffer
という名前の付いた固定の箱の中身を、新しいもので上書きするようなイメージです。箱の場所自体は変わりません。
まとめ
ポインタ (const char* ) | 配列 (char[] ) | |
再代入 str = "..." | 可能 (指す先が変わる) | 不可能 (箱の場所は固定) |
内容の変更 str[0]=.. | 不可能 (指す先は読み取り専用) | 可能 (箱の中身は書き換え自由) |
この違いを理解することは、C言語スタイルの文字列を安全に扱う上で非常に重要です。なお、現代のC++では、これらの複雑さを解消し、より安全に文字列を扱える std::string
クラスを使うことが強く推奨されます。