C言語のきほん|文字列を比較する(strcmp、strncmp)

同じか違うかを、文字列の中身で正しく見分ける。strcmp と strncmp で比較の基本をしっかり身につけよう

C言語で文字列を扱うとき、2つの文字列が同じかどうかを調べたい場面はとても多くあります。
たとえば、入力されたユーザー名が正しいか確認したり、コマンド名が一致しているか判定したり、先頭の数文字だけが同じかを調べたりするときです。

ただし、C言語では整数や文字のように、文字列を == でそのまま比較することはできません。
ここが、C言語の文字列処理で最初につまずきやすいポイントのひとつです。

C言語の文字列は、文字列型として特別に扱われるのではなく、ナル文字 \0 で終わる文字の配列として扱われます。
そのため、見た目が同じ文字列かどうかを調べるには、先頭から順番に1文字ずつ比較していく必要があります。
その役割を担うのが、標準ライブラリ関数の strcmp と strncmp です。

strcmp は文字列全体を比較する関数です。
一方の strncmp は、先頭から指定した文字数だけ比較する関数です。

この2つを使えるようになると、

  • 文字列全体が完全に一致しているか
  • 先頭の何文字かだけ一致しているか
  • どちらの文字列が文字コード順で大きいか

といったことを判定できるようになります。

この節では、strcmp と strncmp の基本的な使い方だけでなく、

  • なぜ == では比較できないのか
  • 比較結果としてどのような値が返るのか
  • if 文でどのように判定するのか
  • 先頭だけの比較はどんな場面で便利なのか

といった点まで、順番にやさしく見ていきます。

文字列比較は、入力チェックや条件分岐に欠かせないとても大切なテーマです。
ここでしっかり理解しておくと、今後の文字列処理がぐっとわかりやすくなります。

文字列を比較するとは

文字列を比較するとは、2つの文字列を先頭から順番に見比べて、同じかどうか、あるいはどちらが文字コード順で大きいかを調べることです。

たとえば、次のような比較を考えてみましょう。

文字列1文字列2判定
catcat等しい
catcar異なる
appleapply異なる
ABCDABCC異なる
everyeveryone先頭部分は同じ

strcmp と strncmp は、どちらも先頭から1文字ずつ比較します。
そして、最初に異なる文字が見つかった時点で、その文字どうしの大小関係によって結果を決めます。

もし最後まで同じなら、等しいと判断して 0 を返します。

比較結果の返り値

strcmp 関数と strncmp 関数は、比較の結果を整数値で返します。
返却値の意味は次の通りです。

比較結果返却値
文字列1 > 文字列2正の値
文字列1 = 文字列20
文字列1 < 文字列2負の値

ここでいう大小関係は、一般的には文字コード順です。
つまり、辞書順に似ていますが、実際には各文字のコード値に基づいて比較されています。

ASCII を前提にすると、小文字は大文字よりも大きい扱いになります。
たとえば A と a は同じアルファベットに見えますが、比較結果は等しいにはなりません。

文字1文字2一般的な判定
AaA < a
BAB > A
aba < b

このあたりは、見た目の印象だけでなく、文字コード順で比較していると理解しておくことが大切です。

なぜ == で文字列比較できないのか

C言語では、文字列は文字型配列として扱われます。
そのため、文字列どうしを == で比べても、文字列の中身そのものを比較しているわけではありません。

たとえば、次のようなコードを考えてみます。

char s1[] = "abc";
char s2[] = "abc";

見た目ではどちらも abc ですが、s1 と s2 は別々の配列です。
そのため、文字列の内容を比べたいなら、配列の中に入っている文字を順番に比べる必要があります。

この処理をしてくれるのが strcmp です。

つまり、C言語では

  • 数値の比較は == でもできる
  • 文字列の比較は strcmp 系の関数を使う

と整理して覚えるのが大切です。

strcmp 関数とは

strcmp は、2つの文字列全体を比較する関数です。

関数宣言

#include <string.h>

int strcmp(const char *s1, const char *s2);

機能

s1 が指す文字列と s2 が指す文字列の大小関係を比較します。

返却値

条件返却値
等しい0
s1 が s2 より大きい正の整数値
s1 が s2 より小さい負の整数値

使用例

char str1[] = "ABCD";
char str2[] = "ABCC";
int rc = strcmp(str1, str2);

この場合、先頭から比べると 1文字目から3文字目までは同じですが、4文字目で D と C が異なります。
D のほうが C より大きいので、rc には正の値が入ります。

strcmp の比較の流れ

strcmp の動きは、次のように考えるとわかりやすいです。

たとえば、apple と apply を比較するとします。

比較位置文字列1文字列2判定
1文字目aa同じ
2文字目pp同じ
3文字目pp同じ
4文字目ll同じ
5文字目ey異なる

この時点で、strcmp は e と y の大小関係を見て結果を返します。
つまり、strcmp は最初に違いが見つかった位置で判定を終える関数です。

strcmp の動きを図で理解する

この図では、2つの文字列が先頭から順に比較され、最初に異なる文字が見つかった時点で比較結果が決まることを表しています。
apple と apply は4文字目までは同じですが、5文字目の e と y で違いが出ます。
strcmp は、この違いを見つけたところで比較を終えます。

strncmp 関数とは

strncmp は、文字数を指定して比較する関数です。
文字列全体ではなく、先頭から n 文字までを比較したいときに使います。

関数宣言

#include <string.h>

int strncmp(const char *s1, const char *s2, size_t n);

機能

s1 が指す文字列と s2 が指す文字列について、先頭 n 文字までの大小関係を比較します。

返却値

条件返却値
等しい0
s1 が s2 より大きい正の整数値
s1 が s2 より小さい負の整数値

使用例

char str1[] = "ABCD";
char str2[] = "ABCC";
int rc = strncmp(str1, str2, 3);

この場合、先頭3文字はどちらも ABC です。
そのため rc には 0 が代入されます。

もし 4文字まで比較すれば、4文字目で D と C が違うため、正の値になります。

strncmp が便利な場面

strncmp は、文字列全体ではなく、先頭部分だけ見たいときに便利です。

たとえば、次のような場面です。

使いどころ
先頭数文字の一致確認every と everyone の先頭5文字を比較
接頭辞の判定Linux で始まるか確認
一部だけの比較日付文字列の年だけ比べる
コマンド判定先頭部分だけ共通かを見る

たとえば、everything と everyone は全体では異なりますが、先頭5文字 every は同じです。
このような場合に strncmp が役立ちます。

strcmp と strncmp の違い

この2つの違いを表で整理すると、次のようになります。

項目strcmpstrncmp
比較範囲文字列全体先頭から n 文字まで
主な用途完全一致の確認先頭部分の一致確認
返却値正の値、0、負の値正の値、0、負の値

つまり、

  • 文字列全体を比較したいなら strcmp
  • 先頭部分だけ比較したいなら strncmp

と考えるとわかりやすいです。

strcmp と strncmp の使用例

ここでは、元のサンプルとは少し内容を変えた、シンプルで親しみやすい例を示します。
2つの単語を入力して、全体比較と部分比較を行うプログラムです。
表示メッセージも別の日本語表現に置き換え、コメントも日本語にしています。

ファイル名:12_7_1.c

#include <stdio.h>
#include <string.h>

int main(void)
{
    char word1[100], word2[100];
    int count;

    printf("1つ目の単語を入力してください(最大99文字)> ");
    scanf("%99s", word1);

    printf("2つ目の単語を入力してください(最大99文字)> ");
    scanf("%99s", word2);

    /* strcmp で文字列全体を比較する */
    if (!strcmp(word1, word2)) {
        printf("2つの単語は同じです。\n");
    } else {
        printf("2つの単語は異なります。\n");
    }

    printf("先頭から比較する文字数を入力してください> ");
    scanf("%d", &count);

    /* strncmp で先頭から指定文字数だけ比較する */
    if (!strncmp(word1, word2, count)) {
        printf("先頭から%d文字は同じです。\n", count);
    } else {
        printf("先頭から%d文字は異なります。\n", count);
    }

    return 0;
}

実行例

たとえば、次のように入力したとします。

1つ目の単語を入力してください(最大99文字)> morning
2つ目の単語を入力してください(最大99文字)> more
先頭から比較する文字数を入力してください> 3

この場合、文字列全体としては異なります。
でも先頭3文字 mor は同じです。

そのため、表示は次のようになります。

2つの単語は異なります。
先頭から3文字は同じです。

if (!strcmp(...)) の意味

ここも大事なポイントです。

if (!strcmp(word1, word2))

この書き方は、最初は少し不思議に見えるかもしれません。
でも意味を分けて考えると簡単です。

strcmp の返却値は、

  • 等しいとき 0
  • 異なるとき 0以外

でした。

そして C言語では、

  • 0 は偽
  • 0以外は真

として扱われます。

さらに、論理否定演算子 ! を付けると、

  • 0 は真になる
  • 0以外は偽になる

ので、結果として

if (!strcmp(word1, word2))

は「文字列が等しいなら実行する」という意味になります。

書き方の比較

書き方意味
if (strcmp(s1, s2) == 0)等しいとき実行
if (!strcmp(s1, s2))等しいとき実行

どちらも正しいです。
ただ、C言語では後者の書き方もよく使われます。

先頭比較のイメージを図で理解する

この図では、everything と everyone のように、全体は違っていても先頭部分が同じ場合をイメージしています。
strncmp は、指定した文字数までしか比較しないため、その範囲が同じであれば 0 を返します。
この性質によって、接頭辞の一致確認や一部分だけの比較がしやすくなります。

strcmp と strncmp を使うときの注意点

文字列比較では、次の点を意識しておくと混乱しにくくなります。

注意点内容
== では比較しない文字列比較には strcmp 系を使う
等しいときは 0真ではなく 0 が返る
0以外は異なる正でも負でも「異なる」という意味になる
大文字小文字は別Apple と apple は等しくない
strncmp は範囲限定指定文字数までしか比較しない

特に、「等しいときは 0」という点は逆に感じやすいですが、
「差がないから 0」と考えると覚えやすいです。

strcmp と strncmp を学ぶ意味

この2つの関数を使えるようになると、文字列を使った条件分岐がとても書きやすくなります。

たとえば、次のような処理に応用できます。

活用場面内容
ログイン判定入力された文字列が登録値と一致するか確認
コマンド判定入力文字列が特定の命令か確認
メニュー選択yes や no の入力を判定
接頭辞判定先頭部分が特定の文字列か確認

文字列比較は、文字列処理の中でも特に実用性の高い基本機能です。
strcmp と strncmp をしっかり理解しておくと、この先のプログラム作成でも何度も役立ちます。

文字列比較を使いこなすための心構え

C言語の文字列比較では、見た目の印象だけでなく、返り値の意味を正しく理解して使うことが大切です。

特に意識したいのは次の点です。

心がけ内容
等しい判定は 0 を見るstrcmp の結果が 0 なら一致
用途で使い分ける全体比較なら strcmp、部分比較なら strncmp
文字コード順を意識する大文字小文字の違いに注意する
if 文の書き方に慣れる!strcmp や == 0 の意味を理解する

strcmp と strncmp は、C言語の文字列処理の基本を支える大切な関数です。
この2つを使いこなせるようになると、入力された文字列を条件にした判定処理が、ぐっと書きやすくなります。