Frage

Testen Sie den folgenden Code ein:

#include <stdio.h>
#include <stdlib.h>
main()
{
    const char *yytext="0";
    const float f=(float)atof(yytext);
    size_t t = *((size_t*)&f);
    printf("t should be 0 but is %d\n", t);
}

Kompilieren mit:

gcc -O3 test.c

Der gute Ausgang soll sein:

"t should be 0 but is 0"

Aber mit meinem gcc 4.1.3, die ich habe:

"t should be 0 but is -1209357172"
War es hilfreich?

Lösung

Mit dem Compiler-Flag -fno-strict-Aliasing.

Mit strengem Aliasing aktiviert ist, wie sie standardmäßig für mindestens O3 in der Zeile ist:

size_t t = *((size_t*)&f);

Der Compiler geht davon aus, dass die size_t * NICHT als Schwimmer auf den gleichen Speicherbereich ist Punkt *. Soweit ich weiß, ist dies standardkonformen Verhalten (die Einhaltung der strengen Regeln Aliasing in dem ANSI-Standard Start um gcc-4, wie Thomas Kammeyer wies darauf hin).

Wenn ich mich richtig erinnere, können Sie einen Zwischenguss verwenden * auf char, dies zu umgehen. (Compiler übernimmt char * kann alles Alias)

Mit anderen Worten, versuchen Sie dies (es kann mich jetzt nicht testen, aber ich denke, es wird funktionieren):

size_t t = *((size_t*)(char*)&f);

Andere Tipps

In dem C99-Standard, das in 6,5-7 durch folgende Regel abgedeckt ist:

  

Ein Objekt hat seinen gespeicherten Wert nur durch einen Ausdruck L-Wertes zugegriffen hat, die eine von   die folgenden Arten: 73)

     
      
  • ein Typ kompatibel mit der effektiven Art des Objekts,

  •   
  • eine qualifizierte Version eines Typ kompatibel mit der effektiven Art des Objekts,

  •   
  • ein Typ, der mit oder ohne Vorzeichen-Typ ist mit dem effektiven Typ der entsprechenden   Objekt,

  •   
  • ein Typ, der mit oder ohne Vorzeichen Typ ist mit einer qualifizierten Version des entsprechenden   effektive Art des Objekts,

  •   
  • ein Aggregat oder Union Typ, der unter seinem einen der vorgenannten Arten umfasst   Mitglieder (einschließlich, rekursiv, ein Mitglied einer Unteraggregat oder enthalten Vereinigung) oder

  •   
  • ein Zeichentyp.

  •   

Der letzte Punkt ist, warum erstes Casting auf einen (char *) funktioniert.

Dies ist nicht mehr erlaubt nach C99 Regeln für Zeiger Aliasing. Pointers von zwei unterschiedlichen Typen können nicht an der gleichen Stelle im Speicher zeigen. Die Ausnahmen von dieser Regel sind ungültig und char Zeiger.

Also in Ihrem Code, wo Sie auf einen Zeiger von size_t werfen, kann der Compiler wählen, dies zu ignorieren. Wenn Sie den Float-Wert als size_t erhalten möchten, einfach zuweisen und der Schwimmer gegossen werden (abgeschnitten nicht gerundet) als solche:

size_t size = (size_t) (f); // dies funktioniert

Dies wird allgemein als Fehler gemeldet, aber in Wirklichkeit ist wirklich eine Funktion, die Optimierer erlaubt, effizienter zu arbeiten.

In gcc können Sie dies mit einem Compiler-Schalter deaktivieren. Ich glaube, -fno_strict_aliasing.

Es ist schlimm, C-Code: -)

Der problematische Teil ist, dass Sie ein Objekt vom Typ float zugreifen, indem sie auf einen ganzzahligen Zeiger Gießen und dereferencing es.

Dies bricht die Aliasing-Regel. Der Compiler ist frei davon ausgehen, dass Verweise auf verschiedene Arten wie Schwimmer oder int nicht überlappen im Speicher. Sie haben genau das getan.

Was der Compiler sieht, ist, dass Sie etwas berechnen, sollte sie in dem Schwimmer f und es nie mehr zugreifen. Höchstwahrscheinlich wird der Compiler hat einen Teil des Codes entfernt und die Zuordnung hat nie passiert.

Der dereferencing über Ihren size_t Zeiger in diesem Fall wird einige nicht initialisierten Müll aus dem Stapel zurück.

Sie können zwei Dinge tun, um Arbeit-um diese:

  1. eine Vereinigung mit einem Schwimmer verwenden und ein size_t Mitglied und tut das Gießen über Typen punning. Nicht schön, aber funktioniert.

  2. verwenden memcopy den Inhalt von f in Ihre size_t zu kopieren. Der Compiler ist intelligent genug, um zu erkennen und diesen Fall zu optimieren.

Warum würden Sie denken, dass t 0 sein sollte?

Oder mehr accuractely ausdrückte: „Warum würden Sie denken, dass die binäre Darstellung einer Gleitkomma-Null würde die gleiche wie die binäre Darstellung einer ganzen Zahl Null sein?“

Das ist schlecht C-Code. Ihre Besetzung bricht C Aliasing Regeln, und der Optimierer ist frei Dinge tun, die diesen Code zu brechen. Sie werden wahrscheinlich feststellen, dass GCC die size_t lesen, bevor der Floating-Point-Schreib cheduled hat (fp Pipeline-Latenz zu verstecken).

Sie können den -fno-strict-aliasing-Schalter oder eine Vereinigung verwenden oder ein reinterpret_cast den Wert in einer standardkonformen Art und Weise neu zu interpretieren.

Neben der Zeiger Ausrichtungen, Sie erwarten, dass sizeof (size_t) == sizeof (float). Ich glaube nicht, es ist (auf 64-Bit-Linux-size_t sollte 64 Bit sein, aber 32 Bit float), Code bedeutet etwas nicht initialisierten lesen.

O3 nicht „gesund“ gelten, -O2 ist in der Regel die obere Schwelle außer vielleicht für einige Multimedia-Anwendungen.

Einige Anwendungen können nicht einmal so weit gehen, und sterben, wenn Sie über -O1 gehen.

Wenn Sie ein neu genug GCC (ich bin auf 4.3 hier), kann es diesen Befehl unterstützen

  gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts

Wenn Sie vorsichtig sind, werden Sie möglicherweise in der Lage sein, durch diese Liste zu gehen und die gegebene singuläre Optimierung Sie ermöglicht, die diesen Fehler verursacht zu finden.

Von man gcc:

  The output is sensitive to the effects of previous command line options, so for example it is possible to find out which
       optimizations are enabled at -O2 by using:

               -O2 --help=optimizers

       Alternatively you can discover which binary optimizations are enabled by -O3 by using:

               gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts
               gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
               diff /tmp/O2-opts /tmp/O3-opts | grep enabled

I getestet Code mit: "I686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. bauen 5465)"

und es war kein Problem. Ausgang:

t should be 0 but is 0

So gibt keinen Fehler in Ihnen Code. Das bedeutet nicht, dass es gut Code ist. Aber ich würde den Rückgabetyp der Haupt-Funktion hinzufügen und das „return 0;“ am Ende der Funktion.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top