標準 C で構造体の 2 つのインスタンスが等しいかどうかを比較するにはどうすればよいですか?
11 に答える
C には、これを行うための言語機能がありません。自分で行って、各構造体のメンバーをメンバーごとに比較する必要があります。
を使用したくなるmemcmp(&a, &b, sizeof(struct foo))
かもしれませんが、すべての状況で機能するとは限りません。コンパイラはアライメント バッファ空間を構造体に追加する場合があり、バッファ空間にあるメモリ位置で見つかった値が特定の値であるとは限りません。
ただし、使用する前に構造体のフルサイズを使用calloc
するmemset
場合は、浅い比較を行うことができます (構造体にポインターが含まれている場合、ポインターが指しているアドレスが同じ場合にのみ一致します)。memcmp
頻繁に行う場合は、2 つの構造を比較する関数を作成することをお勧めします。そうすれば、構造を変更した場合でも、比較を 1 か所で変更するだけで済みます。
それを行う方法については....すべての要素を個別に比較する必要があります
構造体のフィールド間にランダムなパディング文字が含まれている可能性があるため、memcmp を使用して構造体が等しいかどうかを比較することはできません。
// bad
memcmp(&struct1, &struct2, sizeof(struct1));
上記は、次のような構造体では失敗します。
typedef struct Foo {
char a;
/* padding */
double d;
/* padding */
char e;
/* padding */
int f;
} Foo ;
安全のために、メンバーごとの比較を使用する必要があります。
@Gregは、一般的なケースで明示的な比較関数を作成する必要があることは正しいです。
次の場合に使用できますmemcmp
。
- 構造体には、可能性のある浮動小数点フィールドが含まれていません
NaN
。 - 構造体にパディングが含まれていない (
-Wpadded
clang を使用してこれを確認する)、または構造体が初期化memset
時に明示的に初期化されている。 BOOL
異なるが同等の値を持つメンバー型 ( Windows など) はありません。
組み込みシステム用にプログラミングしている (または組み込みシステムで使用される可能性のあるライブラリを作成していない) 場合を除き、C 標準のまれなケースについて心配する必要はありません。ニア ポインターとファー ポインターの区別は、32 ビットまたは 64 ビットのデバイスには存在しません。私が知っている非埋め込みシステムには、複数のNULL
ポインターはありません。
もう 1 つのオプションは、等値関数を自動生成することです。構造体定義を単純な方法で配置すると、単純なテキスト処理を使用して単純な構造体定義を処理できます。一般的なケースには libclang を使用できます。Clang と同じフロントエンドを使用するため、すべてのコーナー ケースを正しく処理します (バグを除く)。
このようなコード生成ライブラリは見たことがありません。ただし、比較的単純に見えます。
ただし、このように生成された等値関数がアプリケーション レベルで間違った動作をすることもよくあります。たとえばUNICODE_STRING
、Windows の 2 つの構造体は、浅く比較する必要がありますか?それとも深く比較する必要がありますか?
すべてのメンバーを (一度に) 初期化しない限り、パディングを気にせずに非静的構造体で memcmp() を使用できることに注意してください。これは C90 で次のように定義されています。
それは、あなたが尋ねている質問が次のとおりであるかどうかによって異なります。
- これら 2 つの構造体は同じオブジェクトですか?
- それらは同じ値を持っていますか?
それらが同じオブジェクトであるかどうかを確認するには、2 つの構造体へのポインターを比較して等しいかどうかを確認します。それらが同じ値を持っているかどうかを一般的に調べたい場合は、詳細な比較を行う必要があります。これには、すべてのメンバーの比較が含まれます。メンバーが他の構造体へのポインターである場合は、それらの構造体にも再帰する必要があります。
構造体にポインターが含まれていない特殊なケースでは、memcmp を実行して、データの意味を知らなくても、それぞれに含まれるデータのビットごとの比較を実行できます。
各メンバーの「等しい」が何を意味するかを確認してください。int の場合は明らかですが、浮動小数点値やユーザー定義型の場合はより微妙です。
memcmp
構造を比較せずmemcmp
、バイナリを比較し、構造体には常にガベージがあるため、比較すると常に False になります。
要素ごとに比較すると、安全で失敗しません。
構造体にプリミティブのみが含まれている場合、または厳密な等価性に関心がある場合は、次のようにすることができます。
int my_struct_cmp(const struct my_struct * lhs, const struct my_struct * rhs) { return memcmp(lhs, rsh, sizeof(struct my_struct)); }
ただし、構造体に他の構造体または共用体へのポインターが含まれている場合は、プリミティブを適切に比較し、必要に応じて他の構造体に対して比較呼び出しを行う関数を作成する必要があります。
ただし、ADT の初期化の一部として memset(&a, sizeof(struct my_struct), 1) を使用して、構造体のメモリ範囲をゼロにする必要があることに注意してください。
以下の適合例では、Microsoft Visual Studio の #pragma pack コンパイラ拡張機能を使用して、構造体のメンバーが可能な限り密にパックされるようにしている:
#include <string.h>
#pragma pack(push, 1)
struct s {
char c;
int i;
char buffer[13];
};
#pragma pack(pop)
void compare(const struct s *left, const struct s *right) {
if (0 == memcmp(left, right, sizeof(struct s))) {
/* ... */
}
}
2 つの構造体変数が calloc で初期化されているか、memset によって 0 に設定されている場合、2 つの構造体を memcmp と比較でき、構造体のガベージについて心配する必要がなく、時間を稼ぐことができます。