2017-10-05 4 views
2

Die Regel für den effektiven Typ in C99 und C11 sieht vor, dass Speicher ohne deklarierten Typ mit einem beliebigen Typ geschrieben werden kann und dass beim Speichern eines Werts eines Nicht-Zeichen-Typs der effektive Typ des Speichers entsprechend festgelegt wird.Ist diese Verwendung der Effektivtyp-Regel streng konform?

Abgesehen von der Tatsache, dass INT_MAX weniger als 123456789 sein könnte, wäre die Verwendung der Regel für den effektiven Typ im folgenden Code streng konform?

#include <stdlib.h> 
#include <stdio.h> 

/* Performs some calculations using using int, then float, 
    then int. 

    If both results are desired, do_test(intbuff, floatbuff, 1); 
    For int only, do_test(intbuff, intbuff, 1); 
    For float only, do_test(floatbuff, float_buff, 0); 

    The latter two usages require storage with no declared type.  
*/ 

void do_test(void *p1, void *p2, int leave_as_int) 
{ 
    *(int*)p1 = 1234000000; 

    float f = *(int*)p1; 
    *(float*)p2 = f*2-1234000000.0f; 

    if (leave_as_int) 
    { 
    int i = *(float*)p2; 
    *(int*)p1 = i+567890; 
    } 
} 

void (*volatile test)(void *p1, void *p2, int leave_as_int) = do_test; 

int main(void) 
{ 
    int iresult; 
    float fresult; 
    void *p = malloc(sizeof(int) + sizeof(float)); 
    if (p) 
    { 
    test(p,p,1); 
    iresult = *(int*)p; 
    test(p,p,0); 
    fresult = *(float*)p; 
    free(p); 
    printf("%10d %15.2f\n", iresult,fresult); 
    } 
    return 0; 
} 

Aus meiner Lektüre der Norm, alle drei Verwendungen der Funktion im Kommentar beschrieben sollte (mit Ausnahme der integer-Bereich Ausgabe) streng konform sein. Der Code sollte daher 1234567890 1234000000.00 ausgeben. GCC 7.2 gibt jedoch 1234056789 1157904.00 aus. Ich denke, wenn leave_as_int 0 ist, speichert es 123400000 zu *p1, nachdem es 123400000.0f zu *p2 speichert, aber ich sehe nichts im Standard, das solches Verhalten autorisieren würde. Fehle ich etwas oder ist GCC nicht konform?

Antwort

0

Der erzeugte Maschinencode schreibt bedingungslos auf beiden Zeiger:

do_test: 
     cmpl $1, %edx 
     movl $0x4e931ab1, (%rsi) 
     sbbl %eax, %eax 
     andl $-567890, %eax 
     addl $1234567890, %eax 
     movl %eax, (%rdi) 
     ret 

Dies ist ein GCC Fehler, da alle Läden zu change the dynamic type of the memory accessed erwartet werden. Ich denke nicht, dass dieses Verhalten vom Standard vorgeschrieben ist; es ist eine GCC-Erweiterung.

Können Sie einen GCC-Fehler einreichen?

+1

Mein Lesen des verknüpften Beitrags schlägt vor, dass Geschäfte den dynamischen Typ von Objekten ändern * mit einem deklarierten Typ * ist eine Erweiterung, aber ein solches Verhalten wird vom Standard für Objekte ohne deklarierten Typ [z. ein Zeiger, der von 'malloc()'] empfangen wird. Die Einhaltung des Standards würde erfordern, dass ein Compiler bestimmte pessimistische Annahmen in Fällen trifft, die oft unnötigen ineffizienten Code ergeben, und es wäre für Compiler sinnvoll, einen * nichtkonformen * Modus anzubieten, der dynamische/effektive Objekte nicht berücksichtigen würde In solchen Fällen zu ändernde Typen. – supercat

+0

Sonst bin ich momentan nicht in der Datei gcc bugs registriert, obwohl ich mich vielleicht registrieren sollte. Ich bin mir jedoch nicht sicher, was als Abhilfe vorgeschlagen werden sollte, wenn das Verhalten nicht als konform beschrieben werden kann. Da die Einhaltung des Standards in einigen Fällen unnötigerweise die Optimierung behindern würde, kann es praktisch sein, einfach zu sagen, dass "-fno-strict-aliasing" für die vollständige Konformität erforderlich ist und dass "-fstrict-aliasing" nur mit Code verwendet werden kann, der niemals existiert recycelt Speicher. Leider würde dies bedeuten, dass Code, der den Speicher recyceln muss, ineffizient verarbeitet wird. – supercat

0

Ja, das ist ein GCC Bug. Ich habe es (mit einem vereinfachten Testfall) als https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82697 abgelegt.

+0

Danke für das Posten dieses Bugs. Interessant, dass dieser Fehler bei der Bestellung von Ladengeschäften eine so schnelle Lösung gefunden zu haben scheint, aber 82697 scheint länger zu dauern. Ich weiß nicht, dass die Autoren des Standards die Pessimisierungen vorschreiben wollten, die erforderlich waren, um zu erkennen, dass "* pi" und "* pl" auch dann alias sind, wenn es keinen Beweis dafür gibt *, aber sicherlich, wenn Aliasing existiert (wie im Beispiel oben bei den Pointercasts), die Möglichkeit zu erkennen ist nicht unangemessen pessimistisch, sondern einfach realistisch. – supercat

Verwandte Themen