C言語で文字の集まりを扱う際、「文字配列」と「文字列」という2つの言葉が出てきます。これらは似ているようで、C言語の世界では非常に重要な違いがあります。
この記事では、両者の決定的な違いと、C言語で文字列を自在に扱うための必須ライブラリ関数 (strcpy
, strlen
など) の使い方を分かりやすく解説します。
文字配列 vs 文字列:決定的な違いは「終端文字」
結論から言うと、C言語における**「文字列」とは、「ヌル文字(\0
)」で終わる文字配列**という特別なルールのことです。
ただの「文字配列」
'a'
, 'b'
, 'c'
のように、単に文字(char
)が複数集まって配列になっているだけのものです。
// これは「文字の配列」
// 注意: 'a' のようにシングルクォートで囲むのが正しい文字の記法
char char_array[] = {'H', 'i', '!'};
これは、メモリ上に H
, i
, !
の3文字が並んでいるだけの状態です。
C言語における「文字列」
ダブルクォート "
で囲んで初期化すると、C言語はそれを「文字列」として特別に扱います。
// これは「文字列」
char string_literal[] = "Hi!";
このコードは、一見3文字に見えますが、コンパイラは自動的に末尾にヌル文字 \0
(ナル、ヌルターミネータとも呼ばれる) を追加します。そのため、メモリ上には H
, i
, !
, \0
の4文字が格納されます。
なぜヌル文字(\0
)が重要なのか?
printf
の%s
や、後述する文字列操作関数は、この**\0
を「文字列の終わり」の目印として利用します**。もし\0
がなければ、関数はどこで処理を終えればよいか分からず、メモリの無関係な場所まで暴走してしまい、バグの原因となります。
実験コード:
#include <stdio.h>
int main(void) {
// \0 がない、ただの文字配列
char char_array[] = {'H', 'i', '!'};
// \0 が自動で付く、文字列
char string_literal[] = "Hi!";
printf("文字配列を%sで表示: %s\n", char_array); // 危険!
printf("文字列を%sで表示: %s\n", string_literal);
return 0;
}
実行結果(環境により異なります):
文字配列を%sで表示: Hi!Ç?..@ // ゴミデータが続く
文字列を%sで表示: Hi!
char_array
は\0
で終わっていないため、printf
は!
の次にある無関係なメモリの内容(ゴミデータ)を表示し続けてしまいます。
文字列を操作する基本関数 (<string.h>
)
C言語には、この\0
で終わる「文字列」を便利に扱うための関数が標準ライブラリとして提供されています。これらの関数を使うには、ファイルの先頭で #include <string.h>
を記述する必要があります。
文字列のコピー:strcpy
/ strncpy
配列は new_str = "abc";
のような単純な代入ができません。文字列をコピーするには strcpy
を使います。
strcpy(コピー先の配列, "コピー元の文字列");
#include <stdio.h>
#include <string.h>
int main(void) {
char destination[20]; // コピー先は十分な大きさを確保
strcpy(destination, "Hello C Language!");
printf("%s\n", destination);
return 0;
}
注意: strcpy
はコピー先の配列サイズをチェックしないため、サイズが足りないとメモリを破壊する危険なバグ(バッファオーバーフロー)の原因になります。より安全な strncpy
関数の使用も検討しましょう。
文字列の長さを調べる:strlen
strlen
は、文字列のヌル文字(\0
)を含まない文字数を返します。
strlen(長さを知りたい文字列);
#include <stdio.h>
#include <string.h>
int main(void) {
char message[] = "Hello"; // H,e,l,l,o,\0 の6文字分の配列
// strlenは \0 を数えないので、5が返る
printf("文字列\"%s\"の長さは %zu です。\n", message, strlen(message));
return 0;
}
文字列の連結:strcat
/ strncat
strcat
は、1つ目の文字列の末尾に2つ目の文字列を連結します。
strcat(連結先の配列, 連結したい文字列);
#include <stdio.h>
#include <string.h>
int main(void) {
// 連結後の長さも考慮して、十分な大きさを確保
char greeting[30] = "Hello, ";
strcat(greeting, "World!");
printf("%s\n", greeting); // "Hello, World!"と表示
return 0;
}
注意: strcat
もstrcpy
と同様にバッファオーバーフローの危険性があります。より安全な strncat
の使用を推奨します。
文字列の比較:strcmp
strcmp
は、2つの文字列を辞書順で比較します。
strcmp(文字列1, 文字列2);
- 戻り値が
0
: 2つの文字列は完全に一致 - 戻り値が負の数: 文字列1が文字列2より辞書順で先
- 戻り値が正の数: 文字列1が文字列2より辞書順で後
#include <stdio.h>
#include <string.h>
int main(void) {
char str1[] = "apple";
char str2[] = "apply";
if (strcmp(str1, "apple") == 0) {
printf("str1は\"apple\"と一致します。\n");
}
if (strcmp(str1, str2) < 0) {
printf("\"%s\"は\"%s\"より辞書順で先です。\n", str1, str2);
}
return 0;
}
まとめ
- C言語の「文字列」とは、ヌル文字(
\0
)で終わる文字配列というルール。 printf("%s", ...)
などの文字列操作関数は、この\0
を目印に動作する。- 文字列のコピーや連結、比較などには
<string.h>
ライブラリの専用関数を使う。 strcpy
やstrcat
にはバッファオーバーフローの危険性があるため、配列のサイズ管理は常に意識する必要がある。
この「ヌル終端文字列」の概念は、C言語プログラミングの根幹をなす非常に重要な知識です。ぜひマスターして、文字列を自在に扱えるようになりましょう。