2013-12-17 12 views
5

Wiem, że memcmp() nie można użyć do porównania struktur, które nie zostały memset() na 0 z powodu niezainicjowanego wypełnienia. Jednak w moim programie mam strukturę z kilkoma różnymi typami na początku, a następnie kilkadziesiąt tego samego typu aż do końca struktury. Moją myślą było ręczne porównanie kilku pierwszych typów, a następnie użycie memcmp() na pozostałym sąsiednim bloku pamięci tych samych wpisanych członków.Porównywanie struktur w C za pomocą memcmp() i arytmetyki wskaźnika

Moje pytanie brzmi: co standard C gwarantuje o dopełnieniu struktury? Czy mogę niezawodnie osiągnąć to na każdym lub wszystkich kompilatorach? Czy standard C umożliwia wstawianie wypełnienia strukturalnego między członkami tego samego typu?

I wprowadziły mojego proponowanego rozwiązania, i to zdaje się dokładnie działa prawidłowo z 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

Drobne: Ponieważ Twój wskaźnik porównuje, zwracają 0 lub 1, co sugeruje, że ubezpieczenie 'memcmp()' zwraca 0 lub 1 za pomocą 'memcmp()! = 0'. – chux

+0

@chux Dobry pomysł, dziękuję za sugestię. – John

Odpowiedz

4

Nie ma gwarancji na to w standardzie C. Z praktycznego punktu widzenia jest to prawdą jako część ABI dla każdej aktualnej implementacji C i wydaje się, że nie ma sensu dodawać dopełnienia (np. Nie może być używane do sprawdzania przepełnień bufora, ponieważ program zgodny może zapisywać do wyściółka). Ale ściśle mówiąc, nie jest "przenośny".

0

Niestety, nie ma standardowej C (które kiedykolwiek słyszał), który pozwala kontrolować strukturę wyściółka. Jest faktem, że automatyczny przydział, który jest inicjowany jak ten

struct something val = { 0 }; 

spowoduje wszystkich członków w val być inicjowane 0. Ale dopełnienie jest pozostawione do implementacji.

Istnieją rozszerzenia kompilatora, których można użyć, jak GCC __attribute__((packed)), aby wyeliminować większość, jeśli nie wszystkie, dopełnienia struktury, ale poza tym może być utrata.

Wiem również, że bez większych optymalizacji na miejscu większość kompilatorów nie będzie zawracać sobie głowy dodawaniem dopełnienia struktury w większości przypadków, co wyjaśniałoby, dlaczego działa to w GCC.

Powiedział, że jeśli członkowie konstrukcja powoduje dziwne problemy wyrównania jak ten

struct something { char onebyte; int fourbyte; }; 

będą powodować kompilator dodać dopełnienie po członka onebyte spełniają wymogi wyrównanie członka fourbyte.

+1

To: 'struct something val = {0};' inicjuje pierwszego członka na 0, a następnie domyślnie inicjuje pozostałych członków (prawdopodobnie z 0, jeśli jest to ich domyślne ustawienie). 'struct something val = {};' domyślnie inializes wszystkich członków, które są bardziej ogólne, ponieważ pierwszy element może lub nie może być integralnym członkiem. –

+0

@JerryJeremiah prawda, ale to daje lepszy pomysł. – randomusername

+0

Podczas badania z 'gdb' stwierdziłem, że po" char a "dodano 7 bajtów dopełniających, tworząc w systemie cały 32 bajtowy struct (zamiast 25, co było w przypadku' __attribute __ ((__ packed __))) . Używając prostego 'memcmp()' na całej strukturze, nie były oczywiście równe. – John

Powiązane problemy