2016-05-13 11 views
1

Ich kann nicht verstehen, wie dieser Code einen segfault verursacht, wenn ich es ausführe, kann mir jemand helfen zu verstehen, was los ist ??Überraschende segfault in C-Code

#include <stdio.h> 

unsigned long long factorial(unsigned long long x, unsigned long long amt) 
{ 
    if (x == 1ULL) return amt; 
    else return factorial(x-1ULL, amt*x); 
} 

int main(int argc, char *argv[]) 
{ 
    for (unsigned long long i = 0; i < 10ULL ;i++) { 
     printf("%llu\n", factorial(i, 1ULL)); 
    } 
} 
+7

Was passiert, wenn Sie 'factorial (0, 1ULL)' aufrufen? TIPP: 'x == 1ULL' ist nicht wahr. Die Ergebnisse sind nicht überraschend. – lurker

+0

Hoppla jetzt fühle ich mich dumm :( –

+2

Hah ... keine Sorge. Manchmal kann ich die Wald für die Bäume nicht sehen. Sie können Ihre faktorielle Funktion vereinfachen, und brauchen nicht zwei Argumente. In Pseudo-C-Code: ' faktorial (n) {if (n <= 1) return 1; sonst return n * faktoriell (n-1);} ' – lurker

Antwort

2

In dem Originalcode:

if (x == 1ULL) return amt; 

soll factorial die Ausgangsbedingung für diese rekursive Funktion sein. Wenn jedoch ein Wert von Null an die Funktion übergeben wird und der Typ xunsigned long long ist, würde der erste rekursive Aufruf der Funktion factorial mit dem Parameter x-1ULL den Wert x auf einen sehr großen Wert setzen (18446744073709551615 ist was ich hier bekommen habe) . Aufeinanderfolgende rekursive Aufrufe an factorial werden den Stapelspeicherplatz, der für das Programm reserviert wird, allmählich zu dem Punkt verarmen, zu dem Sie einen Segmentierungsfehler erhalten.

Sie sollten dies getan haben:

#include <stdio.h> 

unsigned long long factorial(unsigned long long x, unsigned long long amt) 
{ 
     if (x == 0ULL) // Changed the exit condition, see Reference [1] 
       return amt; // Bear in mind that the initial value for amt you passed is 1 
     amt*=x; // See Reference [2] 
     return factorial(x-1ULL, amt); 
} 

int main(int argc, char *argv[]) { 
     for (unsigned long long i = 0; i < 10ULL ;i++) { 
       printf("%llu\n", factorial(i, 1ULL)); 
     } 

} 

Referenzen

  1. Die Idee der Fakultäts (in einfachen Worten) verwendet wird, um die Anzahl der Permutationen (Kombinationen) zu berechnen ordnen eine Reihe von n Zahlen. Man kann sagen, dass eine leere Menge nur in einer Richtung angeordnet werden kann, also 0! = 1. Überprüfen Sie this.
  2. Ich empfinde dies als factorial(x-1ULL, amt*x) besser lesbar ist

Hinweis

Die ULL Suffixe redundante hier sind und gänzlich entfernt werden.

+0

Was war das für downvote? – sjsam

+1

Nicht mein Downvote vielleicht, weil die Antwort nur eine Codeauflistung ohne eine Erklärung dessen ist, was das Problem ist und wie es behoben wird. Und deine Formatierung ist auch ein bisschen falsch. – kaylum

+0

@ kaylum: Ich stimme zu: DI habe die Erklärung hinzugefügt ist keine Verpflichtung, es wäre sinnvoll zu wissen, warum der Downvote war für – sjsam

2

Zuerst werden die segfaults nicht notwendigerweise ungültige Zeigerdereferenz verursacht. In diesem Fall wird es tatsächlich durch die unendliche Rekursion und den eventuellen Platzbedarf des Stacks verursacht. Warum? Die wesentliche Voraussetzung einer Rekursionsfunktion ist, dass sie die Rekursion in einem bestimmten Zustand beendet und beendet. Wenn Sie Ihren Code in der Funktion factorial genau ansehen, ist die Rekursion endlos und stürzt schließlich Ihr Programm ab, wenn x 0 ist. Sie können das beheben, indem Sie die Beendigungsbedingung ändern in:

if (x <= 1ULL) return amt; 
+1

Das ist richtig. Sie senden 'x = 0' und rufen dann' factorial (x-1ULL, amt * x); 'auf, was dasselbe ist wie faktoriell ((- 1) ULL, amt * x);'. Die -1 wird zu einer sehr großen vorzeichenlosen Zahl, die in einer Rekursion endet, die Sie aus dem Stack-Bereich herausführt. – TheGreatContini

+0

Technisch ist das keine "unendliche" Rekursion, sondern eine sehr tiefe. Wenn man "unsigned char" anstelle von "unsigned long long" verwendet, würde der erste Aufruf fast sofort beendet werden und "0" zurückgeben. –

+0

@ChristophTerasa Um genau zu sein, ist das richtig, aber wir sollten ein solches Verhalten nicht unterstützen. :) – fluter