Komm nicht klar auf HÜ in C?

1 Antwort

Vom Fragesteller als hilfreich ausgezeichnet

Wieso willst Du Reallocen(?). Das ist doch gar nicht wirklich nötigt.

Was hast Du denn, oder wo hakt es denn?

Hast Du Dir denn erstmal überlegt, wie Du das ganze als Datenstruktur abbilden möchtest?

ja wir müssen realloc verwenden hab ich vergessen reinzuschreiben:

Dateiinhalt muss am Heap gespeichert werden

  • Vokabelpaare werden in der definierten Datenstruktur am Heap gespeichert
  • Pointer auf die Datenstrukturen werden in einem dynamisch wachsenden Array gespeichert
  • Das Array soll anfangs 10 Elemente groß sein. Wann immer notwendig soll es um 10 Elemente vergrößert werden. (10->20->30->...)

Ich weiß nicht wie ich es schaff zu überprüfen ob der String jetzt zu klein ist und ich erweitern muss oder nicht. Auch ist es mir ein Rätsel wie ich Zeile für Zeile einlesen kann und gleichzeitig überprüfe ob auch wirklich in jeder Zeile zwei Wörter vorkommen.

Als Datastructure hätt ich:

typedef struct

 {

    char *word_1 = malloc(32*sizeof(char));

   char *word_2 = malloc(32*sizeof(char));

 } words;
0
@Maximum12345

Aha, also hat da noch einiges gefehlt.

Die Sache ist halt die:

Ich kann die Datei komplett in den Speicher ziehen und das Array mit den Pointern einfach in den Dateiinhalt zeigen lassen. Das Array ist dann im Endeffekt ein Overlay für einen indizierten Zugriff.

Ich kann die Datei in häppchen einlesen, für jeden String ein malloc() ausführen und so einen Haufen auf den Haufen (Heap) werfen.

Gibt es einen rationalen Grund, warum Du eine fixe Zeichenzahl für die Elemente wählst?

Was das dynamische Array betrifft, sauber kapseln oder nicht, das ist hier die Frage.

struct dyn_arr{
    int size; /* Gesamtgröße */
    int chunk; /* Wachstumsgröße (10) */
    int len;  /* Aktueller Füllstand */
    int alloc; /* Größe von Objekten im Array */
    void *a; /* Basepointer */
}

So in etwa könnte eien geeignete struct aussehen.

(Besser wäre es size_t statt int zu nutzen ...)

Du mußt dann eben nur bei jedem Anfügen schauen, ob noch platz ist, sonst machst Du ein realloc mit size+chunk.

Und wie gesagt, überlege Dir unbedingt, wie Du das imt der Datei handhaben möchtest. Hast Du schon den Parser? Nutzt Du strtok()?

Und kein calloc/reallocarray?

2
@KarlRanseierIII

Die feste Größe sollte 10*sizeof(char) sein (hab mich da mit den 32 vertan) und je nachdem ob das Wort mehr als 10 Zeichen enthält soll halt dann erweitert werden.

Ja das wie realloc funktioniert weiß ich, ich weiß nur nicht wie man das überprüft ob noch genug Platz im String ist.

Also Parser sagt mir leider gar nichts, hatten wir in der VO noch nicht und strtok müsst ich mir auch mal eben kurz anschaun (hab ich leider auch noch nicht davon gehört)

Hmm ich fühl mich halt gerade echt dumm, weil ich gefühlt der einzige bin der hier an die grenzen stößt :/

0
@Maximum12345

Moment, Du sollst eine struct mit zwei char pointern nutzen, die jeweils ein Wortpaar beschreibt.

Ferner ein dynamisches Array, das dann auf diese Datenstrukturen zeigt. Dieses soll dynamisch wachsen.

struct pair{
   char *k;
   char *v;
};

struct dyn_arr{
   [...] /*wie zzuvor beschrieben*/
};

Gehe systematisch und Schritt für Schritt an die Aufgabe:

  1. Kommandozeilenparameter prüfen
  2. dynamisches Array initialisieren
  3. Datei einlesen, hierbei gibt es verschiedene Möglichkeiten
  4. Vokabeln abfragen
  5. Statistik ausgeben

Die Routine zum Dateieinlesen könnte zum Beispiel in etwa so aussehen:

char buf[4096];
struct pair p;
FILE * vok=fopen('filename',r);
while ( fgets(&buf,4095,vok) {
      /* Jetzt die Zeile in die zwei Wörter zerlegen, deren Länge Du natürlich auch bestimmst. Es seien nun s1 und s2 die Teilwörter */
   if (char *tmp=malloc(strlen(s1)+1) )p->k=strcpy(s1,tmp);
   if (char *tmp=malloc(strlen(s2)+2) )p->v=strcpy(s2,tmp);
   /*Jetzt Paar an das Array anhängen */
}

Fehlerbehandlung etc. habe ich mir gespart. Alternativ könnte man wie gesagt die ganze Datei einlesen und dann die Paare erzeugen und einfach nur Pointer auf die richtigen Stellen erzeugen.

Du kannst natürlich auch damit anfangen erstmal die Funktionen für das dynamische Array zu schreiben und diese zu testen, bevor Du Dich dann um das Einlesen der Datei kümmerst.

Wichtig ist halt:

Immer ein Problem nach dem anderen zu lösen und die Probleme erstmal zu umreißen und zu präzisieren.

Ein dynamische Array ist eine Datenstruktur, mit entsprechenden Funktionen, insofern kannst Du das ganz unabhängig vom restlichen Code implementieren.

2
@KarlRanseierIII

Hallo

hab jetzt ein Problem mit realloc -> bekomme immer realloc invalid next size ab dem Element ab dem ich erweitern würde :/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>




typedef struct
  {
      char *vocab_1;
    char *vocab_2;
  } vocabs;

int struct_array(char *word_1, int lines, int cntWords)
{
  static int x = 15;
  static int y = 0;
  void *tmp;
  printf("%d\n",lines);
  vocabs *arr_vocab = malloc(10*sizeof(char));

  if(arr_vocab == NULL)
  {                                       //out of memory
    return 4;
  }

  if(lines > (9 + y))
  {
    y += 5;
    x += 5;
    tmp = arr_vocab;
    tmp = realloc(arr_vocab, x*sizeof(char));
    
    if(tmp == NULL)
    {
      return 4;
    }

    else
    {
      free(arr_vocab);
      arr_vocab = tmp;
      free(tmp);
    }
  }

  if(cntWords == 1)
  {
    arr_vocab[lines].vocab_1 = word_1;
  }

  if(cntWords == 2)
  {
    arr_vocab[lines].vocab_2 = word_1;
  }
 
 
return 0;
}

char cntelements(char* element_str)
{
  int elements;
 
  for(elements = 0; element_str[elements] != '\n'; ++elements)
  {
    continue;
  }

 
  return elements;
}

int counter(char *test_str)
{
  int i;
  int elements = cntelements(test_str);
  int cntWords = 0;
 
 

  for(i = 0; i < elements; i++)
  {
    if(((test_str[i+1] == ' ') || (test_str[i+1] == '\n')) && (test_str[i] != ' '))    
    {
      cntWords++;
      
    }

    if(test_str[i] == '\0')
        return -1;
  }
   
  return cntWords;
}



int make_str(char *buffer_str, int lines)
{
  vocabs vocabs;
  const char *delimiter = " ";
  char *word_str;
  int i;
  char *tmp;
  int X;
 
  int cntWords = counter(buffer_str);

  word_str = strtok(buffer_str, delimiter);
 
 
  while(word_str != NULL)
    {
      
      if(cntWords == 1)
      {
        X = struct_array(word_str, lines, cntWords);
       
      }

      if(cntWords == 2)
      {
        X = struct_array(word_str, lines, cntWords);
       
      }

      word_str = strtok(NULL, delimiter);
        
    }
    
    if(cntWords != 2 && cntWords != -1)
    {
      return 3;
    }
    
  return 0;
 
}


int read_file(char *filename)
{
  char buffer_str[255];
  int new_strs;
  int cnt_lines = 0;
 

  FILE *ptr;
  ptr = fopen(filename, "r");

  if(ptr == NULL)
  {
    return 2;                                             //kann nicht geöffnet werden
  }
  while(!feof(ptr))
  {
    cnt_lines++;
    fgets(buffer_str, 255, ptr);
    
    new_strs = make_str(buffer_str,cnt_lines);
    

    if(new_strs == 3)
    {
      return 3;                                           //falsche Formatierung einer Zeile im Dokument
    }
  }
  return 0;
}







int main(int argc, char *argv[])
{

  vocabs vocabs;
  int file_data;
  char *argx = argv[1];
 

  if(argc < 2)
  {
    printf("usage: [executable] filename\n");
    return 1;
  }
 
  file_data = read_file(argx);

  if(file_data == 2)
  {
    printf("ERROR: cannot open file %s\n", argx);
  }  

  if(file_data == 3)
  {
    printf("ERROR: file %s invalid\n", argx);
  }
 
  printf("%d",file_data);
}
0
@Maximum12345

Invalid next size ist vermutlich die Folge einer Heap-Korruption. D.h. Du berechnest vermutlich irgendwo eine Adresse falsch und machst beim Beschreiben etwas kaputt.

(Das passiert recht leicht)

Füge mal einige Debug Ausgaben hinzu, wo Du Dir die Adressen etc. ausgeben lässt. Eventuell siehst Du es dann leichter (oder Du kannst versuchen mit valgrind an Infos zu kommen, die Dir helfen).

Aber um Dir schonmal etwas mit auf den Weg zu geben:

  char *argx = argv[1];

Normalerweise wird garantiert, daß hinter dem letzten Argument ein Nullpointer folgt (auch aus praktischen Gründen), sodaß das hier auch bei fehlenden Parametern gut ginge. BEsser wäre aber erst nachdem DU weißt, daß es ein Argument ist die Zuweisung zu machen.

  if(argc < 2)
  {
    printf("usage: %s filename\n",argv[0]);
    return 1;
  }

argv[0] enthält den Aufrufname, allerdings wäre heir noch ein basename() angebracht, was aber nicht Teil der Standardbibliothek ist.

    fgets(buffer_str, 255, ptr);

fehlende Fehlerbehandlung, denn:

if ( fgets(buffer_str, 255, ptr) == NULL ) return 2;
 /* Error on fgets or EOF */

usw. usf.

Was mir noch direkt ins Auge gestochen ist:

  if(lines > (9 + y))

Wo kommt diese konstante 9 her?

P.S.: Ich bin immernoch der Auffassung, daß Dein dynamisches Array Objekte vom Typ struct{ char *; char *;}; speichern soll, und nicht die Strings ansich dynamisch wachsen sollen, denn deren Größe kennst Du ja beim Zerlegen der Eingabe bereits.

1
@KarlRanseierIII

Ja ich hätts jetzt eigentlich eh so gemacht, dass meine Strings eine fixe Größe haben und ich das Array anpasse. Nur leider ist mir jetzt auch noch aufgefallen, dass ich beim auslesen der variablen im array auch nur immer den null pointer zurückbekomme. Ich bin echt verwirrt wie man sowas machen soll :/

0
@Maximum12345

https://pastebin.com/Tj7w8Wec

Beispiel nutzt ein fertig typisiertes array, das array ist nicht in die structure eingebettet. Das Einzige was Du realisieren mußt:

Beim EInlesen der Datei die Strings auch auf den Heap werfen, indem Du malloc() nutzt und die beiden Pointer in eine wpair struct wirfst.

Du kannst natürlich noch weitere Funktionen für das Array bauen, z.B. eine, die es komplett leert (der Code findet sich derzeit ja prizipiell schon im Beispiel). Ebenso Funktionen, die die Länge ermitteln, oder einen struct wpair * auf das i. Element liefert usw. . Dadurch wird der restliche Code dann übersichtlicher.

Du kannst natürlich entgegen dem Beispeil die struct dynarr auch auf dem Heap allokieren, da die Funktionen bereits einen Pointer erwarten.

1
@KarlRanseierIII

Wow hast du das geschrieben? Bist meine letzte Rettung! Unendlich viel Dank! hast du eigentlich irgendwelche Buchempfehlungen? Wir haben nämlich kein Skriptum und ich stoße halt mittlerweile echt an meine Grenzen bzw sind sie schon überschritten :(

0
@Maximum12345

Ja habe ich, zur Übung habe ich auch eien generische Variante geschrieben, die quasi 'Objekte' beliebigen Types unterstützt.

Bei solchen 'Fingerübungen' merkt man dann auch immer, wo man ein wenig angerostet ist.

Ich persönlich mochte Kernigham Ritchie - The C Programming Language. Es ist ziemlich knapp, liefert eine sehr spezielle Sicht auf die Sprache. Die deutschen Übersetzungen beinhalten leider einige Fehler :-/.

Ergänzung, falls es nicht klar ist: Lässt Du -DDEBUG als Parameter weg, werden die dbg_prints ignoriert. Ich habe bewußt übermässige viele Debug-Prints eingebaut, damit man besser versteht, was da so vor sich geht, auch mit den Pointern etc. .

1

Was möchtest Du wissen?