2013-12-17 75 views
5

我知道memcmp()不能用來比較的是一直沒memset()爲0,因爲未初始化的填充結構比較結構中的溫度。然而,在我的程序中,我有一個結構在開始時有幾個不同類型,然後有幾十個相同類型,直到結構結束。我的想法是手動比較前幾種類型,然後在相同類型成員的其餘連續內存塊上使用memcmp()使用memcmp()和指針運算

我的問題是,什麼是關於結構填充C標準保證?我可以在任何或所有編譯器上可靠地實現這一點嗎? C標準是否允許在相同類型成員之間插入struct padding?

我已經實現了我提出的解決方案,它似乎工作完全按預期與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()'返回0或1'memcmp()= 0'!。 – 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; }; 

奇對齊問題,它們會導致編譯器的onebyte成員之後添加填充,以滿足fourbyte成員的對齊要求。

+1

這是:'struct something val = {0};'將第一個成員初始化爲0,然後默認初始化其餘成員(如果這是他們的默認成員,則可能使用0)。 'struct something val = {};'因爲第一個項目可能是或不是一個整體成員,所以'default'會初始化所有更通用的成員。 –

+0

@JerryJeremiah是真實的,但是這使得這個想法更好。 – randomusername

+0

當與'調查gdb'我發現有炭A'後'加入7-填充字節,使整個結構在我的系統的32字節(而不是25,這與'__attribute __((__包裝__))'的情況下) 。當在整個結構上使用簡單的'memcmp()'時,它們當然是不相等的。 – John