こんにちは、ナナです。
C言語では、次のように「変数」に比較演算子を使うことで、「定数」や「他の変数」と値を比較することができます。
#include <stdio.h>
int main(void)
{
int num1 = 100;
int num2 = 200;
// 変数と定数の比較
if (num1 == 100)
{
printf("一致\n");
}
// 変数と変数の比較
if (num1 != num2)
{
printf("不一致\n");
}
return 0;
}
しかし、C言語ではこのような変数同士の比較は簡単にできても、文字列のような連続したデータを比較演算子では比較できません。次のプログラムの条件式は「真」とはならないのです。
#include <stdio.h>
int main(void)
{
char moji[] = "Hello";
char num1[] = { 100, 200 };
char num2[] = { 100, 200 };
// 文字列と文字列の比較は比較演算子ではできない
if (moji == "Hello")
{
printf("一致\n");
}
// 配列と配列の比較は比較演算子ではできない
if (num1 == num2)
{
printf("一致\n");
}
return 0;
}
このような、複数の情報から構成される文字列や配列の比較を可能とするのが、strcmp関数とmemcmp関数です。
本記事では次の悩みを解消する内容となっています。
では、「strcmp」と「memcmp」の使い方と扱い方の違いを学んでいきましょう。
strcmp系の標準ライブラリ関数を紹介
まずは、strcmp系の標準ライブラリ関数を紹介しましょう。
#include <string.h>
int strcmp(const char * str1, const char * str1);
int strncmp(const char * str1, const char * str1, size_t n);
strcmpとは「string:文字列」を「compare:比較」するための標準ライブラリ関数です。
strcmp関数の仕様について
strcmp関数は、第1引数と第2引数は比較したい文字列へのポインタを指定します。
includeファイル | string.h |
関数仕様 | int strcmp(const char * str1, const char * str1); |
引数1 | 比較対象の文字列へのポインタ |
引数2 | 比較対象の文字列へのポインタ |
戻り値 | 比較結果 0:文字列内容が一致 正の値:違いがあり、str1の文字コードが大きい 負の値:違いがあり、str2の文字コードが大きい |
特記事項 | 指定文字列からヌル文字までの完全一致を判定する |
strncmp関数の仕様について
strncmp関数は、第3引数で比較するサイズを指定できるバージョンです。
includeファイル | string.h |
関数仕様 | int strncmp(const char * str1, const char * str1, size_t n); |
引数1 | 比較対象の文字列へのポインタ |
引数2 | 比較対象の文字列へのポインタ |
引数3 | 比較するサイズを指定 |
戻り値 | 比較結果 0:文字列内容が一致 正の値:違いがあり、str1の文字コードが大きい 負の値:違いがあり、str2の文字コードが大きい |
特記事項 | 指定文字列からヌル文字、もしくは指定サイズn分だけ文字列を比較する |
文字列の比較結果は戻り値で取得できます。一致したときに「0」の値が返却されることに注意しましょう!
strcmp関数を使ったサンプルプログラム
strcmp関数を使ったプログラムを紹介しましょう。
#include <stdio.h>
#include <string.h>
int main(void)
{
char moji[] = "Hello";
if (strcmp(moji, "Hello") == 0)
{
printf("一致\n");
}
else
{
printf("不一致\n");
}
return 0;
}
戻り値に関しては、文字の大小関係を知りたいことはあまりないため、一般的に「0」か「0以外」かをチェックします。
strcmp関数を使った比較例のサンプルプログラム
strcmp関数において、どのような文字列の比較なら一致とされるかは必ず知っておきましょう。
#include <stdio.h>
#include <string.h>
int main(void)
{
printf("%d\n", strcmp("Hello", "Hello")); // 〇
printf("%d\n", strcmp("HELLO", "hello")); // ×
printf("%d\n", strcmp("Hello", "HelloWorld")); // ×
printf("%d\n", strcmp("ハロー", "ハロー")); // 〇
return 0;
}
No | 第1引数 | 第2引数 | 比較結果 | 説明 |
---|---|---|---|---|
① | “Hello” | “Hello” | 一致 | 完全一致のため |
② | “HELLO” | hello” | 不一致 | 英大文字・小文字は別文字と判断する |
③ | “Hello” | “HelloWorld” | 不一致 | 前方一致のみでは不一致と判断する |
④ | “ハロー” | “ハロー” | 一致 | 全角文字でも一致と判断する |
まず、大事なのは②のケースとして英大文字と小文字は区別されることです。そして、③の前方一致のケースは不一致と判断されます。
strcmp関数による一致判定とは、ヌル文字も含めて完全一致する必要があります。
strncmp関数を使うと前方一致判定も可能
strncmp関数は比較範囲を限定的にすることができるため、前方一致の判定が可能になります。
#include <stdio.h>
#include <string.h>
int main(void)
{
char moji[] = "Hello";
printf("%d\n", strncmp(moji, "Hello", strlen(moji))); // 〇
printf("%d\n", strncmp(moji, "HelloWorld", strlen(moji))); // 〇
return 0;
}
strlen関数はヌル文字を含まない文字サイズを返却するため、このプログラムでは前方一致の場合も一致判定がなされます。
関数の性質を知ることで、使いどころを知ることができます。
memcmp系の標準ライブラリ関数を紹介
続いて、memcmp関数を紹介しましょう。
#include <string.h>
int memcmp(const void * buf1, const void * buf1, size_t n);
memcmp関数は「memory:メモリ」を「compare:比較」するための標準ライブラリ関数です。
strcmp関数では、文字列がヌル文字で終わる性質を利用するため、引数にサイズ指定がありません。しかし、memcmp関数では、データの終端という概念がないため、引数によってサイズを指定することになります。
memcmp関数の仕様について
memcmp関数は、第1引数と第2引数は比較したいメモリへのポインタを指定します。
includeファイル | string.h |
関数仕様 | int memcmp(const void * buf1, const void * buf1, size_t n); |
引数1 | 比較対象のメモリへのポインタ |
引数2 | 比較対象のメモリへのポインタ |
引数3 | 比較サイズを指定 |
戻り値 | 比較結果 0:メモリ内容が一致 正の値:違いがあり、buf1の数値が大きい 負の値:違いがあり、buf2の数値が大きい |
特記事項 | 2つのメモリ内容をサイズ分まで完全一致かを判定する |
特徴的なのは引数がvoid型ポインタになっていることです。
memcmp関数を使ったサンプルプログラム
memcmp関数の引数はvoid型ポインタのため、次のようにあらゆる型へのポインタを指定することが可能です。変数同士、文字列同士、配列同士いずれも指定が可能なのです。
#include <stdio.h>
#include <string.h>
int main(void)
{
long buf1 = 100;
long buf2 = 100;
short num1[] = { 10, 20, 30 };
short num2[] = { 10, 20, 30 };
char moji1[] = "Hello";
char moji2[] = "Hello";
// 変数の比較
printf("%d\n", memcmp(&buf1, &buf2, sizeof(buf1)));
// 文字列の比較
printf("%d\n", memcmp(moji1, moji2, sizeof(moji1)));
// 配列の比較
printf("%d\n", memcmp(num1, num2, sizeof(num1)));
return 0;
}
memcmp関数は、あらゆる情報をメモリ単位で比較できるのが特徴です。
memcmp関数で構造体を比較するときと使う時の注意点
memcmp関数には構造体データへのポインタも指定することが可能です。
#include <stdio.h>
#include <string.h>
typedef struct
{
short num1;
short num2;
} S_TEST;
int main(void)
{
S_TEST buf1 = { 0x1234, 0x5678 };
S_TEST buf2 = { 0x1234, 0x5678 };
printf("%d\n", memcmp(&buf1, &buf2, sizeof(buf1)));
return 0;
}
注意点としては、構造体は構造体メンバのデータ型によって、パディング領域が発生する可能性があります。
そのため、パディング領域が不定値の状態ではメンバの値は一致していても、一致判定ができないことがあることに注意です。
次のプログラムはパディングが発生する構造体定義と変数定義です。
#include <stdio.h>
#include <string.h>
typedef struct
{
char num1;
short num2;
} S_TEST;
// グローバル変数
S_TEST buf1 = { 0x12, 0x5678 };
int main(void)
{
S_TEST buf2 = { 0x12, 0x5678 };
printf("%d\n", memcmp(&buf1, &buf2, sizeof(buf1)));
return 0;
}
-1
このように不一致の判定となります。これはパディング領域の不定値によって不一致判定がされたということです。
構造体について詳しく知りたい方は『C言語 構造体 struct【情報のパッケージ化とそのメリット】』の記事を見ておくとよいでしょう。
構造体をmemcmp関数で比較したければ、memset関数で事前に領域全てを0クリアするといった工夫が必要となります。
strcmp関数とmemcmp関数の使い分け
それぞれの比較関数ですが、次のシーンに応じて使い分けしましょう。
目的のシーンに合わせて関数を使い分けましょう!