2017-02-15 3 views
-1

Ich schreibe gerade eine neue String-Struktur in C wie unten und üben Zeigerarithmetik.C Pointer Arithmetik mit Struktur

struct String { 
    uint32_t check; 
    uint32_t capacity; 
    uint32_t length; 
    char data[1]; 

} String; 

und ich war ein bisschen an diesem Teil des Codes verwechselt

define STRING(s) ((String*)(s - 3*sizeof(uint32_t))) 

void utstrfree(char* self) { 

// if(*(self - sizeof(uint32_t) * 3) == SIGNATURE) WHY DOES THIS NOT >WORK? 

    assert((STRING(self)->check) == SIGNATURE);  //check if it's a >utstring 

    free(self-sizeof(uint32_t) * 3); //properly free the malloc and alternative could have been free(STRING(self)) 

} 

ich, wie mein STRING Makro funktioniert verstehen und es funktioniert, aber ich bin nicht ganz sicher, warum meine if-Anweisung nicht der Fall ist Arbeit. Kann mir das jemand erklären?

+0

Off Topic: ist dies magisch Resize "Struct" Trick koscher in modernen Standard C? – user4581301

+1

@ user4581301 Modernes C sollte ein flexibles Array-Element verwenden - 'char data [];', nicht 'char data [1];', sonst ist es legit (die Größe der Strings über char * an das flexible Array-Element zu manipulieren ist ein wenig fischig, obwohl - nicht sehr typsicher. – PSkocik

+2

Nein, @ user4581301, noch war es nie koscher unter irgendeinem vorherigen Standard, obwohl es tatsächlich * normalerweise funktioniert *. In diesen Tagen hat der Standard direkte Unterstützung für diese Art von Dingen über [flexible Array-Mitglieder] (http://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p18). –

Antwort

2

Wie @AslakBerby bereits beobachtet, der Ausdruck in Ihrer Behauptung, die ich gehe davon aus tut Arbeit, ist nicht gleichbedeutend mit der Bedingung Ausdruck in Ihrer kommentierten-out if Aussage. Da self insbesondere eine char * ist, hat der Ausdruck *(self - sizeof(uint32_t) * 3) den Typ char, während der Ausdruck STRING(self)->check den Typ uint32_t hat. Unter der Annahme, dass das Letztere überhaupt ein definiertes Verhalten hat, liefert das erstere nur das erste Byte des letzteren in systemspezifischer Speicherreihenfolge.

Insgesamt aber in dem Maße, dass diese allgemeine Regelung überhaupt funktioniert, ist es brüchig und unsicher:

  • Es macht Annahmen über die Darstellung des Typs struct String, die Implementierungen sind nicht erfüllen erforderlich;
  • Es überschreitet routinemäßig Array-Grenzen und ruft somit undefiniertes Verhalten auf;
  • Es bietet keinen Mechanismus zur Darstellung von In-Place-Teilstrings;
  • Es bietet genügend Interoperabilität mit den String-Funktionen der Standardbibliothek, um Benutzern eine größere Interoperabilität zu bieten, als sie tatsächlich bietet.

Empfehlungen:

  • Verlass auf offsetof() für Berechnungen der relativen Position innerhalb eines struct. Es ist sicherer und klarer.
  • Versuchen Sie nicht einmal für direkte Interoperabilität mit einfachen Array-Strings char - behandeln Sie diese Objekte als das, was sie tatsächlich sind. Schließlich geht es bei einer solchen Übung in der Regel nicht so sehr um die Datenstruktur selbst, sondern vielmehr um die damit verbundenen verbesserten Funktionen, wie etwa eine O (1) -Version von strlen(). Es ist nicht nötig, die Verwendung dieser Objekte mit den Funktionen der Standardbibliothek zu berücksichtigen.
  • Verwenden Sie einen Zeiger auf die Daten, anstatt die Daten direkt in das Objekt einzubetten. Dies würde sicherlich eine bessere Anpassung der Größenänderung ermöglichen und auch Teilstrings vor Ort unterstützen.
  • Wenn Sie die Daten direkt in das Objekt einbetten, verwenden Sie dazu ein flexibles Array-Element.
1

Nun, Ihr wenn ist nicht gleich dem Makro. Wenn Sie es geben sollte aus soll es so etwas wie dieses:

if(((String*)(self - 3*sizeof(uint32_t)))->check) == SIGNATURE) ... 

Sie müssen den Zeiger Sie typisieren zu erstellen, verwenden Sie dann das Attribut die Sie suchen.

+0

Insbesondere, da 'self' ein' char * 'ist, vergleicht die' if' Anweisung * ein 'char' * mit' SIGNATURE'. Das scheint nicht das zu sein, was er beabsichtigt. –

+0

Ja. Und weil wir nicht wissen, wie "SIGNATURE" definiert ist, ist es unmöglich zu sagen, warum nicht. Er könnte jedoch folgendes tun: 'if (* ((unit32_t *) (self - 3 * sizeof (uint32_t))) == SIGNATURE)' Sollte auch funktionieren. –