
C言語基礎|文字列関連の標準ライブラリ
文字列処理は車の運転と同じ!―安全装備(string.h)を使えば、速く・正確に・事故(バグ)を減らせる。
これまで、strlen や文字列コピーを自作して「仕組み」をしっかり理解してきました。ここからは一歩進んで、実務で本当に使う武器=標準ライブラリを使いこなしていきます。
C言語の文字列は、\0(ナル文字)で終わる char の並びです。だからこそ、コピー・連結・比較・検索などの処理は定番化されていて、<string.h> に便利な関数がまとまっています。
自作するより 速い・読みやすい・テスト済み のことが多いので、まずは標準関数を正しく使えることが大事です。

<string.h> の代表的な関数一覧(用途別に整理)
提示された Table 11C-1 を、学習しやすいように「目的」でまとめ直します。
| 分類 | 関数 | 何をする?(超要約) |
|---|---|---|
| 長さ | strlen | 文字列の長さ(\0を除く) |
| コピー | strcpy / strncpy | 文字列をコピー(n制限あり/なし) |
| 連結 | strcat / strncat | 末尾に追加(n制限あり/なし) |
| 比較 | strcmp / strncmp | 辞書順比較(n制限あり/なし) |
| ロケール | strcoll / strxfrm | ロケール依存の比較用 |
| 検索 | strchr / strrchr | 文字を探す(先頭/末尾) |
| 検索 | strstr | 部分文字列を探す |
| 構成 | strspn / strcspn | 許可文字だけの連続長 / 禁止文字までの長さ |
| 分割 | strtok | 区切りで分解(状態を持つので注意) |
| メモリ | memset | 連続領域を同じ値で埋める |
| メモリ | memcpy / memmove | 連続領域コピー(重なり対応の違い) |
| メモリ | memchr / memcmp | 探索 / 比較(バイト列として) |
restrict って何?(超ざっくり安全メモ)
プロトタイプに restrict が付くことがあります。これはコンパイラに対して、
- このポインタが指す領域は、他の restrict ポインタが指す領域と重ならない前提で最適化していいよ
という意味合いです。
そのため、例えば strcpy は コピー元とコピー先が重なる状況は未定義です。重なる可能性があるなら memmove を使う、という判断につながります。
strcpy 関数:文字列をコピーする
書式
- ヘッダ:#include <string.h>
- 形式:char *strcpy(char * restrict s1, const char * restrict s2);
何をする命令なのか(役割)
- s2 が指す文字列(\0 まで)を、s1 が指す配列へコピーする
- コピー元とコピー先が重なると動作は未定義
- 返り値は s1(コピー先の先頭)を返す
strcpy の要点
| 項目 | 内容 |
|---|---|
| コピー範囲 | \0 まで全部コピー(\0 も含む) |
| コピー先の条件 | 十分な大きさの配列(バッファ)が必要 |
| 重なり | 未定義(避ける) |
| 返り値 | コピー先の先頭ポインタ(s1) |
strncpy 関数:文字数を制限してコピーする
書式
- ヘッダ:#include <string.h>
- 形式:char *strncpy(char * restrict s1, const char * restrict s2, size_t n);
何をする命令なのか(役割)
- 最大 n 文字ぶんコピーする
- s2 の長さが n 未満なら、残りを \0 で埋める
- s2 の長さが n 以上なら、\0 を付けない(ここが事故ポイント!)
- 返り値は s1
strncpy の事故ポイント
| 状況 | どうなる? | 結果 |
|---|---|---|
| s2 の長さ < n | 残りを \0 埋め | 文字列として成立しやすい |
| s2 の長さ >= n | \0 をコピーしない | 文字列にならない可能性あり(危険) |
strcpy と strncpy の違い(イメージ)
strcpy: コピー元が終わるまで(\0 まで)全部コピー
strncpy: n 文字だけコピー(\0 は入らないことがある)
→ 表示や strlen が暴走する原因になりうる
サンプルプログラム
「入力した文字列を safety という配列にコピーし、さらに先頭3文字だけを short にコピーする」プログラム例です。
プロジェクト名:chap11-9-1 ソースファイル名:chap11-9-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
#include <string.h>
int main(void)
{
char input[128];
char safe[128];
char short3[4]; // 3文字 + \0
printf("好きな単語を入力してください:");
scanf("%127s", input);
strcpy(safe, input);
printf("丸ごとコピーしました:%s\n", safe);
strncpy(short3, input, 3);
short3[3] = '\0'; // strncpy の落とし穴対策:自分で終端を付ける
printf("先頭3文字だけコピーしました:%s\n", short3);
return 0;
}ポイント:
- scanf は %127s にして安全寄りにしています。
- strncpy を使ったあと 必ず short3[3] = '\0' を入れて「文字列として成立」を保証しています。
ここがめちゃ大事です。
strcpy の返り値が便利な理由(連続処理が書ける)
strcpy はコピー先の先頭を返すので、次のように「コピーしながら表示」ができます。
例:コピーしてすぐ表示
printf("結果:%s\n", strcpy(safe, input));
なぜこう書ける?
| 関数 | 返すもの | だからできること |
|---|---|---|
| strcpy | s1(コピー先の先頭) | そのまま printf の %s に渡せる |
| strncpy | s1(コピー先の先頭) | 同様に渡せるが \0 には注意 |
strncpy がバグにつながる典型例(なぜ危ない?)
例えば次のようにすると、s は \0 で終わらない可能性があります。
char s[5] = {'X','X','X','X','X'};
strncpy(s, "12345", 2);
結果は先頭2文字だけ書き換わり、残りは X のままです。
つまり、s のどこにも \0 が無ければ 文字列として扱えず、printf の %s や strlen がメモリの奥まで探しに行って危険です。
演習問題
演習11-4
次の仕様の関数 my_strcpy と my_strncpy を作成してください。
- my_strcpy(d, s) は s を d に \0 までコピーし、d を返す
- my_strncpy(d, s, n) は最大 n 文字コピーし、d を返す
ただし標準 strncpy と同じ仕様(n 以上なら \0 を付けない)にする
プロトタイプ:
#include <stddef.h>
char *my_strcpy(char *d, const char *s);
char *my_strncpy(char *d, const char *s, size_t n);
解答例
#include <stddef.h>
char *my_strcpy(char *d, const char *s)
{
char *t = d;
while ((*d++ = *s++) != '\0')
;
return t;
}
char *my_strncpy(char *d, const char *s, size_t n)
{
char *t = d;
while (n > 0 && *s != '\0') {
*d++ = *s++;
n--;
}
while (n > 0) { // 余りは \0 埋め
*d++ = '\0';
n--;
}
return t;
}解説
- my_strcpy は \0 までコピーするので、コピー後は必ず文字列になります。
- my_strncpy は「余ったら \0 埋め」だが、「足りない(nが小さい)と \0 を入れない」ケースがあり得ます。
→ 使う側が終端を保証する設計が必要です。
