Warum stürzt dieses Programm ab?



  • #include <stdio.h>
    #include <math.h>
    #include <time.h>
    #include <limits.h>
    
    void func1(int *nulls_in_1, double *values, int *ticks_1, int ticks, int rounds){
      int n;
    
      for (int j = 0; j <= rounds; j++){
        for (int i = 0; i <= ticks; i++){   
          // Prüfe auf 0
          n = (int)(values[i]*1000.0);
          if (n == 0){
            (*nulls_in_1)++;
          }
          (*ticks_1)++;
        }
      }
    }
    
    void func2(int *nulls_in_2, double *values, int *ticks_2, int ticks, int rounds){
      for (int j = 0; j <= rounds; j++){
        for (int i = 0; i <= ticks; i++){   
          // Prüfe auf 0
          if (values[i] < 0.001 && values[i] > -0.001){
            (*nulls_in_2)++;
          }
          (*ticks_2)++;
        }
      }
    }
    
    int main(){
      printf("Time = %i\n", (int) clock());
      int ticks1 = 0;
      int ticks2 = 0;
      // int n_ticks = 260283; // Stürzt nicht ab.
        int n_ticks = 300000; // Stürzt ab
      int rounds = 200;
      double values[n_ticks];
      int nulls_in_1 = 0;
      int nulls_in_2 = 0;
      clock_t startzeit;
      clock_t zeit1;
      clock_t zeit2;
    
      srand(1);
      for (int i = 0; i <= n_ticks; i++){
          values[i] = rand() / 100000.0;
      }
      /*
      for (int i = 0; i <= 100; i++){
            printf("Testzahl %f\n", values[i]);
      }*/
      printf("startzeit = %i\n", (int) clock());
      startzeit = clock();
    
      // Zeitmessung Func 1  
      func1(&nulls_in_1, &values[0], &ticks1, n_ticks, rounds);
      printf("1 Time = %i\n", (int) clock());
      zeit1 = clock() - startzeit;
    
      // Zeitmessung Func 2
      func2(&nulls_in_2, &values[0], &ticks2, n_ticks, rounds); 
      printf("2 Time = %i\n", (int) clock());
      zeit2 = clock() - startzeit - zeit1;
    
      printf("Zeit 1 = %i\n", (int) zeit1);
      printf("Zeit 2 = %i\n", (int) zeit2);
      printf("ticks_1 = %16i\nticks_2 = %16i\n", ticks1, ticks2);
    
      if (zeit1 > zeit2){
        printf("Differenz (zeit1 - zeit2) =   %12i\n", (int) zeit1 - zeit2);
        printf("Funktion 1 ist langsamer!\n");
      } else{
        printf("Differenz (zeit2 - zeit1) =   %12i\n", (int) zeit2 - zeit1);
        printf("Funktion 2 ist langsamer!\n");
      }
    
      printf("Gefundene Nullen in 1 = %i\n", nulls_in_1);
      printf("Gefundene Nullen in 2 = %i\n", nulls_in_2); 
      printf("Gesamtzeit = %i\n", (int) clock()); 
    
      double y = -0.001;
      if (y < 0.001 && y > -0.001){
         printf("y ist 0, siehe = %f\n", y);
      }
      return 0;
    }
    

    Im obigen C99 Programmcode habe ich eine Variable n_ticks und wenn ich diese auf einen sehr hohen Wert setze, bei mir z.B. 300000, dann stürzt das Porgramm unter Win7 64 Bit aus mir unbekannten Gründen einfach ab.

    Leider werden keine verwertbaren Fehlermeldungen angezeigt.
    Ich würde nun gerne wissen, warum das Programm abstürzt?

    Compiliert wurde mit gcc 4.5.2



  • PS:

    Wer nicht suchen möchte, siehe Zeile 39 und 40, da ist die Variable definiert.



  • Durch das double values[n_ticks]; wird dir der Stack um die Ohren fliegen.

    Ändere das mal in

    double *values = malloc(n_ticks * sizeof(*values));
    

    Für &values[0] kannst du auch ganz einfach nur values schreiben.

    malloc ist in der stdlib.h deklariert.



  • Der Default-Stack deines gcc/MinGW ist zu klein für deine Anforderung, kommt davon, dass du VLA benutzt und u.a. benutzt man deswegen keine VLA.
    Ersetze mal

    double values[n_ticks];
    

    durch

    double *values=calloc(sizeof*values,n_ticks);
    

    und noch ein free dafür am Programmende.



  • Und beim Zugriff auf das Array gehst du immer zu weit.
    In den Schleifen darf kein <= sondern nur ein < stehen.

    Ein double wert[5]; legt dir ein Array mit 5 Elementen an.
    Die gehen von 0 bis 4. Das Element mit der Nummer 5 existiert nicht.

    for (int i = 0; i <  n_ticks; i++){
    //                ^ da gehört nur ein < hin
         values[i] = rand() / 100000.0;
    

    Das musst du in allen Schleifen ändern.

    Dies kann auch ein Grund für den Absturz sein.



  • Danke an euch alle (und danke für den Tip mit dem <= in der Schleife).

    Mit folgendem Programmcode habe ich jetzt herausgefunden, das ein VLA bei meinem System mit gegebenen gcc Compiler maximal 2033 KByte groß sein darf, wird er überschritten, dann kommt das gleiche Problem wie oben.

    #include <stdio.h>
    #include <limits.h>
    
    // Wenn Programm abstuerzen soll, dann ABSTURZ auf 1 setzen
    #define ABSTURZ 0
    
    #if (ABSTURZ)
    #  define ELEMENTE 520612
    #else
    #  define ELEMENTE 520611
    #endif
    
    int main(){
      int size = 0;
    
      int i[ELEMENTE];
    
      size = sizeof(size);
      printf("Ein int Wert belegt auf dieser Maschine %i Bytes.\n\n", size);
      printf("Das Array i enthaelt %i Elemente.\n\n", sizeof(i)/size);
      printf("Es wurden %i KByte belegt.\n", sizeof(i)/1024);
      return 0;
    }
    

    Ausgabe:

    Ein int Wert belegt auf dieser Maschine 4 Bytes.
    
    Das Array i enthaelt 520611 Elemente.
    
    Es wurden 2033 KByte belegt.
    

    Mit malloc dürfte das nicht mehr auftreten.

    Aber bezüglich malloc habe ich noch eine Frage.
    Arrays sind ja zusammenhängende Speicherbereiche an einem Stück, deswegen kann man die in C mit einem Zeiger durchlaufen, aber was passiert denn,
    wenn der zu reservierende zusammenhängende Speicherbereich größer ist,
    als der größte freie zusammenhängende Speicherbereich, den das OS liefern kann?



  • Dann liefert malloc/calloc/realloc NULL zurück.



  • Wutz schrieb:

    Dann liefert malloc/calloc/realloc NULL zurück.

    Okay, aber wenn man nun dringend ein größeres Array braucht, wie wird das dann gelöst?

    Mit einem NULL als Returnwert ist einem ja nicht geholfen, wenn man den Speicher tatsächlich braucht. Mit dieser Information kann man lediglich das Programm geordnet beenden.



  • Wenn malloc den gewünschten Speicher nicht beschaffen kann, gibt es NULL zurück.
    Aber das steht auch in jeder Referenz zu malloc.

    Darum immer den Rückgabewert überprüfen und ggf. auf den Fehler reagieren.



  • Oder anders gefragt, müßte man das OS nicht irgendwie anschubsten können, den Speicher zu defragmentieren, so daß der nächste Versuch, ein entsprechend großes Array anzufordern gelingt?

    Wie bringt man also das OS dazu, den Arbeitsspeicher zu defragmentieren?

    Bei der Frage gehe ich jetzt mal davon aus, das physikalisch genug RAM im Rechner vorhanden ist, nur laufen noch andere Programme, so daß man nichts an einem Stück bekommt, das groß genug ist.



  • Undefiniertes Verhalten schrieb:

    Okay, aber wenn man nun dringend ein größeres Array braucht, wie wird das dann gelöst?

    Dann geht es nicht. Irgendwann ist Schluß.

    Oder mehr Speicher reinbauen, anderen Rechner nehmen.
    Du kannst dir auch eine andere Speicherstrategie ausdenken.
    Muss es ein Array sein? (ein großer Speicherbereich)
    Oder geht auch eine Liste oder ein Array mit Zeigern auf Arrays? (viele kleine Bereiche)



  • DirkB schrieb:

    Undefiniertes Verhalten schrieb:

    Okay, aber wenn man nun dringend ein größeres Array braucht, wie wird das dann gelöst?

    Dann geht es nicht. Irgendwann ist Schluß.

    Oder mehr Speicher reinbauen, anderen Rechner nehmen.

    Wenn der Speicher also fragmentiert ist, dann hilft nur noch mehr physikalisches RAM oder eine andere Datenstruktur, habe ich das so richtig verstanden?

    Falls ja, wie soll ich dann wissen, auf welchen kleinsten gemeinsammen Nenner ich mich immer verlassen kann?

    Auf dem Computer könnten ja > 10 Mio Prozesse laufen, so daß der Speicher völlig fragmentiert ist.
    Dann würde ich nichtmal kleine zusammenhängende Speicherbereiche finden, solange es keinen Weg gibt, das OS dazu zu zwingen, den Speicher zu defragmentieren.

    Muss es ein Array sein? (ein großer Speicherbereich)
    Oder geht auch eine Liste oder ein Array mit Zeigern auf Arrays? (viele kleine Bereiche)

    Momentan ist es nur eine rein akademische Frage, für den Fall das ich mal einen großen Zusammenhängenden Speicherbereich als Array benötige.

    Ein Array in mehrere Arrays zerstückeln würde natürlich, wenn es nicht anders geht und es mal notwendig sein sollte, natürlich schon gehen, da ist dann halt etwas Aufwand.

    Aber ich habe nochmal eine Frage zu malloc, ich habe jetzt versucht im zweiten Quelltext malloc zu verwenden, aber ich erhalte Fehlermeldungen beim compilieren:

    #include <stdio.h>
    #include <limits.h>
    #include <stdlib.h>
    
    // Wenn Programm abstuerzen soll, dann ABSTURZ auf 1 setzen
    #define ABSTURZ 1
    
    #define DYNAMISCH 1
    
    #if (ABSTURZ)
    #  if (DYNAMISCH)
    #    define ELEMENTE 520612
    #  else
    #    define ELEMENTE 520612
    #  endif
    #else
    #  define ELEMENTE 520611
    #endif
    
    int main(){
      int size = 0;
    
      if (DYNAMISCH){
        int *i = malloc(ELEMENTE * sizeof(*i));
      } else {
        int i[ELEMENTE];
      }
      i[2] = 42;
      size = sizeof(size);
      printf("Ein int Wert belegt auf dieser Maschine %i Bytes.\n\n", size);
      printf("Das Array i enthaelt %i Elemente.\n\n", sizeof(i)/size);
      printf("Es wurden %i KByte belegt.\n", sizeof(i)/1024);
    
      if (DYNAMISCH){
          free(i);
      }
      return 0;
    }
    

    gcc -std=c99 -Wall -o "riesenarray" "riesenarray.c" (im Verzeichnis: D:\prog)
    riesenarray.c: In function 'main':
    riesenarray.c:27:9: warning: unused variable 'i'
    riesenarray.c:29:3: error: 'i' undeclared (first use in this function)
    riesenarray.c:29:3: note: each undeclared identifier is reported only once for each function it appears in
    Kompilierung fehlgeschlagen.

    27:9 ist klar, aber 29:3 nicht.
    Ich habe doch jetzt einen Zeiger Namens *i, der auf einen Speicherbereich zeigt, der mit malloc reserviert wurde.



  • Habe gerade die Antwort gefunden, das bei malloc ein sizeof nichts mehr bringt:

    http://www.c-faq.com/malloc/querysize.html



  • Die Variable i (egal ob Zeiger oder Array) ist nur innerhalb des Scopes (den Umgebnden geschweiften Klammern) definiert.
    Nach dem if gibt es die Variablen nicht mehr.
    Mach das mit dem Preprozessor (#if #else #endif)

    Auf modernen Betriebssystemen bekommt jeder Prozess dank der MMU einen komplett eigenen zusammenhängenden Speicherbereich. Wenn der reale Speicher belegt ist, wird geswapt.



  • DirkB schrieb:

    Die Variable i (egal ob Zeiger oder Array) ist nur innerhalb des Scopes (den Umgebnden geschweiften Klammern) definiert.
    Nach dem if gibt es die Variablen nicht mehr.
    Mach das mit dem Preprozessor (#if #else #endif)

    Danke, ich habe es jetzt so umgebaut.
    (Mache zum Testen halt jetzt zwei Arrays, aber da sizeof nicht bei malloc geht, wäre es auch nicht anders sinnvoll gewesen)

    #include <stdio.h>
    #include <limits.h>
    #include <stdlib.h>
    
    // Wenn Programm abstuerzen soll, dann ABSTURZ auf 1 setzen
    #define ABSTURZ 1
    
    #if (ABSTURZ)
    #  define ELEMENTE 520612
    #else
    #  define ELEMENTE 520611
    #endif
    
    int main(){
      int size = 0;
      int *i_dyn = malloc(ELEMENTE * sizeof(*i_dyn));
    
      if (!(i_dyn == NULL)){
        printf("Speicher konnte reserviert werden.\n");
      }
      else {
        printf("Speicherreservierung war nicht erfolgreich.\n");
      }
    
      #ifdef ABSTURZ
        int i[ELEMENTE - 1];
      #else
        int i[ELEMENTE];
      #endif
      i[2] = 42;
      size = sizeof(size);
      printf("Ein int Wert belegt auf dieser Maschine %i Bytes.\n\n", size);
      printf("Das Array i enthaelt %i Elemente.\n\n", sizeof((void*)i)/size);
      printf("Es wurden %i KByte belegt.\n", sizeof(i)/1024);
    
      if (i_dyn){
        free(i_dyn);
      }
      return 0;
    }
    

    Dummerweise stürzt er jetzt immer noch ab, obwohl i jetzt nicht die maximale Elementanzahl, die zum Absturz führt, erreichen sollte.

    Auf modernen Betriebssystemen bekommt jeder Prozess dank der MMU einen komplett eigenen zusammenhängenden Speicherbereich. Wenn der reale Speicher belegt ist, wird geswapt.

    Danke, dann muss ich mir um die physikalische Fragementierung also gar keine Sorgen machen, richtig?

    Wenn also genug Speicher frei ist, dann kriege ich auch genug Speicher am Stück. Also für den Prozess am Stück.



  • Ich denke ich weiß jetzt warum es nun dennoch abstürzt.

    Diese 2033 KByte beziehen sich auf alle Datenwerte.

    Da ich nun einen 4 Byte großen Pointer (32 Bit Programm) Namens i_dyn erstelle,
    muss ich in Zeile 28 die Anzahl der ELEMENTE nicht um 1, sondern um 2 abziehen.

    Also so:

    int i[ELEMENTE - 2]

    dann geht es wieder.

    Mein normales VLA i ist dann allerdings auch um 4 Byte bzw. 1 Element kleiner, also genau der Platz, den der zusätzliche Pointer belegt.

    So sieht mein Code aktuell aus:

    #include <stdio.h>
    #include <limits.h>
    #include <stdlib.h>
    
    // Wenn Programm abstuerzen soll, dann ABSTURZ auf 1 setzen
    #define ABSTURZ 1
    
    #if (ABSTURZ)
    #  define ELEMENTE 520612
    #else
    #  define ELEMENTE 520611
    #endif
    
    int main(){
      int size = 0;
      int *i_dyn = malloc(ELEMENTE * sizeof(*i_dyn));
    
      if (!(i_dyn == NULL)){
        printf("Speicher konnte reserviert werden.\n");
      }
      else {
        printf("Speicherreservierung war nicht erfolgreich.\n");
      }
    
      #if (ABSTURZ)
        int i[ELEMENTE - 2];
      #else
        int i[ELEMENTE];
      #endif
    
      i[2] = 42;
      size = sizeof(size);
      printf("Ein int Wert belegt auf dieser Maschine %i Bytes.\n\n", size);
      printf("Das Array i enthaelt %i Elemente.\n\n", sizeof(i)/size);
      printf("Es wurden %i KByte belegt.\n", sizeof(i)/1024);
    
      if (i_dyn){
        free(i_dyn);
      }
      return 0;
    }
    


  • Zeile 30 muss ich dann allerdings auch auf

    int i[ELEMENTE - 1] ändern.

    Und je mehr zusätzliche Variablen ich definiere, desto mehr muss ich von ELEMENTE abziehen um mein VLA i zu erstellen.

    Oder anders gesagt, es sind maximal nur "ELEMENTE+1" einwertige Variablen (also ohne Arrays) möglich.
    In diesem Fall also 520613, mehr Variablen können nicht definiert werden.



  • Die Unterscheidung ab Zeile 27 ist Blödsinn, da du das ja schon ab Zeile 8 regelst.

    Du kannst auch durch Compiler/Linkeroptionen die Stackgröße ändern.



  • DirkB schrieb:

    Die Unterscheidung ab Zeile 27 ist Blödsinn, da du das ja schon ab Zeile 8 regelst.

    Nein ist sie nicht.

    Weil ich hier keinen Absturz mehr haben wollte, es sollte lediglich berücksichtigt werden, das das größere Array per malloc erzeugt werden kann, aber das int[] Array nicht zum Absturz führt.

    Deswegen steht da ja auch in Zeile 28 ELEMENTE - 2

    Du kannst auch durch Compiler/Linkeroptionen die Stackgröße ändern.

    Wie lautet der Kommandozeilenparameter dazu?



  • Undefiniertes Verhalten schrieb:

    DirkB schrieb:

    Du kannst auch durch Compiler/Linkeroptionen die Stackgröße ändern.

    Wie lautet der Kommandozeilenparameter dazu?

    MinGW-gcc: -Wl,--stack,10000000 reserviert 10 MB Stack zu Programmbeginn.

    viele grüße
    ralph


Anmelden zum Antworten