[C] Fragen zu fgets, malloc/realloc und memset

rohrbold

Mitglied
Hallo beisammen,

bei einer kleinen Programmieraufgabe stieß ich unvermittelt auf drei mir unbekannte Problemfälle, deren Lösung mir nicht ganz klar ist. Es wäre toll, wenn mir jemand erklären könnte, warum manche Dinge so sind, wie sie sind ;-)

  • fgets: Angenommen man möchte von der Standardeingabe eine gewisse Anzahl Zeilen in einen Puffer einlesen, interessiert sich aber nur für die ersten m Zeichen. Ein intuitives Codefragment sähe so aus:
C:
#include <stdlib.h>
#include <stdio.h>

int main()
{
        char buf[100], a[3][4];
        int rows = 3, cols = 4, i, j;

        for (i = 0; i < rows; ++i) {
                fgets(buf, cols, stdin);
                for (j = 0; j < cols; ++j)
                        a[i][j] = buf[j];
        }

        printf("done\n");

        return 0;
}
Dieses Programm bricht allerdings schon nach zwei eingelesenen Zeilen ab. Funktionieren tut es allerdings, wenn man den fgets Aufruf im zweiten Argument mit der Größe des Puffers füttert, also fgets(buf, sizeof(buf), stdin);
Warum ist das so?
  • memset: Wie kann man einen Speicherbereich geschickt initialisieren, wenn man mit einem dynamisch allozierten, mehrdimensionalen Array arbeitet? Folgendes Codefragment initialisiert den Speicherbereich über zwei for-Schleifen:
C:
fgets(buf, sizeof(buf), stdin);
/* take care of ASCII representation */
n = (int) buf[0] - '0';
m = (int) buf[2] - '0';

/* allocate space for point[x][y] */
point = (char **) realloc(point, n * sizeof(char *));
for (i = 0; i < m; ++i)
        point[i] = realloc(point[i], m * sizeof(char));

/* initialization */
for (i = 0; i < n; ++i)
        for (j = 0; j < m; ++j)
                point[i][j] = '.';
Wie könnte man also die letzte Phase der Initialisierung mittels memset() realisieren?
  • malloc/realloc: In einer Endlosschleife möchte ich immer eine gewisse Anzahl Zeilen von stdin einlesen; dafür muss ich Speicher entsprechend allozieren und am Ende der Schleife wieder freigeben. Intuitiv habe ich dafür mit malloc() und free() gearbeitet, allerdings segmentiert nach ein paar Eingaben mein Programm. Funktionieren tut es allerdings mittels realloc(), so wie in obigem Codefragment schon gezeigt.
    Was ist denn daran falsch, am Beginn der Schleife ein malloc() statt eines realloc() auszuführen, wenn man am Ende sowieso den Speicher mittels free() wieder freigibt?
 
Hi

hab leider net so viel zeit deshalb mach ich mal 1.)

also.

Wenn du dich fragst warum fgets immer zu früh aufhört musst du verstehen wie fgets arbeitet.

Also fgets ließt solange ein bis es die max zahl (fgets(buffer,max_zahl,stdin)) erreicht hat + das '\0' Zeichen (das \0 wird an jeden String angehängt und deshalb ließt fgets nur max_zahl -1 Zahlen ein oder ein \n Zeichen kommt dann hört es automatisch auf. Der Grund warum es bei dir zu früh aufhört ist die anzahl der Zeichen.

BSP:

Sagen wir mal du gibst 12345678 ein:

Also fgets ließt die ersten 3 Zeichen ein plus das '\0'. Dann hört es auf und die Schleife überträgt es in den Array a. Dann kommt wieder fgets drann und ließt die restlichen Zeichen im Puffer(stdin) weiter ein wieder max 3 + '\0' = 4 jetzt befinden wir uns bei 6 und fgets hört wieder auf und die Schleife beginnt. Dann kommt noch mal fgets dieses mal aber kann es nur 2 Zeichen lesen bis zur 8 da dann ein \n bzw enter kommt und dann beginnt wieder die Schleife..... Und schluss endlich sind die 3 Duchgänge in einer Zeile verbraucht.

Wenn du aber sizeof(BUF) nimmst dann hast du 100 Zeichen die Gelesen werden können und das wäre bei 12345678\n bis zum \n und dann wären wir in der Nächsten Zeile da fgets ja bei \n aufhört zu lesen.

Und warum bei dir genau nur 2 zeilen waren kann ich dir auch sagen.

du hast bestimmt versucht volgendes einzugeben.

1.) 1234\n (enter)
2.) 1234

1.) fgets ließt von 1 bis 3 und dann von 4 bis \n bzw enter.
2.) fgets ließt von 1 bis 3 und dann sind die 3 duchgänge erreicht und programm ende.


Du solltest dir immer merken das fgets immer nur Zahl-1 Chars ließt da 1 platz für das \0 resaviert ist.

hoffe ich konnte dir helfen

und bei Beispiel 2 blick ich sowieso nicht durch. was machst du da?
ka bitte besser dokumentieren.

n = (int) buf[0] - '0';

m = (int) buf[2] - '0';

wenn du oben beim

fgets "hallo" eingibtst
dann machst du ein m = 104 (h) - 48(0)


wieso machst du keine sicherheits abfrage if(buf[0] <48 || buf[0] >57){ printf("keine Zahl"); ......}

mfg mike4004
 
Zuletzt bearbeitet:
Ok, vielen Dank für die Erläuterung meines ersten Problems. Ist für mich leider nicht ganz intuitiv, warum das so funktioniert, aber dank Deiner Erklärung werde ich mir das hoffentlich künftig merken.

Zu meinem zweiten Problem. Der zitierte Codeausschnitt ist nicht wirklich relevant für das Problem, dass ich damit habe. Im Endeffekt geht es nur darum, aus der Benutzereingabe zwei Zahlen auszulesen, die die Zeilen und Spalten repräsentieren. Genau gesagt geht es um ein Minesweeper Problem, bei dem der Benutzer zu allererst die Anzahl Zeilen und Spalten angibt, für die er später dann die entsprechende Belegung angibt (als Characters), also zum Beispiel so:
Code:
martin@bart:~$ ./mine
4 4
*...
....
..*.
....
Ich habe jetzt in meinen Beispielen auch bewusst versucht auf diverse Sicherheitsabfragen zu verzichten -- der Übersichtlichkeit halber. Im eigentlichen Programm tummeln sich da noch einige Abbruchbedingungen.
 
Hi

nun habe ich Zeit für Problem 2.) zwar nicht viel aber was solls.


C:
for (i = 0; i < n; ++i)
memset(point[i], '.' ,sizeof(point[i]));

habe es nicht getestet oder compiliert aber sollte gehen.(wenn nicht melden).

achja bei Code 2 in line 9 würde ich noch ein
Code:
point[i] =(char *) realloc(point[i], m * sizeof(char));
machen.

und leider geht mir an der Stelle schon wieder die Zeit aus und habe Problem 3 nicht wirklich verstanden im vorüberfliegen.

Allerdings ist es normal das der anfang immer mit malloc initialisiert wird. denn realloc ist nur zum erweitern des Speichers vorgesehen. Und kopiert alle Datensätze vom allten in den Speicherpereich. Wie es jedoch reagiert wenn es mit einem leeren array aufgerufen wird kann ich nicht sagen müsste ich nachlesen. Aber normalerweise wird der erste Speicherblock immer mit malloc angefordert.

mfg mike4004
 
Zuletzt bearbeitet:
Zurück