C言語入門|メモリ比較と初期化の基本関数

ここまでで、
「配列は = で比較してはいけない」
「配列は = で代入してはいけない」
という、C言語らしい厳しいルールを学んできました。

正直、不便ですよね。

でも安心してください。
C言語は 禁止するだけの言語 ではありません。
その代わりに、正しいやり方をちゃんと用意しています。

今回はその中から、

  • メモリの中身を比べる。
  • メモリを一気に初期化する。

という、実務で非常によく使われる
2つの基本関数 を紹介します。

なぜ比較や初期化に専用関数が必要なのか

まず、大前提を整理しておきましょう。

== 演算子で比較すると何が起きる?

int a[3];
int b[3];

if (a == b) {
    /* … */
}

この条件式は、

  • a と b の 中身を比べている
    のではなく
  • a と b の 先頭アドレスが同じか

を判定しています。

つまり、

同一の存在かどうか(等値判定)

を調べているだけです。

しかし実際にやりたいのは、多くの場合、

中身が同じかどうか(等価判定)

ですよね。

そこで登場するのが memcmp です。

memcmp ― メモリ領域をまるごと比較する

memcmp は、
指定したバイト数分のメモリ内容を、そっくりそのまま比較する
ための関数です。

配列だけでなく、
「メモリ領域そのもの」を比較する汎用関数です。

memcmp の書式

int memcmp(const void* addr1, const void* addr2, size_t len);

引数の意味

引数内容
addr1比較元の先頭アドレス
addr2比較先の先頭アドレス
len比較するバイト数

戻り値

戻り値意味
02つのメモリ領域の内容が同じ
0以外どこかに違いがある

配列を memcmp で比較してみよう

次の例では、
中身が同じ int 型配列を比較します。

サンプルプログラム

プロジェクト名:10-8-1 ソースファイル名: sample10-8-1.c

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

int main(void)
{
    int x[3] = {5, 10, 15};
    int y[3] = {5, 10, 15};

    int r = memcmp(x, y, sizeof(x));

    if (r == 0) {
        printf("memcmpの結果:配列の中身は同じです\n");
    }

    if (x == y) {
        printf("== の結果:同じ配列です\n");
    }

    return 0;
}

実行結果

memcmpの結果:配列の中身は同じです

なぜ == では比較できないのか

x == y が意味するのは、

  • x の先頭アドレス
  • y の先頭アドレス

完全に同じかどうか です。

今回の例では、

  • x と y は別々の場所に確保された
  • 別の配列

なので、== では常に偽になります。

判定の違いまとめ

判定の種類内容手段
等値判定同一の存在か== 演算子
等価判定中身が同じかmemcmp

memcmp を使うときの注意点

構造体の比較には要注意

構造体には、

  • パディング(使われない隙間)

が含まれることがあります。

そのため、

  • 見た目は同じ
  • メンバの値も同じ

でも、
パディング部分が異なっていると
memcmp は「違う」と判定してしまいます。

構造体の比較に memcmp を使うのは原則NG
と覚えておきましょう。

memset ― メモリ領域をまるごと初期化する

次に紹介するのは、初期化専用の関数です。

memset は、

指定したメモリ領域を
同じ1バイトの値で埋め尽くす

ための関数です。

もっともよく使われるのは、0での初期化です。

memset の書式

void* memset(void* addr, int val, size_t len);

引数の意味

引数内容
addr書き込み先の先頭アドレス
val書き込む値(1バイト分)
len書き込むバイト数

戻り値

  • addr と同じアドレス

配列を memset で初期化してみよう

サンプルプログラム

プロジェクト名:10-8 ソースファイル名: sample10-8-2.c

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

int main(void)
{
    int data[5];

    memset(data, 0, sizeof(data));

    printf("data[0]=%d\n", data[0]);
    printf("data[4]=%d\n", data[4]);

    return 0;
}

実行結果

data[0]=0
data[4]=0

memset の本当の意味を理解しよう

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

memset は、

  • 要素単位
    ではなく
  • バイト単位

で書き込みます。

そのため、

  • 0 で埋める → 安全
  • 0以外で埋める → 型によっては危険

という特徴があります。

よくある注意点

  • int配列を 1 で埋めても、すべて 1 になるとは限らない。
  • ポインタや構造体の初期化には慎重になる。

👉 memset は基本的に 0 初期化専用
と考えるのが安全です。

メモリ比較・初期化関数の使いどころ

ここまでの内容をまとめます。

関数役割
memcmpメモリ内容の等価判定
memsetメモリ領域の一括初期化

どちらも、

  • 配列
  • バッファ
  • 生のメモリ

を扱う 低レベルで強力な関数 です。

C言語らしい心構え

これらの関数はとても便利ですが、

  • サイズ指定を間違える。
  • 構造体に使ってしまう。
  • 意味を理解せず使う。

と、簡単にバグの温床になります。

C言語では常に、

何バイトを、どこに、何の目的で操作しているか

を意識することが大切です。