Warum erhalte ich den Fehler „vom Zeiger in eine Ganzzahl unterschiedlicher Größe umgewandelt“?

Lesezeit: 7 Minuten

Benutzer-Avatar
SSD

Die folgende Zeile (reines c) kompiliert sauber weiter Fenster (win7 64 Bit + Codeblöcke 13 + mingw32) und debian (Wheezy 32 Bit + Codeblocks 10 + gcc) löst aber eine Warnung aus Kali (64 Bit + Codeblöcke + gcc). Irgendwelche Kommentare? Ich meine, warum erhalte ich diese Warnung, obwohl dieselbe Zeile ohne Warnung unter Windows und Debian kompiliert wird?

void* foo(void *dst, ...) {
    // some code
    unsigned int blkLen = sizeof(int); // this line ok.
    unsigned int offset = (unsigned int) dst % blkLen; // warning here!
    // some code cont...
}

Die Nachricht in Codeblöcken lautet: “Error: Cast von Zeiger auf Integer unterschiedlicher Größe [-Werror=pointer-to-int-cast]”

Hinweis: Meine Compileroptionen sind -std=c99 -Werror -save-temps (auf allen drei Systemen gleich).

Bearbeiten 2: Obwohl ich es geschafft habe, es ohne Warnung mit den folgenden Präprozessorzeilen zu kompilieren, hat @Keith Thompson (siehe unten) einen entscheidenden Punkt zu diesem Problem. Meine letzte Entscheidung ist also die Verwendung uintptr_t wäre die bessere Wahl.

Bearbeiten 1: Danke für alle geantwortet. Wie alle Antworten anmerken, handelt es sich bei dem Problem um ein 32-Bit- vs. 64-Bit-Problem. Ich habe folgende Präprozessorzeilen eingefügt:

#if __linux__   //  or #if __GNUC__
    #if __x86_64__ || __ppc64__
        #define ENVIRONMENT64
    #else
        #define ENVIRONMENT32
    #endif
#else
    #if _WIN32
        #define ENVIRONMENT32
    #else
        #define ENVIRONMENT64
    #endif
#endif // __linux__

#ifdef ENVIRONMENT64
    #define MAX_BLOCK_SIZE unsigned long long int
#else
    #define MAX_BLOCK_SIZE unsigned long int
#endif // ENVIRONMENT64

und ersetzte dann die Problemzeile als:

unsigned int offset = (MAX_BLOCK_SIZE) dst % blkLen;

Jetzt scheint alles in Ordnung zu sein.

  • Auf einigen Systemen/Compilern unterscheidet sich die Größe eines Int von der Größe eines Zeigers. Für ein typisches System beträgt der Offset-Wert jedoch 0 bis 3. Ein bisschen Casting des Ergebnisses des Modulo in unsigned int würde das Problem beheben.

    – Benutzer3629249

    7. November 2014 um 16:26 Uhr


Der Grund für die Warnung ist, dass der Compiler vermutet, dass Sie versuchen, einen Pointer per Roundtrip zu durchlaufen int und zurück. Dies war vor dem Aufkommen von 64-Bit-Computern gängige Praxis und weder sicher noch vernünftig. Natürlich kann der Compiler hier deutlich sehen, dass Sie dies nicht tun, und es wäre schön, wenn er klug genug wäre, die Warnung in solchen Fällen zu vermeiden, aber das ist es nicht.

Eine saubere Alternative, die die Warnung vermeidet, und ein weiteres, viel unangenehmeres Problem mit einem falschen Ergebnis, wenn der konvertierte Wert negativ ist, ist:

unsigned int offset = (uintptr_t) dst % blkLen;

Sie müssen einbeziehen stdint.h oder inttypes.h haben uintptr_t verfügbar.

Benutzer-Avatar
Keith Thompson

Das Problem ist, dass das Konvertieren von a void* Zeiger auf unsigned int ist von Natur aus nicht tragbar.

Der mögliche Größenunterschied ist nur ein Teil des Problems. Dieser Teil des Problems kann mit gelöst werden uintptr_tein Typ, der in definiert ist <stdint.h> und <inttypes.h>. uintptr_t ist garantiert breit genug, dass das Konvertieren von a void* zu uintptr_t und wieder zurück ergibt den ursprünglichen Zeigerwert (oder zumindest einen Zeigerwert, der dem ursprünglichen entspricht). Es gibt auch einen Typ intptr_t, die signiert ist; normalerweise sind unsignierte Typen für solche Dinge sinnvoller. uintptr_t und intptr_t sind nicht garantiert vorhanden, aber sie sollten in jeder Implementierung (C99 oder höher) vorhanden sein, die über die entsprechenden Integer-Typen verfügt.

Aber selbst wenn Sie einen Integer-Typ haben, der groß genug ist, um einen konvertierten Zeiger aufzunehmen, ist das Ergebnis nicht unbedingt für etwas anderes als die Rückwandlung in einen Zeiger von Bedeutung.

Die C-Norm sagt in einer nicht normativen Fußnote:

Die Abbildungsfunktionen zum Umwandeln eines Zeigers in eine Ganzzahl oder einer Ganzzahl in einen Zeiger sollen mit der Adressierungsstruktur der Ausführungsumgebung konsistent sein.

was nicht hilfreich ist, es sei denn, Sie wissen zufällig, was diese Adressierungsstruktur ist.

Sie scheinen zu versuchen, den Offset der zu bestimmen void* Argument ist relativ zum nächstniedrigeren Vielfachen von blkLen; Mit anderen Worten, Sie versuchen festzustellen, wie der Zeigerwert ist ausgerichtet in Gedenken an blkLen-große Speicherblöcke.

Wenn Sie wissen, dass das eine vernünftige Sache ist auf dem System, das Sie verwenden, das ist gut. Sie sollten sich jedoch darüber im Klaren sein, dass arithmetische Operationen mit ganzen Zahlen, die aus Zeigerkonvertierungen resultieren, immer noch von Natur aus nicht portierbar sind.

Ein konkretes Beispiel: Ich habe an Systemen (Cray-Vektormaschinen) gearbeitet, wo a void* Zeiger ist eine 64-Bit-Maschinenadresse (die auf ein 64-Bit-Wort zeigt), mit einem 3-Bit-Byte-Offset, das per Software in das ansonsten unbenutzte eingefügt wird hoher Auftrag 3 Bit. Das Konvertieren eines Zeigers in eine ganze Zahl kopierte einfach die Darstellung. Jede ganzzahlige Arithmetik mit einer solchen ganzen Zahl wird wahrscheinlich bedeutungslose Ergebnisse liefern, es sei denn, sie berücksichtigt diese (zugegebenermaßen exotische) Darstellung.

Schlussfolgerungen:

  1. Solltest du unbedingt nutzen uintptr_t anstatt Präprozessortricks zu spielen, um zu bestimmen, welchen Integer-Typ Sie verwenden können. Der Implementierer Ihres Compilers hat bereits die Arbeit erledigt, einen ganzzahligen Typ zu bestimmen, der einen konvertierten Zeigerwert sicher aufnehmen kann. Es gibt keine Notwendigkeit, dieses spezielle Rad neu zu erfinden. (Vorbehalt: <stdint.h> wurde durch den ISO-Standard von 1999 zu C hinzugefügt. Wenn Sie mit einem alten Compiler festsitzen, der ihn nicht implementiert, müssen Sie möglicherweise immer noch eine Art von verwenden #ifdef hackt. Aber ich würde trotzdem vorschlagen, zu verwenden uintptr_t wenn es verfügbar ist. Sie können testen __STDC_VERSION__ >= 199901L zum Testen auf C99-Konformität – obwohl einige Compiler dies möglicherweise unterstützen <stdint.h> ohne C99 vollständig zu unterstützen.)

  2. Sie müssen sich darüber im Klaren sein, dass das Konvertieren eines Zeigers in eine Ganzzahl und das Spielen mit seinem Wert nicht portierbar ist. Das soll nicht heißen, dass Sie es nicht tun sollten; Eine der größten Stärken von C ist seine Fähigkeit zu unterstützen nicht tragbar Code, wenn es das ist, was Sie brauchen.

  • Dies könnte der sein wertvollster Kommentar zum Thema. Danke vielmals. Ich werde Ihren Punkt berücksichtigen und die Frage erneut bearbeiten, damit alle Follower davon Gebrauch machen können.

    – SSD

    7. November 2014 um 20:45 Uhr

Benutzer-Avatar
b4hand

Weil Casting a void * zu einem unsigned int ist genau das, was diese Warnung abfangen soll, weil sie unsicher ist. Der Zeiger kann 64-Bit sein und die int kann 32-Bit sein. Für jede gegebene Plattform, die sizeof(unsigned int) ist nicht garantiert sizeof(void *). Du solltest benutzen uintptr_t stattdessen.

Vielleicht, weil auf einer 64-Bit-Architektur ein Zeiger 64 Bit lang ist und ein Int nur 32 Bit lang?

Du solltest es versuchen

void* foo(void *dst, ...) {
    // some code
    unsigned int blkLen = sizeof(int); // this line ok.
    uintptr_t offset = (uintptr_t) dst % blkLen; // warning here!
    // some code cont...
}

Ich denke, Sie erhalten die Warnung, weil die Größe von int von den Implementierungen abhängt, z. B. könnte int 2 Byte lang oder 4 Byte lang sein. Dies könnte der Grund für die Warnung sein (Bitte korrigieren Sie mich, wenn ich falsch liege). Aber warum versuchen Sie trotzdem, ein Modulo für einen Zeiger zu machen?

  • Auf viele aber nicht alles Systeme. Siehe das Cray-Beispiel in Keiths Antwort, wo es nicht funktionieren würde.

    – Chris Stratton

    7. November 2014 um 21:37 Uhr


Benutzer-Avatar
Neuling

Sie haben das Makro erstellt, aber denken Sie nicht, dass es immer noch falsch ist? Weil Ihr Zeiger in unsigned long long int oder unsigned long int konvertiert wird, was 32 Bit und 64 Bit in 86x und 64x OS ist, aber Ihr variabler Offset ist unsigned int, was 32 Bit in 64x und 86x OS ist. Also denke ich, dass Sie Offset auch in das jeweilige Makro umwandeln sollten.

Oder Sie können den Zeiger einfach in Long (dh unsigned int in long) und Offset in long (dh unsigned int in long) umwandeln.

  • Auf viele aber nicht alles Systeme. Siehe das Cray-Beispiel in Keiths Antwort, wo es nicht funktionieren würde.

    – Chris Stratton

    7. November 2014 um 21:37 Uhr


1186090cookie-checkWarum erhalte ich den Fehler „vom Zeiger in eine Ganzzahl unterschiedlicher Größe umgewandelt“?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy