
C言語基礎|strcmp と strncmp の違い
文字列比較は「全部」か「先頭だけ」か―strcmp と strncmp を使い分けて、判定ミスを防ごう。
C言語で「文字列が同じか」「どっちが辞書順で前か」を調べたいとき、定番が strcmp と strncmp です。
見た目はそっくりですが、決定的な違いは 比較する範囲。
- strcmp:最後のナル文字まで、文字列全体を比較する。
- strncmp:先頭から最大 n 文字だけ比較する(途中でナル文字が出たらそこで止まる)
「先頭3文字だけ合っていればOK」みたいな判定は strncmp が得意です。逆に「完全一致」が目的なら strcmp が安心ですね。

strcmp / strncmp の書式と「何をする命令か」
strcmp
- ヘッダ:#include <string.h>
- 形式:int strcmp(const char *s1, const char *s2);
何をする命令なのか
- s1 と s2 を先頭から1文字ずつ比較していき、最初に違いが出た場所で大小を決める
- 全部一致(両方とも同じ場所で \0 に到達)なら等しい
返却値の意味
- 0:等しい
- 正の値:s1 のほうが大きい
- 負の値:s1 のほうが小さい
※「正の値が 1 になる」などは保証されません。値の大きさ自体に意味はなく、符号だけ見ればOKです。
strncmp
- ヘッダ:#include <string.h>
- 形式:int strncmp(const char *s1, const char *s2, size_t n);
何をする命令なのか
- s1 と s2 の先頭から最大 n 文字までを比較する
- 途中で \0 が出たら、それ以降は比較しない(文字列の終端で止まる)
返却値の意味
- strcmp と同じ(0 / 正 / 負)
一番大事な違い(表で整理)
| 観点 | strcmp | strncmp |
|---|---|---|
| 比較範囲 | 文字列全体(\0 まで) | 先頭から最大 n 文字 |
| 目的 | 完全一致・辞書順比較 | プレフィックス比較、先頭だけ比較 |
| 文字列でない領域 | 基本は文字列が前提(\0 必須) | 先頭 n バイトに \0 がなくても比較は進む(ただし安全な範囲で) |
| 典型例 | パスワード一致、コマンド一致 | 先頭3文字だけ判定、拡張子判定、ヘッダ判定 |
返却値は「差の値」っぽいけど、符号だけ見よう
strcmp/strncmp は「違った文字同士」を unsigned char として比較し、その差に近い値を返すことが多いです。
でも、どんな値になるかは処理系や実装により得るので、実務ではこう考えるのが安全です。
- 等しいか:== 0
- それ以外:< 0 か > 0 だけ見る
比較の流れ(図でイメージ)
例:s1="ABCDE"、s2="ABC"

この違いが、「先頭だけ一致」を判定したいときに strncmp が便利な理由です。
文字列の大小関係は「文字コード」によって決まる
辞書順っぽく見えますが、基準は文字コードです。
たとえば "a" と "Z" の大小は、人の感覚ではなく文字コード順で決まります。
なので、英字の大小無視で比較したい、ロケール順で比較したい、といった用途では別の手段(例えば正規化や専用関数)が必要になることがあります。
サンプルプログラム
「入力されたコマンドが quit なら終了。先頭3文字が get なら取得扱い」という、プログラム例です。
プロジェクト名:chap11-11-1 ソースファイル名:chap11-11-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
#include <string.h>
int main(void)
{
char cmd[128];
puts("コマンドを入力してください。quit で終了します。");
puts("get で始まると「取得コマンド」として扱います。");
while (1) {
printf("\n入力:");
scanf("%127s", cmd);
if (strcmp(cmd, "quit") == 0) {
puts("終了します。");
break;
}
if (strncmp(cmd, "get", 3) == 0) {
puts("取得コマンドとして受け取りました。");
} else {
puts("その他のコマンドです。");
}
printf("strcmp(cmd, quit) の結果(符号が重要):%d\n", strcmp(cmd, "quit"));
printf("strncmp(cmd, get, 3) の結果(0なら先頭一致):%d\n", strncmp(cmd, "get", 3));
}
return 0;
}ポイント
- 完全一致(quit)は strcmp
- 先頭一致(get...)は strncmp
- 表示メッセージも今回用に変更済みです。
よくある落とし穴
strncmp で n を大きくしすぎると危ないことがある
strncmp は最大 n 文字見るので、比較対象が「必ず n 文字以上読める領域」になっていないと危険です。
文字列なら通常は \0 で止まりますが、比較対象が「文字列ではない生のバイト列」だったり、そもそも不正なポインタだったりするとアウトです。
等しい判定は必ず == 0
次はダメです。
- if (strcmp(a, b)) を「等しい」と思って書く
これは 0 以外が真なので、逆になります。
正しくは:
- if (strcmp(a, b) == 0)
演習問題
演習11-6
次の仕様の関数 my_strcmp と my_strncmp を作成してください。
- my_strcmp(s1, s2) は strcmp と同じ仕様
- my_strncmp(s1, s2, n) は strncmp と同じ仕様
プロトタイプ:
#include <stddef.h>
int my_strcmp(const char *s1, const char *s2);
int my_strncmp(const char *s1, const char *s2, size_t n);
解答例
#include <stddef.h>
int my_strcmp(const char *s1, const char *s2)
{
while (*s1 != '\0' && *s2 != '\0') {
unsigned char c1 = (unsigned char)*s1;
unsigned char c2 = (unsigned char)*s2;
if (c1 != c2)
return (c1 > c2) ? 1 : -1;
s1++;
s2++;
}
if (*s1 == *s2)
return 0;
return ((unsigned char)*s1 > (unsigned char)*s2) ? 1 : -1;
}
int my_strncmp(const char *s1, const char *s2, size_t n)
{
while (n > 0 && *s1 != '\0' && *s2 != '\0') {
unsigned char c1 = (unsigned char)*s1;
unsigned char c2 = (unsigned char)*s2;
if (c1 != c2)
return (c1 > c2) ? 1 : -1;
s1++;
s2++;
n--;
}
if (n == 0)
return 0;
if (*s1 == *s2)
return 0;
return ((unsigned char)*s1 > (unsigned char)*s2) ? 1 : -1;
}解説
- unsigned char に直して比較するのがポイント(負の char を避ける)
- my_strncmp は n が 0 になったら「そこまで一致」として 0 を返す
