Programmieren in C segmentation fault (core dumped) Fehler

Wo habe ich "immer" geschrieben? Ich habe sogar den Konjunktiv benutzt. Ich frage mich: Weißt du denn, wie ein Compiler diese Rekursion einfach wegoptimieren kann?
 
Was spricht hiergegen ?

C:
int a, b, sum;

int ergebnis (int a, int b){
  sum = a + b;
  for (int i = 1; i < b; i++)
    {sum *= (a + b);}
  return sum;
}

int main ()
{
  scanf ("%d %d", &a, &b);
  printf ("%d", ergebnis (a, b));

  return 0;
}
 
@ShiftC Deine absolut inkonsistente Platzierung der geschweiften Klammern? :p
 
  • Gefällt mir
Reaktionen: kuddlmuddl
@ShiftC
Mehreres.
  • Compile Unit spezifische Variablen könnten lokal verwendet werden. Bei älterem C müssen die alle verwendeten Variablen am Anfang der Funktion stehen.
  • Funktionsname ergebnis ist nicht sprechend. Ist das wirklich Pow im eigentlichen Sinn? Ist es das gleiche Verhalten wie ursprünglich?
  • wenn es die pow Funktion darstellen soll, wird noch nicht alles rausgeholt. Wie kann man beispielsweise 3^16 noch schreiben?

@tollertyp
Versuche es nochmal. Versuche meine Frage theoretisch in Hinblick auf Wartung und Eindeutigkeit aufzufassen.

PS.: Ich poste hier keine Lösung; ich versuche nur ein wenig Wissen über gezielte Denkanstöße zu vermitteln.
 
@DaysShadow
Ja, erwischt :)

@titanskin
Mir ging es eher um die Formel zur expliziten Berechnung von (a+b)^b, weil das ja die Hauptintention des TE war. Die Benennung der Funktionen bzw. die Variablen hier sind natürlich undurchdacht, ja.
 
Und wenn das zu erlernende Programmierkonzept Rekursion sein sollte, selbst wenn unvernünftig, dann soll er es nicht rekursiv machen? Weißt du mehr über die Aufgabenstellung als wir? Ich dachte, der TE hat seine Gründe, warum er so realisiert...

Noch ein Tipp: Wenn man die Funktionen aus der Bibliothek nutzt, muss man es gar nicht selbst programmieren.
 
Vorsicht bei der Pow-Implementierung mittels For-Loop wenn b negativ ist.
 
  • Gefällt mir
Reaktionen: ShiftC
Guter Punkt, daran hatte ich gar nicht gedacht!

Dann müsste man entweder prüfen ob kleiner Null und entsprechend den Betrag nehmen oder eben direkt mit abs (). Zum Schluss natürlich 1/x. Dann ist wiederum hier int etwas fehlt am Platz.

Aber ja, gibt natürlich alles schon in der bib.
 
Eine Standardempfehlung für solche Diskussionen ist btw. der Compiler Explorer:
https://godbolt.org/z/8sva4e8Wx bzw. https://godbolt.org/z/xf3oc74Pe
Selbst wenn man kein Experte in assembly oder low-level Sprachen ist,
kann man eigentlich ganz gut nen Gefühl dafür bekommen, was gut bzw. schlecht aussieht.
 
  • Gefällt mir
Reaktionen: ShiftC und titanskin
Die von @ZuseZ3 erwähnte Seite visualisert schön, dass der ursprungliche Code schon mit -O2 keine Rekursion enthält.
Allerdings gibt es noch ein paar von mir offene Fragen, die ich nur theoretisch beantworten will.

Ich versuche euch mal genauer drauf zu stoßen:

Es ist für mich egal, ob die Funktion (a+b)^b oder c^a mit a+b=c heißt. Es kann immer auf pow(a,b) herunter gebrochen werden.
Wenn man sich das Potenzieren genauer anschaut, sieht man, dass wenn der Exponent ganzzahlig ist, es anders formuliert werden kann: 2^4 = 2^(2*2) = 2^(2^2) = (2^2)^2.
Gegeben a : int ,b : int (erstmal unbekannter Genauigkeit),
a^b = a^2*b_0 * ((a^2)^2)*b_1 * ... , wobei b_i das i-te von b ist.

Was ist hier die Optimierung? Ich reduziere die Anzahl der Multiplikationen, dadurch dass ich das Zwischenergebnis verwende.
Beispiel: 2^16 = ((2^2)^2))^2.

Das kann man schon ganz gut in Code gießen.

Wenn man sich jetzt noch die Datentypen ansieht, wird man feststellen, dass b gar nicht so groß sein kann. Da ein int nur maximal 2^31 - 1 = 0x7fffffff groß sein kann, 2 die kleinste Ganzzahl welche vom neutralen Element (bzgl mul) verschieden ist, ist der maximale Wert für b 30.
Ups, oder? D.h. heißt sogar der anfänglich von mir suggerierte Stack-Overflow würde im Normalfall in dem Programm nicht passieren.

Zum Glück gibt es noch long, bzw. float oder double. ;). Hier kann man sich das gleiche auch mal für überlegen.

Btw. es ist ziemlich blöd einen negativen Wert für den Exponenten zu verwenden, da für den int/long was lustiges für a : int b<0: a^b=0. Man probiere es mal mit 2^(-1).

Hier kann man sich noch die Folgefrage stellen, um wie viel ist die Optimierung schneller?

Die hier verwendete Rekursion kann mit -O2 wunderbar aufgelöst werden. Wenn man solch eine Rekursion, d.h. welche man mit einer Schleife leicht reduzieren kann, in komplexerer (produktiv) Software einsetzt, kann unter umständen leider etwas lustiges passieren:
Der Kunde schickt ein Problem-Report ab, der Dev versucht ihn nachzuvollziehen, und läuft in dieser Funktion in einen Stack-Overflow und kommt gar nicht erst an seine eigentliche Stelle.
Da produktiv SW meist mit -O2 compiliert wird, und der Dev meist im Debug Mode umherschwirrt (-O0), wird in einem die Rekursion aufgelöst und im anderen Fall nicht. D.h. der Dev muss erstmal anderen Code fixen um weiter zu kommen, welcher nicht-zwingend Teil seines Programms ist.
Folglich kann unter Umständen auch mal der Team-Spirit kurz schief hängen.

Ich wünsche, viel Spaß beim weiter lernen und ein schönes Wochenende.
 
Zuletzt bearbeitet:
titanskin schrieb:
Da produktiv SW meist mit -O2 compiliert wird, und der Dev meist im Debug Mode umherschwirrt (-O2)
Da ist doch etwas verkehrt?
 
  • Gefällt mir
Reaktionen: titanskin

Ähnliche Themen

Zurück
Oben