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


回答:


196

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


19
2つの構造体変数がcallocで初期化されているか、memsetによって0に設定されている場合、2つの構造体をmemcmpと比較でき、構造体のガベージの心配がなく、これにより時間を稼ぐことができます
MOHAMED

21
@MOHAMEDとの浮動小数点フィールドの比較0.0, -0.0 NaNはの問題memcmp()です。バイナリ表現が異なるポインタは同じ場所(たとえば、DOS:seg:offset)を指す場合があるため、同じです。一部のシステムには、同等に比較する複数のnullポインターがあります。int-0のあいまいな場合、および冗長なエンコーディングを使用する浮動小数点型の場合も同じです。(Intel long double、decimal64など)これらの問題は、calloc()使用されているかどうか、またはパディングに違いはありません。
chux-モニカの復活2015年

2
オン@chux 任意の唯一の問題は、浮動小数点である私の知っている現代の32ビットまたは64ビットシステム。
2017

2
なぜ==(私のように)構造が機能しないのか疑問に
思われる場合

4
@デミ:今日。Cプログラマーへの10番目の戒めは、「あなたは、「すべての世界はVAXである」と主張する卑劣な異端を予告し、放棄し、そして不当に扱うべきです...」です。これを「世界のすべてのPC」に置き換えることは、改善ではありません。
Martin Bonnerがモニカをサポートする

110

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

ただし、構造体を使用する前、callocまたはmemset構造体のフルサイズを使用する場合は、と浅い比較を行うことができます(構造体にポインターが含まれている場合、ポインターが指しているアドレスが同じ場合にのみ一致します)。memcmp


19
「ほぼすべて」のコンパイラで動作するため、近いですが、完全ではありません。C90の6.2.1.6.4を確認してください:「同じオブジェクト表現を持つ2つの値(NaN以外)は等しいが、等しい値は異なるオブジェクト表現を持つ場合があります。」
スティーブジェソップ

22
「BOOL」フィールドを考えます。同等性の点では、ゼロ以外のBOOLはすべてのゼロ以外のBOOL値と等しくなります。したがって、1と2は両方ともTRUEであり、したがって等しい場合、memcmpは失敗します。
ajs410 2013

4
@JSalazarおそらくあなたにとってはより簡単ですが、コンパイラとCPUにとってははるかに困難であり、したがってはるかに遅くなります。コンパイラが最初にパディングを追加すると思いますか?確かに何もメモリを無駄にしないでください;)
Mecki 2014年

4
@Demetri:たとえば、フロート値の正と負のゼロは、どのIEEEフロート実装でも同じですが、同じオブジェクト表現を持ちません。したがって、実際には「ほとんどすべてのコンパイラ」で機能するとは言わないでください。負のゼロを格納できる実装では失敗します。コメントを書いたとき、私はおそらくおかしい整数表現を考えていました。
スティーブジェソップ2015

4
@Demetri:しかし、多くは浮動小数点数を含んでいます。質問者は、「浮動小数点数を含まない構造体をどのように比較するのか」ではなく、「構造体を比較する方法」を尋ねます。この回答はmemcmp、メモリが最初にクリアされていれば、と比較して浅い比較ができると述べています。これは動作に近いですが、正しくありません。多くの場合、質問は「同等」を定義しません。したがって、「オブジェクト表現のバイト単位の同等」を意味する場合、それmemcmpは正確にそれを行います(メモリがクリアされているかどうかにかかわらず)。
Steve Jessop、

22

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

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


1
一度だけ使用する場合でも、別の関数を記述します。
サム・

18

構造体のフィールド間の潜在的なランダムパディング文字のため、memcmpを使用して構造体を比較することはできません。

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

上記は、次のような構造体では失敗します。

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

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


25
ダブルの後にパディングされることはほとんどありません。チャーはダブルの直後に完全に適切に整列されます。
ジョナサンレフラー

7

@Gregは正しいですが、一般的なケースでは明示的な比較関数を作成する必要があります。

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

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

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

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

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

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


2
明示的に持つ構造体を初期化するmemset構造体要素にさらに書き込み後のパディングビットの値を保証するものではありませんなど、以下を参照してくださいstackoverflow.com/q/52684192/689161
gengkev

4

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

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


1
{0, }パディングバイトもゼロになるように実際に指定されていますか?
2015

GCCは、上記のリンクで示されているように、部分的に初期化された構造体のパディングバイトを少なくともゼロにし、stackoverflow.com / questions / 13056364 / C11がその動作を指定していることの詳細。
pixelbeat

1
すべてのパディングは、メンバーに割り当てると不確定になるため、一般的にはあまり有用ではありません
MM

2

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

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

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

構造体にポインタが含まれていない特殊なケースでは、memcmpを実行して、それぞれに含まれているデータのビット単位の比較を実行できます。データの意味を知る必要はありません。

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


2

memcmp構造をmemcmp比較せず、バイナリを比較し、構造体には常にガベージがあるため、比較すると常にFalseになります。

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


1
2つの構造体変数がcallocで初期化されているか、memsetによって0に設定されている場合、2つの構造体をmemcmpと比較でき、構造体のガベージの心配がなく、これにより時間を稼ぐことができます
MOHAMED

1

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

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)を使用する必要があったことに注意してください。


-1

2つの構造体変数がcallocで初期化されているか、memsetによって0に設定されている場合、2つの構造体をmemcmpと比較でき、構造体のガベージの心配がなく、これにより時間を稼ぐことができます。


-2

この準拠例では、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))) {
    /* ... */
  }
}

1
それは確かに正しいです。しかし、ほとんどの場合、構造体をパックしたくありません。非常に多くの命令とポインタでは、入力データをワード境界で整列させる必要があります。そうでない場合、コンパイラは、実際の命令を実行する前に、データをコピーして再配置するための追加の命令を追加する必要があります。コンパイラーがデータを再調整しない場合、CPUは例外をスローします。
Ruud Althuizen、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.