C C - Realloc() Fehler

Force202Gamer

Newbie
Registriert
Jan. 2014
Beiträge
7
Hallo liebe Community!

Ich schreibe gerade an einem Programm, welches mithilfe von einem dynamisch erstellten Feld eine Bücherliste verwalten soll. In der Funktion, wo ich das Feld erstelle, wird der Speicher dann vergrößert, wenn ich 80% des max. Speichers erreicht habe. Leider funktioniert das Vergrößern nicht und der Debugger gibt Meldungen, wie "ntdll!TpWaitForAlpcComletion()" oder "ntdll!RtLargeIntegerDivide()" aus. In Zeile 24 bleibt der Debugger hängen und gibt mir die Meldungen über "Call Stack" aus. Zum Programmieren benutze ich CodeBlocks.
Vom Compiler habe ich grünes Licht bekommen!
Was habe ich falsch gemacht?

Code:
buch_t *erstelleBuecherFeld(char *dateipfad, int *n, int *maximal)
{
  FILE *datei = NULL;
  char zeile[2000];
  buch_t *dynFeld;
  int max_help;

  datei = fopen(dateipfad, "rt");
  if(!datei) {
    return NULL;
  }
  *n = 0;
  *maximal = 10;
  dynFeld = (buch_t *)malloc(*maximal * sizeof(buch_t));
  if(!dynFeld) {
    fclose(datei);
    return NULL;
  }
  while(fgets(zeile, sizeof(zeile), datei)) {
    *n += erstelleNeuesBuch(zeile, dynFeld + *n, *n);
    max_help = *maximal * 0.8;
    if(*n == max_help) {
      *maximal += 10;
      dynFeld = (buch_t *)realloc(dynFeld, *maximal * sizeof(buch_t));
      printf("\n%d Speicherplatz wurde auf %d (%d Bytes) vergroessert!", *n, *maximal,
             sizeof (buch_t) * *maximal);
      if(!dynFeld) {
        fclose(datei);
        return NULL;
      }
    }
  }
  fclose(datei);
  return dynFeld;
}

P.S.: Falls ich vergessen haben sollte etwas zu erwähnen, weist mich bitte darauf hin! Bin noch ganz neu und weiß noch nicht zu 100% was alles an Informationen notwendig ist.
 
[EDIT] My Apologies, hatte ich falsch in Erinnerung.
 
Zuletzt bearbeitet von einem Moderator:
Ja, es ist eine Hausaufgabe, das gebe ich ganz offen und ehrlich zu. :D

Aber ich kann dir nicht ganz folgen, welche Stellen in der Funktion meinst du?
 
Also was oben stand war etwas zu vorschnell, deswegen hab ich es rausgenommen.

Ein paar Punkte:

- Du übergibst Parameter an die Funktion, die du dann auf einen festen Wert setzt.
- Du verwendest durchgängig Zeiger, greifst aber bei vielen Sachen stets nur auf den Wert zu
- Zeile 22 würde ich irgendwie als Ungleichung realisieren
 
1. Das liegt daran, dass die Parameter in weiteren Funktionen gebraucht werden.
2. Eigentlich ist es ja Sinn der Sache auf die Werte zuzugreifen.
3. Eine Ungleichung ergibt in meinen Augen wenig Sinn, da der Zähler (n) ja angibt, ob nun bereits die 80% erreicht wurden oder nicht und dazu brauche ich eben einen Vergleichswert, der mir angibt, wie groß nun die 80% sind. Der Speicher soll ja dann vergrößert werden, wenn ich 80% der Gesamtgröße erreicht habe.
 
Du solltest lernen, einen Debugger zu benutzen. Gehe im Call Stack zurück, bis du etwas bekommst, mit dem du arbeiten kannst. Funktionen in ntdll sind Teil des Betriebssystems und nur der letzte Schritt zum Tod deines Programms. Der Fehler kommt vorher.

€: Ad Flup:

1. Lerne C.
2. Lerne C.
3. Stimme zu. Hier ad OP: Was ist, wenn der bereits angeforderte Speicher 81% des Maximalwerts hat?
 
Zuletzt bearbeitet:
Bez. des Speichers: Theoretisch nicht möglich, da der max. Speicher immer in 10er Schritten erweitert wird. 80% davon ist immer eine gerade Zahl und da Anzahl nur einen Integerwert haben kann, ist eine Überschreitung nicht möglich.

Bis ich die ntdll - Meldung bekomme, hab ich im "Call Stack" immer nur die main-Methode und die Funktion selbst stehen. Dazu noch die übergebenen Parameter, sonst nichts.
 
In dem Fall ist dein Hinweis auf die ntdll bedeutungslos und du hättest ihn weglassen sollen, damit andere Leute nicht unnötig abgelenkt werden. Einen Debugger benutzen zu lernen ist weiterhin wärmstens zu empfehlen.
Breakpoints und Singlestepping sind ja wohl so das Minimum, das man können sollte und werden dir in Verbindung mit Beobachtung der Variablen ausreichend weiterhelfen.
 
Das mit der Verwirrung tut mir leid, dachte es wäre der Fehler.
Ich habe es auch mit Breakpoints und Singlestepping versucht und es lief auch alles so, wie gedacht, bis zum erwähnten Punkt. Und da mir diese Meldung seltsam vorkam und sie mir bisher auch noch nie unterkommen war, dachte ich, es sei die Fehlerquelle.
Auch die Variablen verändern sich so wie sie es tun sollten. Ich habe wirklich keine Ahnung, wo der Fehler liegt.
 
Vielleicht hilft das hier weiter: http://c-faq.com/expr/ieqiplusplus.html und auch das hier: http://c-faq.com/expr/comma.html

€: Ich mache die Antwort vielleicht doch etwas wortreicher.

realloc() versagt höchstwahrscheinlich, weil auf dynFeld in unerlaubter Weise zugegriffen wurde. Also über das Ende des Puffers hinaus
oder (unwahrscheinlicher) vor den Anfang des Puffers geschrieben wurde und du damit interne Datenstrukturen des Allocators kaputt gemacht hast.
der Ausdruck *n += funktion(zeiger + *n) hat bei mir den Verdacht erweckt, dass der Compiler deinen undefinierten Code kaputt optimiert hat,
und *n zuerst erhöht haben könnte, um den Wert Danach an die Funktion zu übergeben. Ich habe das nicht ausprobiert, denn selbst wenn,
könnte das Ergebnis bei mir völlig anders ausfallen als bei dir. Der Fehler muss also nicht da liegen. Den undefinierten Code zu korrigieren würde
ich aber trotzdem einfach mal so ans Herz legen. Schaden kann es ja nicht.
 
Zuletzt bearbeitet:
auch wenn schon angemerkt und hier nicht das problem:
Code:
 max_help = *maximal * 0.8;
if(*n == max_help) {
das ist wirklich ganz böser stil, so etwas solltest du tunlichst vermeiden. was ist z.B., wenn du mal die schrittgröße ändern willst?

edit: schau dir mal dieses stück code an:
Code:
   float factor = 0.8;
   int n = 83886080; 
   int m = n * factor; // at least on some configs, now m == 67108865 and NOT m == 67108864
   printf("%d\n", m);
hier wird eine durch 10 teilbare zahl mal 0.8 gerechnet, und es kommt eine ungerade zahl raus. es wird zwar bewusst float statt double benutzt, das prinzipielle problem der ungenauigkeit hat double aber auch, wenn auch erst bei größeren ganzen zahlen.
 
Zuletzt bearbeitet:
asdfman schrieb:
der Ausdruck *n += funktion(zeiger + *n) hat bei mir den Verdacht erweckt, dass der Compiler deinen undefinierten Code kaputt optimiert hat,
und *n zuerst erhöht haben könnte, um den Wert Danach an die Funktion zu übergeben. Ich habe das nicht ausprobiert, denn selbst wenn,
könnte das Ergebnis bei mir völlig anders ausfallen als bei dir. Der Fehler muss also nicht da liegen. Den undefinierten Code zu korrigieren würde
ich aber trotzdem einfach mal so ans Herz legen. Schaden kann es ja nicht.

Ich entschuldige mich - für den Fall, daß ich hier Mist erzähle - schon mal im Voraus, aber meiner Meinung nach gibt es an dem Ausdruck nichts Undefiniertes. Er ist sicherlich nicht gerade optimal lesbar, und man sollte solche komplexen Ausdrücke möglichst vermeiden, aber definiert sollte die Sache meiner Ansicht ganz klar sein. Und zwar sollte hier immer erst der Wert von *n genommen und dann mit zeiger addiert werden, das Resultat dann an funktion übergeben und das Ergebnis der Funktion dann auf *n draufgeschlagen werden. Wenn du da anderer Meinung bist, würde ich gerne eine Quelle sehen, die das belegt.
 
http://en.m.wikipedia.org/wiki/Sequence_point (bei examples)

Ja, sry Wikipedia, aber die Erklärung sollte hinkommen. Es ist nicht garantiert, in welcher Reihenfolge die Operanden evaluiert werden und der Compiler könnte zum Beispiel feststellen, dass der rechte Operand garantiert zu einer Konstante evaluiert wird und die Zuweisung dann vorziehen.

Auch ich sichere mich ab und gebe zu, potentiellen Mist zu reden. Jeder mit tieferem Wissen möge mich bitte bitte erleuchten.

Was meinen Hinweis auf den endgültigen Grund für das Versagen von realloc() angeht, bin ich mir weiterhin recht sicher, dass ein falscher Umgang mit dem Zeiger vor dem Aufruf Ursache ist. Dieser Fehler könnte natürlich auch in der aufgerufenen Funktion gemacht werden, aber das weiß keiner außer OP.
 
Zuletzt bearbeitet:
Erst einmal vielen Dank an alle für die umfangreiche Hilfe, hatte nicht erwartet so viele Antworten in der kurzen Zeit zu bekommen.

@maxwell-cs: Diesem Problem bin ich mir bewusst. Wenn ich mich richtig erinnere, trifft dieses Problem aber erst bei sehr großen Zahlen, wie bei deinem Bsp. zu. In der Praxis wäre es wohl besser es anders zu programmieren, aber dafür muss dieses Programm noch nicht ausgelegt sein. Außerdem hat unserer Lehrer uns empfohlen es auf diese Weise zu programmieren.

@Hancock: Die Funktion arbeitet korrekt und liefert 0 (Buch konnte NICHT erstellt werden) oder 1 (Buch konnte erstellt werden) zurück.

@asdfman: Hier kann ich antred, auf mein Bsp. bezogen, recht geben. Der Wert von *n arbeitet völlig korrekt und die Erhöhung funktioniert auch ordnungsgemäß. Auch bei dieser Methode hat es uns der Lehrer so vorgezeigt (wir programmierten ein ähnliches Bsp. in der Schule zur Vorbereitung auf die HÜ).
Angenommen ich hätte tatsächlich falsch auf dynFeld zugegriffen, wo im Programm könnte das sein? Eine Überschreitung des bereits allocierten Speichers ist ja nicht möglich, da ich die if-Anweisung laut Debugger nicht überspringe. Falls es helfen sollte, lege ich einmal die Funktion erstelleNeuesBuch hier rein.

Code:
int erstelleNeuesBuch(char *textzeile, buch_t *buecher, int anzahlAkt)
{
  int ok, bestand_help;
  char c, *token, buchNummer_help[11], titel_help[100];
  double preis_help;

  token = strtok(textzeile, ";");
  strcpy(buchNummer_help, token);

  token = strtok(NULL, ";");
  strcpy(titel_help, token);

  token = strtok(NULL, ";");
  if(token == NULL)
    return 0;
  ok = sscanf(token, "%d %c", &bestand_help, &c); /*Abstand: alle Whitespacezeichen werden ignoriert*/
  if(ok != 1) {
    return 0;
  }

  token = strtok(NULL, ";");
  if(token == NULL)
    return 0;
  ok = sscanf(token, "%lf %c", &preis_help, &c); /*Abstand: alle Whitespacezeichen werden ignoriert*/
  if(ok != 1) {
    return 0;
  }
  token = strtok(NULL, ";");
  if(token == NULL) {
    if(gueltigeBuchNummer(buchNummer_help) && gueltigeZahl(bestand_help, preis_help)) {
      if(sucheBuch(buecher, anzahlAkt, buchNummer_help, 0)== NULL) {
        strcpy(buecher[anzahlAkt].buchNummer, buchNummer_help);
        buecher[anzahlAkt].titel = (char *)malloc(strlen(titel_help)+1);
        if(!buecher[anzahlAkt].titel) {
          return 0;
        }
        strcpy(buecher[anzahlAkt].titel, titel_help);
        buecher[anzahlAkt].bestand = bestand_help;
        buecher[anzahlAkt].preis = preis_help;
        printf("\n%d %s wurde der Liste hinzugefuegt!", anzahlAkt, buecher[anzahlAkt].titel);
      } else {
          if(!overwrite(buecher, buchNummer_help, anzahlAkt, titel_help,
                        bestand_help, preis_help)) {
            printf("\n%d Bestand (und Preis) von \"%s\" wurden geaendert!",
                   anzahlAkt, buecher[anzahlAkt].titel);
            return 0;
          }
      }
    } else {
      return 0;
    }
    return 1;
  }
  return 0;
}
 
asdfman schrieb:
http://en.m.wikipedia.org/wiki/Sequence_point (bei examples)

Ja, sry Wikipedia, aber die Erklärung sollte hinkommen. Es ist nicht garantiert, in welcher Reihenfolge die Operanden evaluiert werden und der Compiler könnte zum Beispiel feststellen, dass der rechte Operand garantiert zu einer Konstante evaluiert wird und die Zuweisung dann vorziehen.

Die Thematik mit den sequence points ist eine andere, die mit dem genannten Ausdruck meines Erachtens erst mal nicht so viel zu tun hat. Laut dem Standard gilt ja diesbezüglich:

"At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place."

Deshalb wären sequence points hier auch nur von Interesse, wenn der Teilausdruck funktion(zeiger + *n) des Ausdrucks

Code:
*n += funktion(zeiger + *n);

Nebenwirkungen bezüglich *n hätte. Da das hier aber nicht der Fall ist, sollte das Ergebnis auch absolut definiert sein.

Force202Gamer schrieb:
@asdfman: Hier kann ich antred, auf mein Bsp. bezogen, recht geben. Der Wert von *n arbeitet völlig korrekt und die Erhöhung funktioniert auch ordnungsgemäß.

Verlasse dich in solchen Fragen bitte nicht darauf, daß bloß weil es mit dem Compiler, den du im Moment verwendest, funktioniert, es auch wirklich standardkonformes C ist. Manche Compiler weichen in einigen Punkten von den Vorgaben des C-Standards ab, und mit einem anderen Compiler könnte es dir durchaus passieren, daß das selbe Programm sich anders verhält.
Ergänzung ()

Force202Gamer schrieb:
Angenommen ich hätte tatsächlich falsch auf dynFeld zugegriffen, wo im Programm könnte das sein? Eine Überschreitung des bereits allocierten Speichers ist ja nicht möglich, da ich die if-Anweisung laut Debugger nicht überspringe. Falls es helfen sollte, lege ich einmal die Funktion erstelleNeuesBuch hier rein.

Ich will dich nicht entmutigen, aber es muß nicht mal unbedingt so sein, daß die eigentliche Fehlerursache in unmittelbarer Nähe der Stelle liegt, an der der Fehler zu Tage tritt. Hast du dir erst mal deinen Heap korrumpiert (Heap = der dynamische Speicherbereich), können an beliebigen Punkten im Programm die witzigsten Effekte auftreten, die manchmal in keinerlei Zusammenhang zu dem eigentlichen Fehler mehr zu stehen scheinen. Solche Fehler können deshalb auch unheimlich frustrierend aufzuspüren sein, wenn man die Ursache nicht schon von vornherein auf ein beherrschbares Teilstück des Programm eingrenzen kann.

Ich würde dir, wenn dir der Fehler nicht plötzlich doch noch wie Schuppen von den Augen fällt, dazu raten, dein Programm so lange Schritt für Schritt um Code zu reduzieren, bis der Absturz (oder irgendwelcher anderer seltsamer Käse) nicht mehr auftritt. Also immer wieder was entfernen, neu-kompilieren und ausführen. Funktioniert dann (wenigstens erst mal scheinbar) plötzlich alles, dann ist die Chance hoch, daß der zuletzt entfernte Codeteil zumindest etwas mit dem Problem zu tun hatte.

Des weiteren würde ich dir wärmstens die äußerst freizügige Benutzung des assert()-Makros (definiert in assert.h) ans Herz legen. Wenn du meinst, daß in Zeile so-und-so die Variable x eigentlich den Wert y haben sollte, dann klatsch da ein assert( x == y ); hin. Meinst du, der Pointer blubb dürfte an dieser Stelle niemals NULL sein, dann setz da ein assert( blubb );. Überprüfe jede einzelne Invariante, so inkonsequent sie auch erscheinen mag. So kannst du viele Fehler so früh wie möglich aufspüren, und nicht erst dann, wenn sie schon das ganze Programm in Mitleidenschaft gezogen haben.
 
Zuletzt bearbeitet:
Code:
 *n += erstelleNeuesBuch(zeile, dynFeld + *n, *n);
Ich nehme an, n soll auf die aktuelle Anzahl von Büchern zeigen. Der Ausdruck "dynFeld + *n" zeigt also auf die Array-Position des aktuellen Buches (Hier solltest du evtl. "dynFeld[*n]" verwenden, ich finde das kann man besser lesen).

In der erstelleNeuesBuch-Funktion machst du aber u.A. folgendes:
Code:
 buecher[anzahlAkt].titel = (char *)malloc(strlen(titel_help)+1);
Allerdings zeigt buecher in dem Fall ja schon auf die aktuelle Position, du greifst aber auf die Position anzahlAkt dahinter zu. Wenn z.B. *n = 5, dann ist "dynFeld + 5" das 5. Buch im Array, in der Funktion schreibst du aber wieder in das 5. Element, also insgesamt in das 10. Element.

edit:/ Evtl. könnte der Threadersteller ja mal so eine CSV-Datei hochladen, damit man die Schritte besser nachvollziehen kann.
 
Zuletzt bearbeitet:
Und noch was:

Code:
strcpy(buchNummer_help, token);

Niemals ohne Längenprüfung einfach in einen Speicherbereich langen. Was wenn der Zielpuffer nicht groß genug ist? strcpy() schreibt gnadenlos über Puffergrenzen hinaus - und zerballert dir dann, was immer im Speicher eben dahinter liegt. Verwende wenigstens strncpy() ... dieser Funktion muß man wengistens noch die Größe des Zielpuffers mitgeben. Sollte der zu klein sein, bricht strncpy() den Kopiervorgang ab, statt einfach auf Teufel komm raus über die Puffergrenze hinweg weiterzuschreiben.
 
@ antred: Danke für die Tipps! Werde versuchen sie passend umzusetzen.
Das mit "strcpy" wird funktionieren, da ich bereits eine Funktion habe, die die Länge und die Nummer selbst überprüft. Diese muss nämlich bestimmte Vorraussetzungen erfüllen!

@stwe: Vielen Dank für den Hinweis! Das könnte sehr gut die Fehlerquelle, jetzt wo du es sagst wird mir so einiges klar :D. Ich werde das gleich heute Abend testen.

Eine passende csv-Datei kann ich heute Abend auch hochladen (schreibe grade am Handy).
Ergänzung ()

Habe nun das Programm umgeschrieben und getestet. Es lag wirklich daran, dass ich den Index sowohl außerhalb der Funktion, sowie in der Funktion erneut ausgewählt hatte. Nun komme ich mir etwas blöd vor! :mad:

Vielen Dank an alle für die großartige Hilfe und die tollen Tipps!
 
Ich muss sagen, dass mir dieser Thread gefallen hat. Ich glaube, wir haben hier wirklich geholfen und einen Erkenntnissgewinn beschert, statt wie in anderen Threads so üblich nur "Hier ist die Lösung" und "Guck mal, ich kann das viel schöner schreiben".

Bitte weiter so, Computerbase :3
 
Zurück
Oben