2015-12-01 6 views
6

Nach N1570 6,5/6:jede Compilern Übertragungs wirksame Art durch Memcpy/memmove

Wenn ein Wert in ein Objekt kopiert wird, das keine deklarierten Typ unter Verwendung Memcpy oder memmove oder kopiert wird als ein Array von Zeichentyp, dann ist der effektive Typ des geänderten Objekts für diesen Zugriff und für nachfolgende Zugriffe, die den Wert nicht ändern, der effektive Typ des Objekts, von dem der Wert kopiert wird, wenn es einen Wert hat.

, dass diese auch auf einem System vorschlagen würde, wo „lang“ und einige andere Integer-Typ die gleiche Darstellung haben, würde die folgende undefinierten Verhalten aufrufen:

#if ~0UL == ~0U 
    #define long_equiv int 
#elif ~0UL == ~0ULL 
    #define long_equiv long long 
#else 
#error Oops 
#endif 
long blah(void) 
{ 
    long l; 
    long_equiv l2; 
    long_equiv *p = malloc(sizeof (long)); 
    l = 1234; 
    memcpy(p, &l, sizeof (long)); 
    l2 = *p; 
    free(p); // Added to address complaint about leak 
    return l2; 
} 

da die Daten darauf durch l deutlich hat effektiven Typ long und das Objekt, auf das von verwiesen wird, hat keinen deklarierten Typ, der memcpy sollte den effektiven Typ des Speichers auf long setzen. Da das Lesen eines lval vom Typ long_equiv zum Lesen eines Objekts mit dem effektiven Typ long nicht zulässig ist, ruft der Code Undefined Behavior auf.

Vorausgesetzt, dass vor C99 memcpy eine der Standardmethoden zum Kopieren von Daten eines Typs in Speicher eines anderen Typs war, verursachen die neuen Regeln über memcpy viel vorhandenen Code zum Aufrufen von Undefined Behavior. Wenn die Regel stattdessen gewesen wäre, dass die Verwendung von memcpy zum Schreiben in zugewiesenen Speicher das Ziel ohne einen effektiven Typ verlässt, wird das Verhalten definiert.

Gibt es Compiler, die die effektive Art des Ziel ungesetzt, wenn sie zum Kopieren von Daten zu zugewiesenen Speicher nicht verhalten, als ob memcpy verlässt, oder sollte von memcpy zum Zwecke der Daten Übersetzung in Betracht gezogen werden „sicher“ nutzen? Wenn einige Compiler einen effektiven Typ der Quelle auf das Ziel anwenden, was wäre die richtige Art, Daten in typenunabhängiger Weise zu kopieren? Was ist mit "als Array von Zeichentypen kopiert" gemeint?

+0

Ihr Code ruft bereits UB auf, weil Sie nicht 'frei (p)', aber den Zeiger auf Rückkehr verlieren. Dein Punkt ist nicht ganz klar. IIRC, es war bereits UB in C89, einen Typ auf eine andere Art zu kopieren. Es ist einfach passiert und funktioniert wahrscheinlich immer noch. Der effektive Typ wird vom Compiler nicht "angewendet", sondern angenommen. Wie "const" qualifizierte Objekte garantiert der Programmierer dies und der Compiler verlässt sich darauf. Für den letzten Satz lesen Sie bitte die Beschreibung von 'memmove' &' memcpy'. Sie sollen 'char'-weise kopieren. – Olaf

+0

Meinst du '~ 0UL'? Weil "~ 0", "~ 0L" und "~ 0LL" im Zweierkomplement alle "-1" sind und alle gleich sind, unabhängig von der Darstellungsgröße –

+5

@Olaf: Ein Speicherleck ist kein undefiniertes Verhalten. –

Antwort

2

Der C-Standard besagt, dass der effektive Typ übertragen wird. Per Definition übertragen daher alle konformen Compiler den effektiven Typ.

Ihr Codebeispiel verursacht ein undefiniertes Verhalten, indem die strenge Aliasing-Regel verletzt wird, da ein Wert des effektiven Typs long von einem Lvalue des Typs long long gelesen wird.

Das war auch in C89 wahr, ich bin mir nicht sicher, was Sie über "neue Regeln in C99" sagen (abgesehen von der Tatsache, dass long long nicht in C89 war).

Es ist wahr, dass, wenn C standardisiert wurde, ein existierender Code undefiniertes Verhalten hatte. Und es stimmt auch, dass Menschen weiterhin Code mit undefiniertem Verhalten schreiben.

Was bedeutet "als ein Array von Zeichentypen kopiert"?

Dies bedeutet, dass Zeichen mit Zeichenart kopiert werden.

Was wäre der richtige Weg, Daten in typenunabhängiger Weise zu kopieren?

Es ist nicht möglich, "effektiven Typ zu löschen", soweit ich weiß. Um einen Wert unter Verwendung einer long long * korrekt zu lesen, müssen Sie auf eine Stelle des effektiven Typs long long zeigen.

In Ihrem Code, zum Beispiel:

// If we have confirmed that long and long long have the same size and representation 
long long x; 
memcpy(&x, p, sizeof x); 
return x; 

Union Aliasing ist eine weitere Option.

Wenn Sie nicht alles mögen, dann kompilieren Sie mit -fno-strict-aliasing.

+0

In C89 war das Aufrufen von memcpy auf nicht überlappenden Operanden semantisch äquivalent zu einer Schleife, die jedes Zeichen des Quelloperanden in das Ziel kopierte.Da Operationen, die den effektiven Typ von zugeordnetem Speicher festlegen, dies nur bis zum nächsten Zugriff tun, der den gespeicherten Wert modifiziert, und da zeichenartige Schreibvorgänge den effektiven Typ nicht festlegen, würde das Schreiben in zugewiesenen Speicher unter Verwendung eines zeichenartigen Zeigers das effektive löschen type davon, und memcpy würde dies auch in C99 tun, wenn nicht für die zitierte Regel, die, soweit ich weiß, keine Entsprechung in C89 hat. – supercat

+0

@supercat Ich werde den Text überprüfen und später auf Sie zurückkommen –

+0

Wenn das Kopieren Zeichen-nach-Zeichen den effektiven Typ überträgt, anstatt den effektiven Typ zu löschen (Objekte ohne effektiven Typ können wie jeder Typ gelesen werden), welcher Grad von Machenschaften muss man machen, um die "Charakter-Art" -Ausnahme zu den Aliasing-Regeln nützlich zu machen? – supercat

2

Experimentell verhält sich gcc 6.2 in einer Weise, die nur in Bezug auf memmove als Übertragung der effektiven Art der Quelle auf das Ziel vertretbar wäre. Wenn gcc feststellen kann, dass die Quellen- und Zielzeiger übereinstimmen, behandelt es den Speicheroperanden so, dass er nur über seinen früheren effektiven Typ lesbar ist, und nicht als Speicher, der zuletzt unter Verwendung eines Zeichentyps geschrieben wurde und somit unter Verwendung eines beliebigen Typs zugegriffen werden kann. Ein solches Verhalten wäre ohne die Regel nicht zu rechtfertigen, die es ermöglicht, Informationen vom effektiven Typ zu übertragen. Andererseits ist das Verhalten von gcc manchmal unter keiner Regel zu rechtfertigen, daher ist es nicht notwendigerweise klar, ob das Verhalten von gcc eine Folge der Interpretation des Standards durch die Autoren ist oder ob es einfach gebrochen ist. Wenn es z. B. bestimmen kann, dass das Zielziel memcpy das gleiche konstante Bitmuster wie die Quelle enthält, behandelt es die memcpy als keine Operation, auch wenn die Quelle den Typ enthält, der als nächstes zum Lesen des Zielspeichers verwendet wird , und das Ziel hatte einen anderen Typ, den der Compiler entschieden hatte, konnte den nächsten Lesevorgang nicht übernehmen.

Verwandte Themen