Warum segfault dieser Code auf einer 64-Bit-Architektur, funktioniert aber auf 32-Bit einwandfrei?

Lesezeit: 5 Minuten

Benutzeravatar von user7
Benutzer7

Dabei bin ich auf folgendes C-Puzzle gestoßen:

F: Warum segfault das folgende Programm auf IA-64, funktioniert aber auf IA-32 einwandfrei?

  int main()
  {
      int* p;
      p = (int*)malloc(sizeof(int));
      *p = 10;
      return 0;
  }

Ich weiß, dass die Größe von int auf einem 64-Bit-Computer ist möglicherweise nicht die gleiche Größe wie ein Zeiger (int könnte 32 Bit und der Zeiger 64 Bit sein). Aber ich bin mir nicht sicher, wie dies mit dem oben genannten Programm zusammenhängt. Irgendwelche Ideen?

  • Ist es etwas Dummes wie stdlib.h nicht enthalten?

    – Benutzer786653

    25. September 2011 um 12:07 Uhr

  • Dieser Code läuft gut auf meinem 64-Bit-Rechner. Es wird sogar ohne Warnungen kompiliert, wenn Sie #include stdlib.h (für malloc)

    – mpenkow

    25. September 2011 um 12:08 Uhr

  • D’oh! @ user786653 hat das Wichtige getroffen. Mit #include <stdlib.h>es ist perfekt zu finden, aber das steht nicht in Frage.

    Benutzer395760

    25. September 2011 um 12:08 Uhr


  • @delnan – es muss jedoch nicht so funktionieren, es könnte auf einer Plattform, auf der es legitim ist, fehlschlagen sizeof(int) == sizeof(int*)wenn zum Beispiel Zeiger durch ein anderes Register zurückgegeben wurden ints in der verwendeten Aufrufkonvention.

    – Flexo

    25. September 2011 um 13:29 Uhr

  • In einer C99-Umgebung sollte der Compiler Sie zumindest vor der impliziten Deklaration von warnen malloc(). GCC sagt: warning: incompatible implicit declaration of built-in function 'malloc' zu.

    – Jonathan Leffler

    25. September 2011 um 20:34 Uhr

Benutzeravatar von Flexo
Flexo

Die Besetzung zu int* maskiert die Tatsache, dass ohne die richtige #include der Rückgabetyp von malloc wird vermutet int. IA-64 hat zufällig sizeof(int) < sizeof(int*) was dieses Problem deutlich macht.

(Beachten Sie auch, dass es aufgrund des undefinierten Verhaltens auch auf einer Plattform fehlschlagen kann, auf der sizeof(int)==sizeof(int*) gilt, zum Beispiel wenn die Aufrufkonvention andere Register für die Rückgabe von Zeigern als Ganzzahlen verwendet)

Das comp.lang.c FAQ hat einen Eintrag, in dem diskutiert wird, warum die Rückkehr aus gecastet wird malloc wird nie benötigt und ist möglicherweise schlecht.

  • Warum wird ohne das richtige #include angenommen, dass der Rückgabetyp von malloc ein int ist?

    – Benutzer7

    25. September 2011 um 12:15 Uhr

  • @WTP – was ein guter Grund ist, es immer zu verwenden new in C++ und kompilieren Sie C immer mit einem C-Compiler und nicht mit einem C++-Compiler.

    – Flexo

    25. September 2011 um 12:16 Uhr

  • @user7 – das sind die Regeln. Es wird angenommen, dass jeder Rückgabetyp ist int wenn es nicht bekannt ist

    – Flexo

    25. September 2011 um 12:17 Uhr

  • @vlad – die bessere Idee ist es stets Funktionen deklarieren, anstatt sich genau aus diesem Grund auf implizite Deklarationen zu verlassen. (Und nicht die Rückkehr aus werfen malloc)

    – Flexo

    25. September 2011 um 12:19 Uhr

  • @user7: “Wir haben einen Zeiger p (der Größe 64), der auf 32 Bit Speicher zeigt” – falsch. Die Adresse des von malloc zugewiesenen Blocks wurde gemäß der Aufrufkonvention für a zurückgegeben void*. Aber der aufrufende Code denkt, dass die Funktion zurückkehrt int (da Sie sich entschieden haben, es nicht anders zu sagen), also versucht es, den Rückgabewert gemäß der Aufrufkonvention für an zu lesen int. Somit p tut nicht zeigen zwangsläufig auf den zugewiesenen Speicher. Es funktionierte einfach so für IA32, weil ein int und ein void* haben die gleiche Größe und werden auf die gleiche Weise zurückgegeben. Auf IA64 erhalten Sie den falschen Wert.

    – Steve Jessop

    25. September 2011 um 12:41 Uhr


Benutzeravatar von paxdiablo
paxdiablo

Höchstwahrscheinlich, weil Sie es sind nicht inklusive die Header-Datei für malloc und obwohl der Compiler Sie normalerweise davor warnen würde, bedeutet die Tatsache, dass Sie den Rückgabewert explizit umwandeln, dass Sie ihm mitteilen, dass Sie wissen, was Sie tun.

Das heißt, der Compiler erwartet eine int von zurückgegeben werden malloc die es dann in einen Zeiger umwandelt. Wenn sie unterschiedliche Größen haben, wird Ihnen das Kummer bereiten.

Deshalb Sie noch nie werfen die malloc Rückkehr in C. Die void* das zurückgegeben wird, wird implizit in einen Zeiger des richtigen Typs konvertiert (es sei denn, Sie haben den Header nicht eingefügt, in diesem Fall hätte es Sie wahrscheinlich vor der potenziell unsicheren int-zu-Zeiger-Konvertierung gewarnt).

  • Entschuldigung, dass ich naiv klinge, aber ich bin immer davon ausgegangen, dass malloc einen void-Zeiger zurückgibt, der in einen geeigneten Typ umgewandelt werden kann. Ich bin kein C-Programmierer und würde mich daher über ein wenig mehr Details freuen.

    – Benutzer7

    25. September 2011 um 12:19 Uhr

  • @user7: Ohne das #include nimmt der C-Compiler an, dass der Rückgabewert von malloc ein int ist.

    – Sashang

    25. September 2011 um 12:31 Uhr

  • @user7: Der void-Zeiger kann gecastet werden, wird aber in C as nicht benötigt void * kann implizit in jeden anderen Zeigertyp konvertiert werden. int *p = malloc(sizeof(int)) funktioniert, wenn der richtige Prototyp im Geltungsbereich ist, und schlägt fehl, wenn dies nicht der Fall ist (weil dann angenommen wird, dass das Ergebnis int). Mit dem Cast würde beides kompilieren und letzteres würde bei der Ausführung zu Fehlern führen sizeof(int) != sizeof(void *).

    Benutzer395760

    25. September 2011 um 12:31 Uhr


  • @user7 Aber wenn du nicht einschließt stdlib.hder Compiler weiß es nicht malloc und weder seinen Rückgabetyp. Es wird also nur angenommen int als Standard.

    – Christian Rau

    25. September 2011 um 12:38 Uhr


Aus diesem Grund kompilieren Sie niemals ohne Warnungen über fehlende Prototypen.

Aus diesem Grund wird die malloc-Rückgabe in C niemals gecastet.

Die Umwandlung wird für die C++-Kompatibilität benötigt. Es gibt wenig Grund (sprich: hier keinen Grund), es wegzulassen.

C++-Kompatibilität ist nicht immer erforderlich und in einigen Fällen überhaupt nicht möglich, aber in den meisten Fällen ist sie sehr einfach zu erreichen.

  • Warum um alles in der Welt sollte es mich interessieren, ob mein C-Code mit C++ “kompatibel” ist? Es ist mir egal, ob es mit Perl oder Java oder Eiffel oder … kompatibel ist.

    – Stefan Kanon

    25. September 2011 um 18:05 Uhr

  • Wenn Sie garantieren, dass jemand auf der ganzen Linie Ihren C-Code nicht ansieht und sagt, hey, ich werde ihn mit einem C++-Compiler kompilieren, weil das funktionieren sollte!

    – Steven Lu

    25. September 2011 um 18:32 Uhr

  • Das ist Ursache für den meisten C-Code trivial C++-kompatibel gemacht.

    – Neugieriger

    26. September 2011 um 21:22 Uhr


1422810cookie-checkWarum segfault dieser Code auf einer 64-Bit-Architektur, funktioniert aber auf 32-Bit einwandfrei?

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

Privacy policy