2013-12-17 9 views
5

Ich weiß, dass memcmp() nicht verwendet werden kann, um Strukturen zu vergleichen, die nicht memset() auf 0 wegen der nicht initialisierten Auffüllung waren. In meinem Programm habe ich jedoch eine Struktur mit ein paar verschiedenen Typen am Anfang, dann mehrere Dutzend vom selben Typ bis zum Ende der Struktur. Mein Gedanke war, die ersten paar Typen manuell zu vergleichen und dann einen memcmp() auf dem verbleibenden zusammenhängenden Speicherblock derselben typisierten Mitglieder zu verwenden.Vergleichen Sie Strukturen in C mit memcmp() und Zeigerarithmetik

Meine Frage ist, was garantiert der C-Standard über Strukturauffüllung? Kann ich dies bei einigen oder allen Compilern zuverlässig erreichen? Ermöglicht der C-Standard das Einfügen von Struct-Padding zwischen denselben Typmitgliedern?

Ich habe meine vorgeschlagene Lösung umgesetzt, und es scheint, genau zu arbeiten, wie mit gcc bestimmt:

#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

Minor: Da Ihr Zeiger vergleicht, gibt 0 oder 1 zurück, was darauf hindeutet, dass 'memcmp()' mit 'memcmp()! = 0' 0 oder 1 zurückgibt. – chux

+0

@chux Gute Idee, danke für den Vorschlag. – John

Antwort

4

Es gibt keine Garantie dafür in der C-Norm. Aus praktischer Sicht ist es als Teil der ABI für jede aktuelle C-Implementierung gültig, und es scheint keinen Sinn zu geben, Padding hinzuzufügen (z. B. könnte es nicht zum Prüfen auf Pufferüberläufe verwendet werden, da ein konformes Programm in die Polsterung). Aber streng genommen ist es nicht "tragbar".

0

Leider gibt es keine C-Norm (die ich je gehört haben), das Ihnen erlaubt Struktur zu steuern Polsterung. Es ist die Tatsache, dass die automatische Zuordnung, die wie dieser

struct something val = { 0 }; 

die Mitglieder alle in val verursacht initialisiert wird 0 initialisiert werden. Aber die Zwischenfüllung bleibt der Implementierung überlassen.

Es gibt Compiler-Erweiterungen, die Sie verwenden können wie GCC __attribute__((packed)), um die meisten, wenn nicht alle Strukturpolsterung zu eliminieren, aber abgesehen davon, dass Sie möglicherweise ratlos sind.

Ich weiß auch, dass die meisten Compiler ohne größere Optimierungen in den meisten Fällen keine Strukturauffüllung hinzufügen, was erklären würde, warum dies unter GCC funktioniert.

Das heißt, wenn Sie Ihre Strukturelemente wie diese

ungerade Ausrichtungsprobleme verursachen
struct something { char onebyte; int fourbyte; }; 

sie der Compiler Polsterung nach dem onebyte verursacht Mitglied hinzufügen, um die Ausrichtungsanforderungen des fourbyte Mitglied zu erfüllen.

+1

Dies: 'struct something val = {0};' initialisiert das erste Mitglied auf 0 und initialisiert dann die restlichen Mitglieder (möglicherweise mit 0, wenn das der Standard ist). 'struct something val = {};' default initialisiert alle Mitglieder, was generischer ist, da das erste Element ein integrales Mitglied sein kann oder nicht. –

+0

@JerryJeremiah wahr, aber das bringt die Idee über besser. – randomusername

+0

Als ich mit 'gdb' nachforschte, fand ich, dass nach' char a' 7 Füllbytes hinzugefügt wurden, wodurch die gesamte Struktur 32 Bytes auf meinem System hatte (statt 25, was bei '__attribute __ ((__ packed __)) der Fall war)') . Bei Verwendung einer einfachen 'memcmp()' auf der gesamten Struktur waren sie natürlich nicht gleich. – John