Anfänger + Hausaufgaben + Faulheit = Progamm =>Probleme



  • Hallo, ich hab vor 2-3 Tagen angefangen C zu lernen. Gestern kam ich auf die Idee mir meine Mathehausaufgaben zu erleichtern, indem ich ein Programm schreibe, welches für mich die lästigen Rechnungen übernimmt. Unzwar die Lästigen Rechnungen beim Austauschprozess bei stochastischen Matrizen. Da wir die Zahlen in der Schule, als Bruch angeben müssen, eine Bruch-Struktur erstellt und funktionen zum Umgang mit diesen Brüchen. Man muss die Zahlen entweder als ganze Zahl oder als Bruch( z.B "3/4" für Dreiviertel) eingeben.

    Hier wie man es Rechnet veranschaulicht:
    http://i364.photobucket.com/albums/oo86/_All4ONE_/Rechnung.png

    Mein Problem ist nun, dass ab einem (je nach zahlen anders), das ergebnis 0/1 ist, was ich mir garnicht erklären kann, oder irgendwelche Negativen Zahlen auftauchen, obwohl alle eingegebenen Zahlen positiv(es wird nur Addiert und Multipliziert). Da ist der "Fehler" vermutlich, dass long int zuwenig Kapazität für die Zahlen hat im Zähler und im Nenner hat.
    Nun meine Fragen, wie kann ich das ganze mit beliebig großen Zahlen realisieren und wieso kommt ab und an als ergebnis 0/1?

    Hier der C Code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct {
            long int zaehler;
            long int nenner;
             }bruch;
    
    //Bruch Kürzen
    void b_kuerzen(bruch *bruch){
          int ggt;
          ggt = ggT((*bruch).zaehler, (*bruch).nenner);
          (*bruch).zaehler /= ggt;
          (*bruch).nenner /= ggt;
          }
    
    //Bruch-Addition
    bruch b_add(bruch bruch1, bruch bruch2){
         if((bruch1.zaehler != 0) & (bruch2.zaehler != 0) 
            &(bruch1.nenner != 0) & (bruch2.nenner != 0)){
              bruch ergebnis;
              int kgv,kgvt1,kgvt2;
              kgv = kgV(bruch1.nenner,bruch2.nenner);
              kgvt1 = kgv/bruch1.nenner;
              kgvt2 = kgv/bruch2.nenner;
              ergebnis.zaehler = bruch1.zaehler * kgvt1 +bruch2.zaehler * kgvt2;
              ergebnis.nenner = kgv;
              b_kuerzen(&ergebnis);
              return ergebnis ;}
         else{
              if ((bruch1.zaehler == 0) || (bruch1.nenner == 0)){ b_kuerzen(&bruch2);  return bruch2;}
              if ((bruch2.zaehler == 0) || (bruch2.nenner == 0)){ b_kuerzen(&bruch1);  return bruch1;}
             }
         }
    
    //Bruch-Subtraktion
    bruch b_sub(bruch bruch1, bruch bruch2){
          bruch2.zaehler *= -1;
          return b_add(bruch1,bruch2);
          }
    
    //Bruch-Multiplikation
    bruch b_mult(bruch bruch1, bruch bruch2){
         bruch ergebnis;
         b_kuerzen(&bruch1);
         b_kuerzen(&bruch2);
         ergebnis.zaehler = bruch1.zaehler * bruch2.zaehler;
         ergebnis.nenner = bruch1.nenner * bruch2.nenner;
         return ergebnis;
         }
    
    //Bruch-Division
    bruch b_div(bruch bruch1, bruch bruch2){
          long int temp = bruch2.nenner;
          bruch2.nenner = bruch2.zaehler;
          bruch2.zaehler = temp;
          return b_mult(bruch1,bruch2);
          }
    
    //Größten gemeinsamen Teiler 2er Zahlen bestimmen    
    int ggT(int zahl1,int zahl2){
       int rest;
       do{
           rest = zahl1 % zahl2;
           zahl1 = zahl2;
           zahl2= rest;
           }while(rest != 0);
        return zahl1;
       }
    
    //Kleinstes gemeinsames Vielfaches 2er Zahlen bestimmen
    int kgV(int zahl1,int zahl2){
        return betrag((zahl1*zahl2)/ggT(zahl1,zahl2));
        }
    
    //Betrag einer Zahl
    int betrag(int zahl){
        if(zahl < 0){ return zahl = -zahl;}else{ return zahl;};
    }
    
    //Bruch mithilfe von scanf eingeben
    void b_eingabe(bruch *bruch){
         char eingabe[30]="0/1",delimiter[]= "/",*ptr;
         scanf("%s",eingabe);
         if(strrchr(eingabe, '/')!= NULL){
             ptr = strtok(eingabe, delimiter);
             (*bruch).zaehler = atoi(ptr);
             ptr = strtok(NULL, delimiter);
             (*bruch).nenner = atoi(ptr);
         }else{
             (*bruch).zaehler = atoi(eingabe);
             (*bruch).nenner = 1;
             }
         }
    
    int main(int argc, char *argv[])
    {
     int i,j,exp;
     bruch a[9],b[3],c[3];
    
     for(i=0;i<9;i++){                  
        printf("Geben Sie a%d als Bruch oder ganze Zahl ein:",i+1);
        b_eingabe(&a[i]);
        printf("a%d = %ld/%ld\n",i+1,a[i].zaehler,a[i].nenner);
        printf("\n");
        }
    
     for(i=0;i<3;i++){
        printf("Geben Sie b%d als Bruch oder ganze Zahl ein:",i+1);
        b_eingabe(&b[i]);
        printf("b%d = %ld/%ld\n",i+1,b[i].zaehler,b[i].nenner);
        printf("\n");
        }
    
     bruch ergebnis[2];  
     printf("Geben sie den exponenten n ein:");
     scanf("%d",&exp);
     printf("\n");
    
     for(i=0; i<exp; i++){
      ergebnis[0] = b_add(b_add(b_mult(a[0],b[0]),b_mult(a[1],b[1])),b_mult(a[2],b[2])); 
      ergebnis[1] = b_add(b_add(b_mult(a[3],b[0]),b_mult(a[4],b[1])),b_mult(a[5],b[2])); 
      ergebnis[2] = b_add(b_add(b_mult(a[6],b[0]),b_mult(a[7],b[1])),b_mult(a[8],b[2]));
      b[0] = ergebnis[0];
      b[1] = ergebnis[1];
      b[2] = ergebnis[2]; 
      printf("Zeitschritt %d\n",i+1);
      for(j=0;j<3;j++){b_kuerzen(&ergebnis[j]); printf("x%d=%ld/%ld\n",j+1,ergebnis[j].zaehler,ergebnis[j].nenner);}
      printf("\n");
     }
    
      system("PAUSE");	
      return 0;
    }
    

  • Mod

    In Zeile 86 ist die Eingabe falsch, das & gehört da nicht hin.

    Fast sämtliche ausgaben sind falsch, der Formatspezifizierer für long int ist %ld.

    b_add: bist du sicher, dass die Funktion immer mit einem return endet? Mein Compiler kann das nicht nachvollziehen und bei deiner Formatierung mag ich es nicht lesen.

    Das ist erst einmal was mein Compiler selbstständig sagt, wenn du das ausgebessert hast und immer noch Fehler da sind, sehen wir weiter.



  • Mein Compiler nimmt das so hin und compiled den Code, wenn ich bei 86 das & wegnehme stürzt das Programm bei der eingabe ab.

    Ich benutze Dev-C++ von Bloodshed.

    edit: Ich habs auch auf meinem Laptop versucht, der compiled das auch ohne zu meckern.
    Die %d's stehen da weil ich ganz am anfang nur int benutzt hatte, ich werds sofort ausbessern 🙂

    Edit2: Ich hab jetzt das & mal rausgenommen und es Funktioniert immernoch. Nun stellt sich mir die Frage: Wann muss das & gesetzt werden und wann nicht? Bis jetzt ist das Programm jedes mal abgestürzt wenn ich das & bei scanf nicht eingesetzt hatte 😕


  • Mod

    Compilieren != Funktionieren. Compiler können (und müssen) sehr vieles verarbeiten, da bei C davon ausgegangen wird, dass der Programmierer Ahnung hat und irgendwelche tollen Tricks versucht. Wenn man das versehentlich macht, dann kommt aber höchstwahrscheinlich Mist raus. Dein Compiler (auch wenn Dev C++, bis auf die allerneueste Beta-Version einen extrem veralteteten Compiler dabei hat) kann sicherlich auch Warnungen erzeugen, wenn er Konstrukte findet, die ihm komisch vorkommen, die er aber schlucken muss. Guck mal in die Doku. Als Anfänger solltest du solche Warnungen wie Fehler behandeln.

    Funktioniert es denn jetzt mit den Änderungen?

    Zur Frage: Wann braucht man bei scanf ein & und wann nicht?
    Darf ich dir erst einmal empfehlen dir die Grundlagen anzueignen, wie man in C Werte aus Funktionen zurückgibt und wie die Arrays genau funktionieren, insbesondere deren Besonderheiten als Funktionsparameter? Dann wird dir wahrscheinlich schon ein Licht aufgehen. Die technische Antwort wirst du ohne diese Grundlagen vermutlich nicht verstehen. Bis dahin Faustregel: char, short, int, long, float, double und Konsorten mit Adressoperator, char-Arrays ohne.



  • @A1140NE - vor 2 Tagen erst mit C angefangen, und dann so ein Programm hinlegen (auch wenn´s nicht funktioniert)? Wie kann man nach 2 Tagen solchen Code "ptr = strtok(eingabe, delimiter);" verwenden? Eine neue Koryphäe am C-Himmel! 😃



  • Gast2 schrieb:

    @A1140NE - vor 2 Tagen erst mit C angefangen, und dann so ein Programm hinlegen (auch wenn´s nicht funktioniert)? Wie kann man nach 2 Tagen solchen Code "ptr = strtok(eingabe, delimiter);" verwenden? Eine neue Koryphäe am C-Himmel! 😃

    Jo. Von einem totalen Anfänger ist der Code nicht. Nach 2 Tagen verwendet man keine Zeiger und Funktionen ....



  • "Faszinierend" hätte ER gesagt 😉



  • Nun ja, ein paar Anfängerfehler sehe ich schon ^^
    Wahrscheinlich hat er eine C - ähnliche Sprache bereits gelernt (mir ist es
    zum Beispiel sehr leicht gefallen von C auf die Scriptsprache Javascript, oder
    auf PHP umzusteigen bzw. neu zu erlernen...

    bruch b_add(bruch bruch1, bruch bruch2) {
    
         if((bruch1.zaehler != 0) & (bruch2.zaehler != 0) & (bruch1.nenner != 0) & bruch2.nenner != 0) ) {
              bruch ergebnis;
              int kgv,kgvt1,kgvt2;
    
              kgv = kgV(bruch1.nenner,bruch2.nenner);
              kgvt1 = kgv/bruch1.nenner;
              kgvt2 = kgv/bruch2.nenner;
              ergebnis.zaehler = bruch1.zaehler * kgvt1 +bruch2.zaehler * kgvt2;
              ergebnis.nenner = kgv;
              b_kuerzen(&ergebnis);
              return ergebnis;
         }
         else {
              if ((bruch1.zaehler == 0) || (bruch1.nenner == 0)) { 
                  b_kuerzen(&bruch2);  
                  return bruch2;
              }
              if ((bruch2.zaehler == 0) || (bruch2.nenner == 0)) { 
                  b_kuerzen(&bruch1);  
                  return bruch1;
              }
         }
    }
    

    && nicht &, ein einzelnes & ist eine Bitverknüpfung bei denen alle einzelnen Bits mit "und" verknüpft werden (sprich alles ist Falsch ausser True&True)

    Hmm, ich glaub schon dass diese Funktion immer mit einem Return endet. Ich sehe´
    jedenfalls nicht auf den ersten Blick eine Ausnahme, was aber nicht heißt dass es
    keine gibt 😉

    Scheppertreiber schrieb:

    "Faszinierend" hätte ER gesagt 😉

    Faszinierend 😉

    Weiteres

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    
    typedef struct { 
            long int zaehler; 
            long int nenner; 
    } bruch; 
    
    //Bruch Kürzen 
    void b_kuerzen(bruch *bruch) {
    
          int ggt; 
          ggt = ggT(bruch->zaehler, bruch->nenner); // verwende '->', ist übersichtlicher und für irgendwas is es ja da ^^ 
          bruch->zaehler /= ggt; 
          bruch->nenner /= ggt; 
    
    } 
    
    //Bruch-Addition 
    bruch b_add(bruch bruch1, bruch bruch2) { 
    
         if((bruch1.zaehler != 0) && (bruch2.zaehler != 0) && (bruch1.nenner != 0) && (bruch2.nenner != 0)) { 
    
              bruch ergebnis; 
              int kgv,kgvt1,kgvt2; 
    
              kgv = kgV(bruch1.nenner, bruch2.nenner);  // ich trenne gerne Zuweisungen durch Funktionen und direkte Zuweisungen, da es übersichtlicher ist
    
              kgvt1 = kgv/bruch1.nenner; 
              kgvt2 = kgv/bruch2.nenner; 
              ergebnis.zaehler = bruch1.zaehler * kgvt1 + bruch2.zaehler * kgvt2; 
              ergebnis.nenner = kgv; 
    
              b_kuerzen(&ergebnis); 
    
              return ergebnis;
         } 
         else { 
              if ((bruch1.zaehler == 0) || (bruch1.nenner == 0)) { 
                 b_kuerzen(&bruch2);  
                 return bruch2;
              } 
              if ((bruch2.zaehler == 0) || (bruch2.nenner == 0)) { 
                 b_kuerzen(&bruch1);  
                 return bruch1;
              } 
        } 
    } 
    
    //Bruch-Subtraktion 
    bruch b_sub(bruch bruch1, bruch bruch2) { 
    
          bruch2.zaehler *= -1; 
          return b_add(bruch1, bruch2); 
    
    } 
    
    //Bruch-Multiplikation 
    bruch b_mult(bruch bruch1, bruch bruch2) { 
    
         bruch ergebnis; 
    
         b_kuerzen(&bruch1); 
         b_kuerzen(&bruch2); 
    
         ergebnis.zaehler = bruch1.zaehler * bruch2.zaehler; 
         ergebnis.nenner = bruch1.nenner * bruch2.nenner; 
    
         return ergebnis; 
    
    } 
    
    //Bruch-Division 
    bruch b_div(bruch bruch1, bruch bruch2) {
    
          long int temp = bruch2.nenner;
    
          bruch2.nenner = bruch2.zaehler; 
          bruch2.zaehler = temp; 
    
          return b_mult(bruch1, bruch2); 
    
    } 
    
    //Größten gemeinsamen Teiler 2er Zahlen bestimmen     
    int ggT(int zahl1, int zahl2) { 
    
       int rest; 
    
       do {
    
           rest = zahl1 % zahl2; 
           zahl1 = zahl2; 
           zahl2= rest;
    
       } while(rest != 0); 
    
       return zahl1; 
    
    } 
    
    //Kleinstes gemeinsames Vielfaches 2er Zahlen bestimmen 
    int kgV(int zahl1, int zahl2) {
    
        return betrag( (zahl1*zahl2) / ggT(zahl1, zahl2)); 
    
    } 
    
    //Betrag einer Zahl 
    int betrag(int zahl) { 
    
        if(zahl < 0) { 
          return zahl = -zahl;
        }
    
        //else{ return zahl;};  // else nicht nötig, weiteres lies dir mal die Warnings durch ^^ --> '};'
        return zahl;
    } 
    
    //Bruch mithilfe von scanf eingeben 
    void b_eingabe(bruch *bruch) { 
    
         char eingabe[30] = "0/1", delimiter[] = "/", *ptr;  // ich möcht ja nicht kleinlich sein, aber ich hab noch in Erinnerung das so eine Zuweisung nicht gültig ist (zu faul um auszuprobieren ^^) --> delimiter[] = {"/"}; (kann mich aber auch täuschen ^^)
    
         scanf("%s",eingabe);
    
         if(strrchr(eingabe, '/') != NULL) { 
    
             ptr = strtok(eingabe, delimiter); 
             bruch->zaehler = atoi(ptr); 
             ptr = strtok(NULL, delimiter); 
             bruch->nenner = atoi(ptr);
    
         }
         else { 
             bruch->zaehler = atoi(eingabe); 
             bruch->nenner = 1; 
         } 
    } 
    
    int main(int argc, char *argv[]) 
    { 
    
     int i, j, exp; 
     bruch a[9], b[3], c[3]; 
     bruch ergebnis[2];
    
     for(i=0; i<9; i++) {
    
        printf("Geben Sie a%d als Bruch oder ganze Zahl ein:", i+1); 
        b_eingabe(&a[i]); 
        printf("a%d = %ld/%ld\n", i+1, a[i].zaehler, a[i].nenner); 
        printf("\n");
    
     } 
    
     for(i=0; i<3; i++) { 
    
        printf("Geben Sie b%d als Bruch oder ganze Zahl ein:", i+1); 
        b_eingabe(&b[i]); 
        printf("b%d = %ld/%ld\n", i+1, b[i].zaehler, b[i].nenner); 
        printf("\n");
    
     } 
    
     //bruch ergebnis[2];    // variablen Deklaration wenn möglich am Anfang der Funktion
     printf("Geben sie den exponenten n ein: "); 
     scanf("%d", &exp); 
     printf("\n"); 
    
     for(i=0; i<exp; i++) { 
    
      ergebnis[0] = b_add( b_add(b_mult( a[0], b[0]), b_mult( a[1], b[1])), b_mult(a[2], b[2])); 
      ergebnis[1] = b_add( b_add(b_mult( a[3], b[0]), b_mult( a[4], b[1])), b_mult(a[5], b[2])); 
      ergebnis[2] = b_add( b_add(b_mult( a[6], b[0]), b_mult( a[7], b[1])), b_mult(a[8], b[2])); 
    
      b[0] = ergebnis[0]; 
      b[1] = ergebnis[1]; 
      b[2] = ergebnis[2]; 
    
      printf("Zeitschritt %d\n",i+1); 
    
      for(j=0; j<3; j++) {
    
        b_kuerzen(&ergebnis[j]); 
        printf("x%d=%ld/%ld\n", j+1, ergebnis[j].zaehler, ergebnis[j].nenner);
    
      }
    
      printf("\n"); 
     } 
    
      system("PAUSE");    
      return 0; 
    }
    

    Habs mal halbwegs leserlich formatiert, schönheitsfehler ausgebessert, Semantikfehler
    hab ich vorerst nicht gesucht 🕶



  • Käse! Der hat das irgend wo her kopiert, und versteht nicht die Bohne vom Code. Der sucht nur ein paar eitle Dumme die ihm helfen seine Aufgabe zu kösen.

    Der soll seine Schulaufgaben gefälligst selbst machen! 😡



  • SeppJ schrieb:

    Zur Frage: Wann braucht man bei scanf ein & und wann nicht?
    Darf ich dir erst einmal empfehlen dir die Grundlagen anzueignen, wie man in C Werte aus Funktionen zurückgibt und wie die Arrays genau funktionieren, insbesondere deren Besonderheiten als Funktionsparameter? Dann wird dir wahrscheinlich schon ein Licht aufgehen. Die technische Antwort wirst du ohne diese Grundlagen vermutlich nicht verstehen. Bis dahin Faustregel: char, short, int, long, float, double und Konsorten mit Adressoperator, char-Arrays ohne.

    Ah jetzt fällts mir wieder ein, das hat doch bestimmt damit zutun dass char-Arrays im prinzip auch nur Zeiger sind, richtig?

    Gast2 schrieb:

    @A1140NE - vor 2 Tagen erst mit C angefangen, und dann so ein Programm hinlegen (auch wenn´s nicht funktioniert)? Wie kann man nach 2 Tagen solchen Code "ptr = strtok(eingabe, delimiter);" verwenden? Eine neue Koryphäe am C-Himmel! 😃

    Britney Spears schrieb:

    Jo. Von einem totalen Anfänger ist der Code nicht. Nach 2 Tagen verwendet man keine Zeiger und Funktionen ....

    Scheppertreiber schrieb:

    "Faszinierend" hätte ER gesagt 😉

    Danke! Das fass ich jetzt alles mal als lob auf 😃

    itedvo schrieb:

    Nun ja, ein paar Anfängerfehler sehe ich schon ^^
    Wahrscheinlich hat er eine C - ähnliche Sprache bereits gelernt (mir ist es
    zum Beispiel sehr leicht gefallen von C auf die Scriptsprache Javascript, oder
    auf PHP umzusteigen bzw. neu zu erlernen...

    [...]

    && nicht &, ein einzelnes & ist eine Bitverknüpfung bei denen alle einzelnen Bits mit "und" verknüpft werden (sprich alles ist Falsch ausser True&True)

    Hmm, ich glaub schon dass diese Funktion immer mit einem Return endet. Ich sehe´
    jedenfalls nicht auf den ersten Blick eine Ausnahme, was aber nicht heißt dass es
    keine gibt 😉

    Scheppertreiber schrieb:

    "Faszinierend" hätte ER gesagt 😉

    Faszinierend 😉

    Weiteres

    [...]

    Habs mal halbwegs leserlich formatiert, schönheitsfehler ausgebessert, Semantikfehler
    hab ich vorerst nicht gesucht 🕶

    Es stimmt, vor 4-5 Jahren hatte ich kurze Zeit(1-2 Monate?) mit der Skriptsprache LUA für die PSP programmiert, jedoch waren mir damals Funktionen und Zeiger noch ein Fremdwort.. 😉 Das mit den Funktionen und Zeigern ist für mich Neu. Aber auf C-Howto ist das ganze sehr gut erklärt und alles scheint mir ganz logisch und ich kanns gut nachvollziehen warum was gemacht wird 🙂
    Das mit den & hab ich jetzt ausgebessert. Danke für Formatieren! Ich werd diese Formatierung mir von nun an als Vorbild nehmen, da der Code vieeeeeel übersichtlicher ist 🙂

    Mutti schrieb:

    Käse! Der hat das irgend wo her kopiert, und versteht nicht die Bohne vom Code. Der sucht nur ein paar eitle Dumme die ihm helfen seine Aufgabe zu kösen.

    Der soll seine Schulaufgaben gefälligst selbst machen! 😡

    Diese Anschuldigung kann ich jetzt garnicht nachvollziehen, der Code ist komplett von mir und ich verstehe diesen auch folglich 😉
    Desweiteren ist das keine Schulaufgabe, da ich C aus eigenem interesse lerne. Das mit der Mathehausaufgabe, kann ich auch gut ohne dieses Programm lösen, jedoch war die Hausaufgabe nur eine "Inspiration" und meines erachtens eine gute Übung für mich. 🙂


  • Mod

    A1140NE schrieb:

    Ah jetzt fällts mir wieder ein, das hat doch bestimmt damit zutun dass char-Arrays im prinzip auch nur Zeiger sind, richtig?

    ⚠ ARRAYS SIND KEINE ZEIGER!

    Entschuldige das Geschrei, aber diesen Anfängerirrtum - meistens weitergereicht von schlechten Lehrern, die es selber nicht besser wissen - vergisst du lieber schnell wieder. Arrays zerfallen jedoch beim kleinsten Zeichen von Gefahr in einen Pointer auf ihr erstes Element, deswegen kann man leicht den Eindruck bekommen, sie wären äquivalent. Sind sie aber nicht. Und das wird an anderen Stellen noch wichtig.

    Siehe auch dieses Stichwort:
    Google: array to pointer decay
    Der erste Treffer gefällt mir besonders. Setzt allerdings voraus, dass du schon ein bisschen Bescheid weißt, damit du ihn verstehst.



  • Interessante Seite SeppJ, werd ich mir für den Nachhilfekurs den ich für die ersten und zweiten Klassen gebe , merken.

    Back2Problem: Ich hab dein Programm jetzt nicht getestet, geschweige überprüft ob du korrekt die Zahlen umwandelst oder nicht irgendwo eine Zahl abschneidest (Ich sehe nämlich keine Überprüfung, ob innerhalb eines Strings den du in eine Zahl umwandeln möchtest, noch weitere Zahlen enthalten sind, testen kannst du das ganze mit dem Debugger oder einfacher: mit ner Ausgabe der von dir Eingelesenen Rechnung.)



  • Hat man mal etwas mit Assembler zu tu gehabt fällt es einem ja wie Schuppen von den Augen 🕶


Log in to reply