web-dev-qa-db-ja.com

Cで構造体の等価性をどのように比較しますか?

標準Cで構造体の2つのインスタンスが等しいかどうかをどのように比較しますか?

196
Hans Sjunnesson

Cにはこれを行うための言語機能はありません。各自でそれを実行し、メンバーごとに各構造を比較する必要があります。

182
Greg Hewgill

memcmp(&a, &b, sizeof(struct foo))を使用したくなるかもしれませんが、すべての状況で機能するとは限りません。コンパイラーは構造体にアライメントバッファースペースを追加する場合があり、バッファースペース内のメモリ位置で見つかった値は特定の値であるとは限りません。

しかし、callocまたはmemsetを使用する前に構造のフルサイズを使用する場合、can do(shallowmemcmpとの比較(構造体にポインターが含まれる場合、ポインターが指すアドレスが同じ場合にのみ一致します)。

102
Sufian

たくさんやるなら、2つの構造を比較する関数を書くことをお勧めします。そうすれば、構造を変更した場合でも、比較を1か所で変更するだけで済みます。

方法については...すべての要素を個別に比較する必要があります

21
Ben

Memcmpを使用して、構造体のフィールド間でランダムなパディング文字が発生する可能性があるため、構造体の等価性を比較することはできません。

  // bad
  memcmp(&struct1, &struct2, sizeof(struct1));

このような構造体の場合、上記は失敗します。

typedef struct Foo {
  char a;
  /* padding */
  double d;
  /* padding */
  char e;
  /* padding */
  int f;
} Foo ;

安全のために、メンバーごとの比較を使用する必要があります。

18
structinator

@Gregは、一般的な場合に明示的な比較関数を記述する必要があることは正しいです。

次の場合、memcmpを使用できます。

  • 構造体には、NaNである可能性のある浮動小数点フィールドが含まれていません。
  • 構造体にはパディングが含まれていません(これを確認するにはclangで-Wpaddedを使用)OR構造体は初期化時にmemsetで明示的に初期化されます。
  • 明確ではあるが同等の値を持つメンバータイプ(Windows BOOLなど)はありません。

組み込みシステム向けのプログラミング(またはそれらで使用される可能性のあるライブラリの作成)をしているのでない限り、C標準の一部のケースについては心配しません。 32ビットまたは64ビットのデバイスには、ニアポインターとファーポインターの区別はありません。私が知っている埋め込みシステムには、複数のNULLポインターがありません。

別のオプションは、等式関数を自動生成することです。構造体定義を簡単な方法でレイアウトすると、単純なテキスト処理を使用して単純な構造体定義を処理できます。一般的なケースにはlibclangを使用できます。Clangと同じフロントエンドを使用するため、すべてのコーナーケースを正しく処理します(バグはありません)。

私はそのようなコード生成ライブラリを見たことがありません。ただし、比較的単純に見えます。

ただし、このように生成された等式関数は、アプリケーションレベルで間違った動作をすることがよくあります。たとえば、Windowsの2つのUNICODE_STRING構造体を浅くまたは深く比較する必要がありますか?

7
Demi

すべてのメンバーを(一度に)初期化しない限り、パディングを気にせずに非静的構造でmemcmp()を使用できることに注意してください。これはC90で定義されています。

http://www.pixelbeat.org/programming/gcc/auto_init.html

5
pixelbeat

memcmpは構造を比較せず、memcmpはバイナリを比較し、構造には常にゴミが存在するため、比較では常にFalseになります。

要素ごとに安全を比較し、失敗しません。

2
sergio

それはあなたが尋ねている質問が以下であるかどうかに依存します:

  1. これらの2つの構造体は同じオブジェクトですか?
  2. 彼らは同じ価値を持っていますか?

それらが同じオブジェクトであるかどうかを確認するには、2つの構造体へのポインターが等しいかどうかを比較します。それらが同じ値を持っているかどうかを一般的に調べたい場合は、詳細な比較を行う必要があります。これには、すべてのメンバーの比較が含まれます。メンバーが他の構造体へのポインタである場合、それらの構造体にも再帰する必要があります。

構造体にポインターが含まれない特別な場合、memcmpを実行して、データの意味を知らなくても、それぞれに含まれるデータのビットごとの比較を実行できます。

各メンバーの「等しい」の意味を必ず確認してください。intの場合は明らかですが、浮動小数点値またはユーザー定義型の場合はより微妙です。

2
domgblackwell

構造体にプリミティブのみが含まれている場合、または厳密な等価性に関心がある場合は、次のようなことができます。

 int my_struct_cmp(const struct my_struct * lhs、const struct my_struct * rhs)
 {
 return memcmp(lhs、rsh、sizeof(struct my_struct)); 
 } 

ただし、構造体に他の構造体または共用体へのポインターが含まれている場合は、プリミティブを適切に比較し、必要に応じて他の構造体と比較呼び出しを行う関数を作成する必要があります。

ただし、me​​mset(&a、sizeof(struct my_struct)、1)を使用して、ADT初期化の一部として構造体のメモリ範囲をゼロにする必要があることに注意してください。

1
Kevin S.