# verkehrte string und speicherverwaltung



## Heinzi1991 (7. November 2013)

Aufgabenstellung:
Es sollen von der Konsole (stdin) Zeichen eingelesen werden, bis das Ende des Eingabestreams (EOF) erreicht ist. Beim Testen kann man den Stream mit dem Tastenkürzel Strg+D (in Windows Strg+Z) beenden, oder man leitet als Eingabe für das Programm eine Datei auf stdin des Programms um. Das sieht mit der Datei input.txt zum Beispiel so aus: 

```
./hw5 < input.txt
```

Eingelesen werden soll in einen dynamisch reserviertem Speicher (malloc), der wenn er voll ist vergrößert wird (realloc). Sobald der Eingabestream zu Ende ist, werden die eingelesenen Zeichen rückwärts wieder ausgegeben und mit einem "\n" abgeschlossen.

Im Falle eines Fehlers soll die Fehlermeldung "Error: Out of memory!\n" ausgegeben werden und das Programm mit dem Fehlercode 1 ohne weitere Ausgaben beendet werden. 


Beispielaufruf:

```
% ./hw5
Hallo Welt!\n
Jetzt noch ein Zeilenumbruch und dann EOF.\n[EOF]
\n
.FOE nnad dnu hcurbmunelieZ nie hcon tzteJ\n
!tleW ollaH\n
```

mein code ist jetzt ein bisschen anders, aber er funktioniert so einigermaßen, will es aber natürlich richtig machen, aber ehrlich gesagt hab ich keinen plan was ich machen soll, genau!
kann mir jemand schriftlich, also punkt für punkt (kann auch grob sein) zusammenfassen was ich machen soll!

PS: codeabschnitte sind natürlich erlaubt!

Vielen Dank im voraus!


----------



## cwriter (7. November 2013)

Hallo Heinzi1991

Wie sieht denn dein Code aus?

Grundsätzlich: Mit fgetc(stdin) die einzelnen Zeichen in einer Schlaufe einlesen, bis der Rückgabewert EOF ist. Die Zeichen in einem char-Array, der sich dynamisch erweitert, abspeichern.


```
int main(int argc, char* argv[])
{
    char* speicher = NULL;
    char* tmp = NULL;
    size_t size = 0;
    char c = 0;
    while((c = fgetc(stdin)) != EOF)
    {
        size++;
        tmp = (char*)malloc(size);
        if(tmp == NULL)
        {
            printf("Error: Out of memory!\n");
            return 1;
        }
        memcpy(tmp,speicher,size);
        speicher = (char*)realloc(speicher,size);
        if(speicher == NULL)
        {
            printf("Error: Out of memory!\n");
            return 1;
        }
        memcpy(speicher,tmp,size);
        speicher[size] = c;
        free(tmp);
    }
    //Nun zur Ausgabe
    for(size_t i = size;i>0;i--)
    {
        printf("%c",speicher[i]);
    }
    printf("\n");
    free(speicher);
    return 0;
}
```

Ungetestet und ohne Abbruchmechanismen.

Gruss
cwriter


----------



## sheel (7. November 2013)

Hi

zeig doch mal deinen "anderen" Code, dann kann man beim Verbessern helfen

Pseudocode:

```
uint fill=0, gesamt=10;
char *array;
char c;
array=malloc(gesamt);
wenn mallocfehler dann fehler-printf und exit 1
while 1
{
    c =getc();
    if(c == -1)
        break;
    array[fill++] = c;
    wenn fill >= gesamt
    {
        gesamt *= 2;
        realloc(array, gesamt);
        wenn reallocfehler dann fehler-printf und exit 1
    }
}
for(--fill;fill >= 0;fill--)
    putc(array[fill]);
putc('\n');
free(array);
```

edit: Ok, einen fertigen Code hast du ja auch schon bekommen


----------



## cwriter (7. November 2013)

@sheel
"Fertig"... Ich könnte wetten, dass es da noch ein paar schöne Memory Leaks drin hat ;-)
Ich bin gerade etwas unsicher: Realloc garantiert ja nicht, dass der Speicherbereich erweitert wird. Müsste bei deinem Pseudocode nicht noch ein temporärer Speicher erstellt werden?

@Heinzi1991
Entschuldige bitte, dass es kein Ausschnitt ist. Ich konnte einfach nicht widerstehen :-(
Gibt es noch irgendwelche Vorgaben, WIE der Speicher erweitert werden soll? Mein Code bietet eher langsame Einzelschritte, verbraucht aber nur den benötigten Speicher, während sheels Variante schneller ist, aber auch "zu viel" Speicher (Vielleicht 10 Bytes mehr) verbrauchen kann.

Gruss
cwriter


----------



## sheel (7. November 2013)

cwriter hat gesagt.:


> Müsste bei deinem Pseudocode nicht noch ein temporärer Speicher erstellt werden?


Da hast du Recht.
Wenn man den größeren Speicher nicht bekommt braucht man zumindest noch was zum free´n.


(
Andererseits...
"Wenn reallocfehler dann mach blabla"
Wie diese Prüfung erfolgt bleibt offen.
Da steht nirgends, dass keine zusätzliche Variable dazugehört 
)


----------



## Heinzi1991 (7. November 2013)

genau; also es besteht keine weitere prüfung oder so! alles ganz easy; mein code bestand darin die datei input.txt zu lesen und dann auszugeben! aber dann hab ich gelesen das man das eig nicht darf!
werde jetzt einmal deine lösung checken wegen den angeblichen leaks und so und werde bescheid geben wenn alles passt oder eine fehelerkorrektur durchführen und dann in fehlerlosen code posten!
Aber danke vielmals


----------



## sheel (7. November 2013)

@cwriter: Mir will nicht in den Kopf, wozu ein voll reservierte tmp
und die Kopiererei gut ist...jedenfalls, zu den Speicherlecks D):

Zeile 16 erst nach 17, weil zuerst das Array noch zu klein ist.
Bei den beiden Error-returns zuerst freigeben, was vorhanden ist.
Zeile 24 ist ein ungültiger Index.
Und die Schleife auf 28 zählt um eins verschoben.


----------



## cwriter (8. November 2013)

Ohoh, stimmt natürlich.

```
memcpy(tmp, speicher, size-1); //line 16
```
Zeile 17 muss nicht vor Zeile 16, da ja auch ein Speicherbereich alloziert werden könnte, der die Daten nicht beinhaltet.
Zur Kopiererei: Gut möglich, dass ich irre, aber kopiert realloc den content gleich auch zur neuen Destination? Ich meine, dass das eben nicht geben ist, und daher wollte ich da nichts riskieren.

```
speicher[size-1] = c; //line 24
```
Bei der for-Schlaufe sollte es reichen, alle Konstanten -1 zu nehmen.

Gruss cwriter


----------



## sheel (8. November 2013)

cwriter hat gesagt.:


> Gut möglich, dass ich irre, aber kopiert realloc den content gleich auch zur neuen Destination?
> Ich meine, dass das eben nicht geben ist, und daher wollte ich da nichts riskieren.


Sollte es schon machen
(falls überhaupt eine Verschiebung nötig ist.
Eventuell kann man das Array ja auch am alten Platz lassen
und nur die Reservierung nach hinten raus vergrößern oÄ.
Impl.abhängig)


----------



## Heinzi1991 (8. November 2013)

ok es schaut verdammt gut aus! jetzt gibt es nur noch vier Probleme!

1. Problem: Der erste Buchstabe wird nicht ausgespuckt und in der ausgabe! Also bei "hallo welt" gibt er "tlew olla" aus!

2. Problem: er beendet das Einlesen nicht bei [EOF] ich muss das Programm immer selbstständig abbrechen, bzw das einlesen!

3. Problem: size_t ist nur in C99 möglich, aber wir benutzen keine Ahnung welches C! also ich benutze Ubuntu 12.04 LTS und die vorigen Aufgaben haben reibungslos funktioniert!

4. Problem: 
Warnung: Implizierte Deklaration der Funktion >>memcpy<<
Warnung: Unverträgliche implizite Deklaration in der eingebauten Funktion >>memcpy<<


----------



## sheel (8. November 2013)

Problem 1:
Hast du die vier oben festgestellten Fehler schon gesehen bzw. bei dir ausgebessert?

P2: Was bedeutet das genau?
Tastatureingabe oder Datei mit < ?

P3+4: Wie/womit kompilierst du? (Compileraufrufszeile am Besten)
Hast du stdlib.h und string.h inkludiert?


----------



## Heinzi1991 (8. November 2013)

jap hab ich schon ausgebessert!

tastatureingabe! keine datei

ich kompiliere mit dem terminal! also aufruf: gcc -Wall -o hw5 hw5.c
string.h hab ich nicht inkludiert. aber wir dürfen das auch nicht; leider gottes!


----------



## sheel (8. November 2013)

Zum Abbrechen:
Ist das nicht so gedacht?


Heinzi1991 hat gesagt.:


> Es sollen von der Konsole (stdin) Zeichen eingelesen werden, bis das Ende des Eingabestreams (EOF) erreicht ist. Beim Testen kann man den Stream mit dem Tastenkürzel Strg+D (in Windows Strg+Z) beenden...



size_t. Hm...naja, wenns nicht bekannt ist, nimm einfach unsigned int.
Ist prinzipiell nichts Anderes.

memcpy: Wenn die Headerdatei nicht inkludiert werden darf. kann man das so nicht verwenden
(bzw. man könnte den entprechenden Teil selbst definieren,
aber das wird nicht im Sinn vom Lehrer sein.)
Was ein memcpy(a,b,c) tut ist einfach nur c Bytes vom Array b in Array a zu kopieren.
Wenn man ein unbenütztes int i hat und a und b char-Arrays sind (wie bei diesem Code eben),
einfach ersetzen mit:

```
for(i = 0; i < c; i++)
    a[i] = b[i];
```


----------



## cwriter (8. November 2013)

Zu Punkt 3:
Ersetze size_t durch unsigned int.

Gruss
cwriter

Edit: Ach Menno, sheel war schneller 

EDIT 2: Nach cplusplus.com KANN realloc den Speicherblock in den neuen kopieren. In dem Falle brauchst du auch das memcpy nicht.


----------



## Heinzi1991 (8. November 2013)

So es funktioniert! hab alles ausgebessert und es klappt! die Ausgabe wird richtig ausgegeben!
Danke vielmals!


----------



## Scal (11. November 2013)

@Heinzi1991

Hallo, 

ich habe ein ähnliches Problem. Magst du vielleicht deinen Code posten damit ich sehen kann was du nun gemacht hast? Du hast erwähnt das du dies vll tust 

"werde jetzt einmal deine lösung checken wegen den angeblichen leaks und so und werde bescheid geben wenn alles passt oder eine fehelerkorrektur durchführen und dann in fehlerlosen code posten!"

lg


----------



## dobi_1990 (11. November 2013)

sheel hat gesagt.:


> Zum Abbrechen:
> Ist das nicht so gedacht?
> 
> 
> ...



Ich verstehe das mit dem ändern des memcpy nicht.
Könntest du das bitte anhand dieses Programms einmal für mich machen.

Danke


----------



## sheel (12. November 2013)

Was genau verstehst du denn nicht?

memcpy macht nichts anderes, als Werte von einem Array in ein anderes zu kopieren.
Die drei Parameter, die die Funktion bekommt sind:
1: Das Zielarray (das mit Werten vom anderen gefüllt wird)
2: Das Quellarray (aus dem wird eben gelesen, es selbst wird nicht verändert)
3: Wie viele Byte kopiert werden sollen.
Einfach eine Zahl, oder eine int-Variabale mit dem Wert drin.
Achtung: Wieviel _Byte_, nicht wieviel Arrayelemente.
Wenn man drei char von einem char-Array kopieren will ist es also 3.
Für das Kopieren von 3 float aus einem float-Array wäre es 12 (weil ein float 4 Byte braucht)

Wenn man nur den Fall char-Array hernimmt kann man das Kopieren mit einer ganz einfachen Schleife auch machen.
Folgender Code mit memcpy:

```
char array1[100];
char array2[100];
...
//irgendwas in array1 reinspeichern
...
memcpy(array2, array1, 100); //das komplette array1 in array2 kopieren
```
kann auch so geschrieben werden:

```
char array1[100];
char array2[100];
...
//irgendwas in array1 reinspeichern
...
int zaehler; //Man braucht etwas für die Schleife zum durchzählen

for(zaehler = 0; zaehler < 100; zaehler++)
    array2[zaehler] = array1[zaehler];
```


----------



## EscTheCtrl (12. November 2013)

Hey =)

kann man dies auch ohne memcpy machen?
wenn man ein Array mit realloc() vergrößert wird doch der alte Inhalt mitkopiert.

mal angenommen wir befüllen ein Array_1, sobald dieses "voll" ist, erweitern wir  mit realloc(). sagen wir das Array_1 zu Beginn Platz für 64 Byte hatte. 


```
Array_tmp = realloc(Array_1,(sizeof(char) *128)); // Array_tmp hat nun Inhalt von Array_1 und hat insgesamt 128 Byte (wobei 64  
                                                  // Byte schon initialisiert sind)
```

Nun bin ich mir nicht sicher , wenn ich den Pointer von Array_tmp dem Array_1 übergebe, ob der Inhalt der davor initialisierten Bytes erhalten bleibt .

Also wenn ich schreiben würde:


```
Array_1 = Array_tmp;  //kopiert pointer von Array_tmp zu Array_1
free(tmp);
tmp=NULL;
```

Bzw. darf man dies überhaupt so machen ? 

Mfg


----------



## cwriter (12. November 2013)

> kann man dies auch ohne memcpy machen?
> wenn man ein Array mit realloc() vergrößert wird doch der alte Inhalt mitkopiert.


Meistens schon. Die Definition garantiert das aber nicht.


> Nun bin ich mir nicht sicher , wenn ich den Pointer von Array_tmp dem Array_1 übergebe, ob der Inhalt der davor initialisierten Bytes erhalten bleibt .


Der Speicher bleibt reserviert, ja.


> Bzw. darf man dies überhaupt so machen ?


Ich weiss nicht, was bei dir "tmp" ist. Die erste Zeile deines Codes sollte reichen.

Gruss
cwriter


----------



## Caligulaminus (12. November 2013)

cwriter hat gesagt.:


> EscTheCtrl hat gesagt.:
> 
> 
> 
> ...


Das ist garantiert, das ist ja schließlich Sinn und Zweck von realloc().


----------



## sheel (12. November 2013)

http://pubs.opengroup.org/onlinepubs/009604499/functions/realloc.html
Zwar auch nicht "das" Standarddokument, aber ich denke, dass man sich darauf verlassen kann.


----------



## cwriter (12. November 2013)

Upsala, stimmt, da habe ich mich verlesen.
Auf cplusplus.com steht:


> The function may move the memory block to a new location (whose address is returned by the function).


Und das habe ich fälschlicherweise als "möglicherweise" interpretiert.

Asche auf mein Haupt
cwriter


----------



## wolfi1992 (13. November 2013)

Hallo ! Ich hab da mal eine Frage wo wird denn hier überprüft wann der Speicher voll ist und erweitert werden muss ?! Fehlt da nicht eine if ? 



```
int main(int argc, char* argv[])
{
    char* speicher = NULL;
    char* tmp = NULL;
    size_t size = 0;
    char c = 0;
    while((c = fgetc(stdin)) != EOF)
    {
        size++;
        tmp = (char*)malloc(size);
        if(tmp == NULL)
        {
            printf("Error: Out of memory!\n");
            return 1;
        }
        memcpy(tmp,speicher,size);
        speicher = (char*)realloc(speicher,size);
        if(speicher == NULL)
        {
            printf("Error: Out of memory!\n");
            return 1;
        }
        memcpy(speicher,tmp,size);
        speicher[size] = c;
        free(tmp);
    }
    //Nun zur Ausgabe
    for(size_t i = size;i>0;i--)
    {
        printf("%c",speicher[i]);
    }
    printf("\n");
    free(speicher);
    return 0;
}
```

Lg Wolfi


----------



## deepthroat (13. November 2013)

wolfi1992 hat gesagt.:


> Hallo ! Ich hab da mal eine Frage wo wird denn hier überprüft wann der Speicher voll ist und erweitert werden muss ?! Fehlt da nicht eine if ?


Nein, der Speicher ist immer "voll".

Es wird in jedem Schleifendurchlauf realloc aufgerufen, d.h. der Speicher wird jedes Mal genau um 1 Byte erweitert. (was an sich relativ ineffizient ist)

Wie schon festgestellt wurde, ist der Codeteil mit dem memcpy ziemlich sinnfrei.


----------



## cwriter (13. November 2013)

Dein Code ist nicht vollständig. Wie sieht der Rest aus?
Und du reallokierst den Speicher nur einmal.
Sollte das nicht 
	
	
	



```
size % BLOCK_SIZE == 0
```
 heissen?

Gruss
cwriter


----------



## wolfi1992 (13. November 2013)

Hallo liebe Leute

könnte mir bitte irgendjemand helfen und sagen wo mein Fehler liegt das mein speicher nur einmal erweitert wird?


```
int main(int argc, char* argv[])
{
  
  char* temp_array = NULL;
  unsigned int len = 0;
  unsigned int size = 0;
  unsigned int i;
  char c = 0;

  temp_array = (char*)malloc(BLOCK_SIZE);

  if(temp_array == NULL)
  {
    printf("Error: Out of memory!\n");
    return 1;
  }

  while((c = fgetc(stdin)) != EOF)
  {
    len++;
		
    if(len == BLOCK_SIZE)
    {
      size = BLOCK_SIZE + len;
      temp_array = (char*)realloc(temp_array,size);

      if(temp_array == NULL)
      {
        printf("Error: Out of memory!\n");
        return 1;
      }
    }
    printf("%d SPEICHER\n", size);
    temp_array[len-1] = c;
  }
```


----------



## Caligulaminus (13. November 2013)

Da len ja stetig wächst, kann es nur ein Mal gleich BLOCK_SIZE sein.

Edit: P.S. feof() gibt int zurück, EOF ist ein int.


----------



## deepthroat (13. November 2013)

Du müßtest dir die aktuelle Größe des Speichers _und_ den Füllstand des Speichers (len) merken.

Immer wenn der Speicher erschöpft ist (Füllstand >= Größe), müßtest du den Speicher (um Blockgröße) vergrößern und die neue Größe speichern.


----------



## wolfi1992 (13. November 2013)

logisch x) danke euch !


----------

