Ihr Code ist vollkommen in Ordnung
Sie sind absolut richtig und Ihr Lehrer ist falsch. Es gibt absolut keinen Grund, diese zusätzliche Komplexität hinzuzufügen, da sie das Ergebnis überhaupt nicht beeinflusst. Es führt sogar einen Fehler ein. (Siehe unten)
Erstens ist die separate Prüfung, ob nNull ist, offensichtlich völlig unnötig und dies ist sehr einfach zu realisieren. Um ehrlich zu sein, stelle ich die Kompetenz Ihres Lehrers in Frage, wenn er Einwände dagegen hat. Aber ich denke, jeder kann von Zeit zu Zeit einen Hirnfurz haben. Ich denke jedoch, dasswhile(n) dies geändert werden sollte, while(n != 0)da es ein wenig mehr Klarheit ohne auch nur eine zusätzliche Zeile zu kosten. Es ist jedoch eine Kleinigkeit.
Der zweite ist etwas verständlicher, aber er liegt immer noch falsch.
Dies ist der C11-Standard 6.5.5.p6 sagt :
Wenn der Quotient a / b darstellbar ist, ist der Ausdruck (a / b) * b + a% b gleich a; Andernfalls ist das Verhalten von a / b und a% b nicht definiert.
Die Fußnote sagt dies:
Dies wird oft als "Abschneiden gegen Null" bezeichnet.
Abschneiden gegen Null bedeutet, dass der Absolutwert für a/bgleich dem Absolutwert (-a)/bfür alle ista und ist b, was wiederum bedeutet, dass Ihr Code vollkommen in Ordnung ist.
Modulo ist eine einfache Mathematik, kann aber nicht intuitiv sein
Ihr Lehrer hat jedoch den Punkt, dass Sie vorsichtig sein sollten, da die Tatsache, dass Sie das Ergebnis quadrieren, hier tatsächlich entscheidend ist. Das Berechnen a%bgemäß der obigen Definition ist einfach, kann aber Ihrer Intuition widersprechen. Bei Multiplikation und Division ist das Ergebnis positiv, wenn die Operanden das gleiche Vorzeichen haben. Aber wenn es um Modulo geht, hat das Ergebnis das gleiche Vorzeichen wie der erste Operand. Der zweite Operand beeinflusst das Vorzeichen überhaupt nicht. Zum Beispiel 7%3==1aber (-7)%(-3)==(-1).
Hier ist ein Ausschnitt, der dies demonstriert:
$ cat > main.c
#include <stdio.h>
void f(int a, int b)
{
printf("a: %2d b: %2d a/b: %2d a\%b: %2d (a%b)^2: %2d (a/b)*b+a%b==a: %5s\n",
a, b ,a/b, a%b, (a%b)*(a%b), (a/b)*b+a%b == a ? "true" : "false");
}
int main(void)
{
int a=7, b=3;
f(a,b);
f(-a,b);
f(a,-b);
f(-a,-b);
}
$ gcc main.c -Wall -Wextra -pedantic -std=c99
$ ./a.out
a: 7 b: 3 a/b: 2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: 3 a/b: -2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: 7 b: -3 a/b: -2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: -3 a/b: 2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
Ironischerweise hat Ihr Lehrer seinen Standpunkt bewiesen, indem er sich geirrt hat.
Der Code Ihres Lehrers ist fehlerhaft
Ja, das ist es tatsächlich. Wenn die Eingabe INT_MINUND die Architektur das Zweierkomplement ist UND das Bitmuster, bei dem das Vorzeichenbit 1 ist und alle Wertbits 0 sind, KEIN Trap-Wert ist (die Verwendung des Zweierkomplements ohne Trap-Werte ist sehr häufig), führt der Code Ihres Lehrers zu einem undefinierten Verhalten auf der Linie n = n * (-1). Ihr Code ist - wenn auch nur geringfügig - besser als sein. Und wenn man bedenkt, einen kleinen Fehler einzuführen, indem man den Code unnötig komplex macht und einen Wert von absolut Null erreicht, würde ich sagen, dass Ihr Code VIEL besser ist.
Mit anderen Worten, in Kompilierungen mit INT_MIN = -32768 (obwohl die resultierende Funktion keine Eingabe von <-32768 oder> 32767 empfangen kann) verursacht die gültige Eingabe von -32768 undefiniertes Verhalten, da das Ergebnis von - (- 32768i16) kann nicht als 16-Bit-Ganzzahl ausgedrückt werden. (Tatsächlich würde -32768 wahrscheinlich kein falsches Ergebnis verursachen, da - (- 32768i16) normalerweise -32768i16 ergibt und Ihr Programm negative Zahlen korrekt verarbeitet.) (SHRT_MIN kann je nach Compiler -32768 oder -32767 sein.)
Ihr Lehrer hat jedoch ausdrücklich angegeben, dass ndies im Bereich [-10 ^ 7; 10 ^ 7]. Eine 16-Bit-Ganzzahl ist zu klein. Sie müssten [mindestens] eine 32-Bit-Ganzzahl verwenden. Die Verwendung intscheint seinen Code sicher zu machen, außer dass dies intnicht unbedingt eine 32-Bit-Ganzzahl ist. Wenn Sie für eine 16-Bit-Architektur kompilieren, sind beide Codefragmente fehlerhaft. Aber Ihr Code ist immer noch viel besser, weil dieses Szenario den INT_MINoben erwähnten Fehler mit seiner Version wieder einführt . Um dies zu vermeiden, können Sie longstattdessen schreiben int, was in beiden Architekturen eine 32-Bit-Ganzzahl ist. A kann longgarantiert jeden Wert im Bereich [-2147483647; 2147483647]. C11 Standard 5.2.4.2.1 LONG_MIN ist häufig-2147483648aber der maximal zulässige Wert (ja, maximal, es ist eine negative Zahl) fürLONG_MINist 2147483647.
Welche Änderungen würde ich an Ihrem Code vornehmen?
Ihr Code ist in Ordnung, es handelt sich also nicht wirklich um Beschwerden. Es ist eher so, wenn ich wirklich, wirklich etwas über Ihren Code sagen muss, gibt es einige kleine Dinge, die es nur ein kleines bisschen klarer machen könnten.
- Die Namen der Variablen könnten etwas besser sein, aber es ist eine kurze Funktion, die leicht zu verstehen ist, also keine große Sache.
- Sie können den Zustand von
nbis ändern n!=0. Semantisch ist es 100% äquivalent, aber es macht es ein bisschen klarer.
- Verschieben Sie die Deklaration von
c(in die ich umbenannt habe digit) in die while-Schleife, da sie nur dort verwendet wird.
- Ändern Sie den Argumenttyp,
longum sicherzustellen, dass er den gesamten Eingabesatz verarbeiten kann.
int sum_of_digits_squared(long n)
{
long sum = 0;
while (n != 0) {
int digit = n % 10;
sum += (digit * digit);
n /= 10;
}
return sum;
}
Tatsächlich kann dies etwas irreführend sein, da die Variable - wie oben erwähnt - digiteinen negativen Wert erhalten kann, eine Ziffer an sich jedoch niemals positiv oder negativ ist. Es gibt ein paar Möglichkeiten, dies zu umgehen, aber das ist WIRKLICH ein Trottel, und ich würde mich nicht für so kleine Details interessieren. Insbesondere die separate Funktion für die letzte Ziffer geht zu weit. Ironischerweise ist dies eines der Dinge, die der Code Ihres Lehrers tatsächlich löst.
- Wechseln Sie
sum += (digit * digit)zu sum += ((n%10)*(n%10))und überspringen Sie die Variable digitvollständig.
- Ändern Sie das Vorzeichen,
digitwenn negativ. Ich würde jedoch dringend davon abraten, den Code komplexer zu gestalten, nur um einen Variablennamen sinnvoll zu machen. Das ist ein sehr starker Code-Geruch.
- Erstellen Sie eine separate Funktion, die die letzte Ziffer extrahiert.
int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }Dies ist nützlich, wenn Sie diese Funktion an einer anderen Stelle verwenden möchten.
- Nennen Sie es einfach so,
cwie Sie es ursprünglich getan haben. Dieser Variablenname gibt keine nützlichen Informationen, ist aber auch nicht irreführend.
Aber um ehrlich zu sein, sollten Sie an dieser Stelle zu einer wichtigeren Arbeit übergehen. :) :)
n = n * (-1)ist eine lächerliche Art zu schreibenn = -n; Nur ein Akademiker würde daran denken. Ganz zu schweigen von den redundanten Klammern.