2013-12-17 16 views
5

は、初期化されていないパディングのためにmemset()にない構造体を比較するために使用できません。しかし、私のプログラムでは、始めにいくつかの異なるタイプの構造体があり、構造体の終わりまで同じタイプの構造体が数十個あります。最初のいくつかの型を手動で比較し、同じ型付きメンバの残りの連続したメモリブロックにmemcmp()を使用することを考えました。memcmp()とポインタ算術を使ってCの構造体を比較する

私の質問は、C標準が構造体の埋め込みについて何を保証していますか?すべてのコンパイラで確実にこれを達成できますか? C標準では、同じタイプのメンバー間で構造体のパディングを挿入できますか?

私は提案されたソリューションを実装している、そしてそれはgccで意図どおりに動作するようです:

悲しいこと
#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 

struct foo 
{ 
    char a; 
    void *b; 
    int c; 
    int d; 
    int e; 
    int f; 
}; 

static void create_struct(struct foo *p) 
{ 
    p->a = 'a'; 
    p->b = NULL; 
    p->c = 1; 
    p->d = 2; 
    p->e = 3; 
    p->f = 4; 
} 

static int compare(struct foo *p1, struct foo *p2) 
{ 
    if (p1->a != p2->a) 
     return 1; 

    if (p1->b != p2->b) 
     return 1; 

    return 
     /* Note the typecasts to char * so we don't get a size in ints. */ 
     memcmp(
      /* A pointer to the start of the same type members. */ 
      &(p1->c), 
      &(p2->c), 
      /* A pointer to the start of the last element to be compared. */ 
      (char *)&(p2->f) 
      /* Plus its size to compare until the end of the last element. */ 
      +sizeof(p2->f) 
      /* Minus the first element, so only c..f are compared. */ 
      -(char *)&(p2->c) 
     ) != 0; 
} 

int main(int argc, char **argv) 
{ 
    struct foo *p1, *p2; 
    int ret; 

    /* The loop is to ensure there isn't a fluke with uninitialized padding 
    * being the same. 
    */ 
    do 
    { 
     p1 = malloc(sizeof(struct foo)); 
     p2 = malloc(sizeof(struct foo)); 

     create_struct(p1); 
     create_struct(p2); 

     ret = compare(p1, p2); 

     free(p1); 
     free(p2); 

     if (ret) 
      puts("no match"); 
     else 
      puts("match"); 
    } 
    while (!ret); 

    return 0; 
} 
+0

マイナー:あなたのポインタ比較が0か1を返すので、 'memcmp()'が 'memcmp()!= 0'で0または1を返すようにしてください。 – chux

+0

@chux良いアイデア、提案のおかげで。 – John

答えて

4

これはCの標準では保証されていません。実用的な観点からは、現在のC実装のABIの一部として真実であり、パディングを追加する目的はないようです(例えば、バッファオーバーフローに対するチェックには使用できませんでした。パディング)。しかし厳密に言えば、それは「可搬性」ではありません。

0

、あなたが構造を制御することができます(私が今まで聞いたことがあるということ)はC標準がありませんパディングこの

struct something val = { 0 }; 

のように初期化されている自動割り当てがval内のすべてのメンバーが0に初期化されることになりますという事実があります。しかし、その間のパディングは実装に委ねられています。

GCCの__attribute__((packed))のように使用できるコンパイラの拡張機能がありますが、すべての構造体パディングではありませんが、それ以外はほとんどなくなります。

大部分の最適化がなければ、ほとんどのコンパイラはほとんどの場合構造体パディングを追加する必要がないことがわかります。なぜこれがGCCで動作するのか説明します。あなたの構造体のメンバは、この

struct something { char onebyte; int fourbyte; }; 

のような奇妙なアライメントの問題が発生する場合、彼らは、コンパイラがfourbyteメンバーの整列要件を満たすためにonebyteメンバーの後にパディングを追加します、と述べ

+1

これは: 'struct something val = {0};'は最初のメンバーを0に初期化し、残りのメンバーをデフォルトで初期化します。 'struct something val = {};'は、最初の項目が不可欠なメンバーかもしれないし、そうでないかもしれないので、より一般的なすべてのメンバーを初期化します。 –

+0

@JerryJeremiah trueですが、これはより良いアイデアを得ることができます。 – randomusername

+0

'gdb'を調べてみると' char a'の後ろに7バイトのパディングバイトが追加され、私のシステムで32バイトの構造体全体が(__attribute __(__ packed __)の場合)25ではなく) 。構造体全体に単純な 'memcmp()'を使用した場合、それらは当然平等ではありませんでした。 – John