回答:
Cはこれを行うための言語機能を提供していません。自分で行い、メンバーごとに各構造体メンバーを比較する必要があります。
0.0, -0.0 NaN
はの問題memcmp()
です。バイナリ表現が異なるポインタは同じ場所(たとえば、DOS:seg:offset)を指す場合があるため、同じです。一部のシステムには、同等に比較する複数のnullポインターがあります。int
-0のあいまいな場合、および冗長なエンコーディングを使用する浮動小数点型の場合も同じです。(Intel long double、decimal64など)これらの問題は、calloc()
使用されているかどうか、またはパディングに違いはありません。
を使いたくmemcmp(&a, &b, sizeof(struct foo))
なるかもしれませんが、すべての状況で機能するとは限りません。コンパイラーは、アライメント・バッファー・スペースを構造体に追加する場合があり、バッファー・スペースにあるメモリー位置にある値は、特定の値であるとは限りません。
ただし、構造体を使用する前、calloc
またはmemset
構造体のフルサイズを使用する場合は、と浅い比較を行うことができます(構造体にポインターが含まれている場合、ポインターが指しているアドレスが同じ場合にのみ一致します)。memcmp
memcmp
、メモリが最初にクリアされていれば、と比較して浅い比較ができると述べています。これは動作に近いですが、正しくありません。多くの場合、質問は「同等」を定義しません。したがって、「オブジェクト表現のバイト単位の同等」を意味する場合、それmemcmp
は正確にそれを行います(メモリがクリアされているかどうかにかかわらず)。
構造体のフィールド間の潜在的なランダムパディング文字のため、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
ポインターがありません。
別のオプションは、等式関数を自動生成することです。構造体定義を単純な方法でレイアウトする場合、単純なテキスト処理を使用して単純な構造体定義を処理することが可能です。libclangは一般的な場合に使用できます。Clangと同じフロントエンドを使用しているため、すべてのコーナーケースを正しく処理します(バグがない限り)。
私はそのようなコード生成ライブラリを見たことがありません。ただし、それは比較的単純に見えます。
ただし、このように生成された等価関数がアプリケーションレベルで間違った動作をすることもよくあります。たとえばUNICODE_STRING
、Windowsの2つの構造体を浅くまたは深く比較する必要がありますか?
memset
構造体要素にさらに書き込み後のパディングビットの値を保証するものではありませんなど、以下を参照してくださいstackoverflow.com/q/52684192/689161
すべてのメンバーを(一度に)初期化しない限り、パディングを心配することなく、非静的構造でmemcmp()を使用できます。これはC90で定義されています。
{0, }
パディングバイトもゼロになるように実際に指定されていますか?
それはあなたが尋ねている質問が以下であるかどうかに依存します:
それらが同じオブジェクトであるかどうかを確認するには、ポインターが2つの構造体と等しいかどうかを比較します。一般的にそれらが同じ値を持っているかどうかを知りたい場合は、詳細な比較を行う必要があります。これには、すべてのメンバーを比較することが含まれます。メンバーが他の構造体へのポインターである場合、それらの構造体にも再帰する必要があります。
構造体にポインタが含まれていない特殊なケースでは、memcmpを実行して、それぞれに含まれているデータのビット単位の比較を実行できます。データの意味を知る必要はありません。
各メンバーの「等しい」の意味を知っていることを確認してください。intの場合は明らかですが、浮動小数点値やユーザー定義型の場合はより微妙です。
構造体にプリミティブのみが含まれている場合、または厳密な等価性に関心がある場合は、次のようにすることができます。
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))) {
/* ... */
}
}