2016-06-21 17 views
2

Ich bin neu in C und spiele damit herum. Also habe ich den Fibonacci-Code implementiert (iterativ und rekursiv). Ich habe eine Testfunktion geschrieben, die mir eine grün (meine Umsetzung funktioniert) oder rot geben soll. Es sagt, dass ich den richtigen Rückgabewert bekomme, aber der Status ist rot. Die beiden Werte sollten beide vorzeichenlos lang sein. Ich bin Kompilieren auf OSX mit MakeVergleicht man zwei gleich lange vorzeichenlose Länge in C ergibt sich als falsch

#include <stdio.h> 

unsigned long fibonacci(unsigned long n); 
void test_fibonacci(unsigned long n, unsigned long assertion); 

int main(int argc, char* argv[]) 
{ 
    test_fibonacci(1, 1); 
    test_fibonacci(2, 1); 
    test_fibonacci(3, 2); 
    test_fibonacci(10, 55); 
    return 0; 
} 

unsigned long fibonacci(unsigned long n) 
{ 
    unsigned long result = 1; 
    unsigned long lastResult; 
    for (unsigned long i = 2; i <= n; i++) 
    { 
     // save the current result to save it as the lastResult after this iteration 
     unsigned long lastResultTmp = result; 
     result = lastResult + result; 
     lastResult = lastResultTmp; 
    } 
    return result; 
} 

void test_fibonacci(unsigned long n, unsigned long assertion) 
{ 
    printf(
     "fibonacci(%lu): %lu | %s | asserted: %lu\n", 
     n, 
     fibonacci(n), 
     (fibonacci(n) == assertion) ? "green" : "red", 
     assertion 
    ); 
} 

Mein Makefile

CFLAGS=-Wall -g 

all: main 

clean: 
    rm -f main 
    rm -Rf *.dSYM 

Ausgang:

fibonacci(1): 1 | green | asserted: 1 
fibonacci(2): 1 | red | asserted: 1 
fibonacci(3): 2 | red | asserted: 2 
fibonacci(10): 55 | red | asserted: 55 

Antwort

3

Ich bin nicht die Ausgabe erhalten, die Sie sind. Hier ist, was ich sehe:

fibonacci(1): 1 | green | asserted: 1 
fibonacci(2): 2 | red | asserted: 1 
fibonacci(3): 4 | red | asserted: 2 
fibonacci(10): 3353 | red | asserted: 55 

Ich war neugierig, warum ich so lief ich es mit valgrind. Das schnell diesen Fehler aufgetaucht:

==5619== Conditional jump or move depends on uninitialised value(s) 
==5619== at 0x4005E7: test_fibonacci (fibonacci.c:31) 
==5619== by 0x400559: main (fibonacci.c:9) 

So sieht es aus wie diese mit einer nicht initialisierten Variablen zu tun hat, lesen Sie bekommen, was Sie die falschen Werte geben würde. Das deutet letztlich uns hier:

unsigned long fibonacci(unsigned long n) 
{ 
    unsigned long result = 1; 
    unsigned long lastResult; // <---- LOOK HERE 
    for (unsigned long i = 2; i <= n; i++) 
    { 
     // save the current result to save it as the lastResult after this iteration 
     unsigned long lastResultTmp = result; 
     result = lastResult + result; 
     lastResult = lastResultTmp; 
    } 
    return result; 
} 

Beachten Sie, dass lastResult nicht initialisiert ist, wird aber in der Linie

result = lastResult + result; 

So sieht es aus wie Sie diesen Wert initialisieren lesen müssen. Da dieser Wert der vorherigen Fibonacci-Nummer entspricht, sollten Sie auf Null initialisieren. Dadurch werden alle Tests bestanden.

Nun, was genau passiert ist, hat dazu geführt, dass es so aussieht, als ob Sie die richtige Antwort bekommen würden, aber immer noch scheitern? Beachten Sie, dass Sie zweimal in Ihrem Testcode fibonacci aufgerufen haben. Meine Vermutung ist, dass der erste Anruf an fibonacci - der, der gedruckt wurde - nur zufällig zufällig funktioniert, denn aus irgendeinem Grund ist der Wert in lastResult beim ersten Aufruf passiert 0. Allerdings werde ich raten dass der zweite Aufruf an fibonacci - der, der gegen das erwartete Ergebnis verglichen wurde - nicht den gleichen Wert wie der erste Aufruf zurückgegeben hat, weil aus irgendeinem Grund der Wert lastResult nicht 0 war, wenn dieser zweite Aufruf vorgenommen wurde. Das ist die Sache mit undefiniertem Verhalten - so etwas kann passieren!

+0

Vielen Dank! Das ist völlig richtig. Ich setze i wegen der ersten Fibonacci-Zahlen in der for-Schleife auf 3 und lastResult auf 1. Das löst meine Probleme. Ich muss Valgrind total überprüfen. – noeden

+0

@noeden Valgrind ist ein wunderbares Werkzeug, wenn Sie anfangen. Meine Empfehlung ist (1) kompilieren mit Warneinstellungen bis ganz nach oben, (2) Warnungen in Fehler verwandeln, und (3) Programme in Valgrind ausführen. Sie wären erstaunt, wie viele Fehler Sie auf diese Weise bekommen. :-) – templatetypedef

+0

Stimmen Sie der Notwendigkeit zu, 'lastResult' zu initialisieren, da dies nicht zu UB führt. Dennoch bin ich misstrauisch, dass etwas anderes OP dazu brachte, die "richtigen" Antworten zu bekommen, aber schade, vergleiche es. Hmmmm. UB ist UB. -> Aha, 'fibonacci()' hat die richtige Antwort jedes Mal für OP gemeldet. – chux

1

Fibonacci (1): 1 | grün | behauptet: 1

fibonacci (2): 3076653057 | rot | geltend gemacht: 1

fibonacci (3): 3076653058 | rot | behauptet: 2

fibonacci (10): 1526988855 | rot | behauptet: 55

Ich bekomme diese Ausgabe zu Ihrem Code. Und ich denke, das liegt an der nicht initialisierten Variable lastResult. Weil, als ich es mit 0 initialisiert habe, habe ich das richtige Ergebnis bekommen.

1

OP see comment.

Dies zeigt eine leichte Schwäche in Ihrem Test. Wenn fibonacci(n) aufgerufen wurde, lieferte es die richtige Antwort. Als (fibonacci(n) == assertion) angerufen wurde lieferte es die falsche Antwort.Die Schwäche, die Ihr Testcode fibonacci(n) zweimal pro Test statt einmal nannte. Als Ihr Code eine nicht initialisierte Variable hatte: @templatetypedef

// unsigned long lastResult; // bad 
unsigned long lastResult = 0; // good 

Mit UB (undefiniertes Verhalten) - das ist möglich.


Prüfregeln sollte haben die Testfunktion einmal genannt:

unsigned long f = fibonacci(n); 
printf("fibonacci(%lu): %lu | %s | asserted: %lu\n", 
    n, f, (f == assertion) ? `"green" : "red", assertion; 

Dann wird zumindest mit dem verirrten test_fibonacci(), viel wahrscheinlicher, konsistente Ergebnisse zu erhalten.


OTOH, diese Schwäche wies darauf hin, eine Stärke sein könnte, dass der Test hatte nur test_fibonacci() genannt einmal pro Schleife, die UB sich nicht auf eine schlechte Weise manifestiert haben.

+1

Danke! Ich werde das ändern. – noeden