C: malloc, Initialisierung, free auslagern?

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

C: malloc, Initialisierung, free auslagern?

Beitrag von paedubucher » 11.06.2017 17:55:56

Ich habe ein C-Programm, in dem ein zweidimensionales char-Array vorkommt. Die erste Dimension ist dabei flexibel (gemäss Kommandozeilenparameter), die zweite statisch. Für das Array soll erstens Speicher reserviert werden. Dann soll es mit einem bestimmten Character vorinitialisiert werden. Und am Ende des Programmes soll der Speicher wieder freigegeben werden.

Als Java-Programmierer bin ich mich an sogenannte Factory-Methoden gewöhnt. Auf C übersetzt hätte ich nun eine Funktion, die Speicherreservierung und Initialisierung übernimmt. Ich würde also so etwas schreiben:

Code: Alles auswählen

char **init(int n, int  m)
{
    char **table = (char**)malloc(sizeof(char*) * n);
    if (table == NULL) {
        exit(EXIT_FAILURE);
    }
    for (i = 0; i < n; i++) {
        table[i] = (char*)malloc(sizeof(char) * m);
        if (table[i] == NULL) {
            exit(EXIT_FAILURE);
        }
    }
    // Initialisierung
    return table;
}
Das free müsste nun in der übergeordneten Funktion ablaufen. Ich habe von der Faustregel gehört, dass eine Funktion, die Speicher reserviert, denselben Speicher auch immer wieder freigeben sollte. (Des weiteren reklamiert splinter, dass table möglicherweise als null zurückgegeben werden könnte, was ja wegen exit() nicht der Fall sein kann.)

Was meint ihr: ist das Reservieren des Speichers in einer solchen init-Funktion legitim oder ist das schlechter (von Java inspirierter) C-Stil? Sollte das malloc() in der übergeordneten Funktion aufgerufen werden, und die init()-Funktion nur für die Initialisierung der eigentlichen Werte verwendet werden?
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: C: malloc, Initialisierung, free auslagern?

Beitrag von Meillo » 11.06.2017 18:20:19

paedubucher hat geschrieben:Ich habe ein C-Programm, in dem ein zweidimensionales char-Array vorkommt. Die erste Dimension ist dabei flexibel (gemäss Kommandozeilenparameter), die zweite statisch. Für das Array soll erstens Speicher reserviert werden. Dann soll es mit einem bestimmten Character vorinitialisiert werden. Und am Ende des Programmes soll der Speicher wieder freigegeben werden.
Verwaltest du die einzelnen Unterarrays dynamisch? Weil sonst wuerde ich die komplette Speicherflaeche mit einem einzigen malloc(3) anlegen: malloc(sizeof(char *) * n * m)

Wenn du nur am Programmende den Speicher freigibst (weil der Speicher nur genau einmal angelegt wird und dann die ganze Programmlaufzeit konstant bleibt), dann kannst du dir das auch ganz sparen, weil am Programmende sowieso aller Speicher freigegeben wird. Dies wird von verschiedenen Personen wohl unterschiedlich gesehen werden, ich unterscheide in meiner Sichtweise halt zwischen Speicherflaechen, die nur einmal alloziert werden und dann die ganze Laufzeit lang konstant vorhanden sind und welchen, die im Betrieb angelegt und freigegeben werden.


Ich habe von der Faustregel gehört, dass eine Funktion, die Speicher reserviert, denselben Speicher auch immer wieder freigeben sollte.
Das kann natuerlich so nicht sein, weil wenn die Funktion den Speicher gleich wieder freigibt, dann hat man ja nichts davon. Die Regel ist, dass Allozierung und Freigabe auf dem gleichen Layer passieren sollen. Wenn du also eine Funktion anbietest, die Speicher alloziert, dann sollest du auch eine anbieten, die diesen Speicher wieder freigibt ... z.B. createobj() und deleteobj(). Man sagt, dass man bei der Speicherverwaltung immer eine Symmetrie von Allozierung und Freigeben haben will.

[...] die init()-Funktion nur für die Initialisierung der eigentlichen Werte verwendet werden?
Ich denke, es muss halt klar und einheitlich sein. Der Begriff ``init'' wird manchmal als ``create'' verstanden und manchmal nur als ``set initial state'', darum ist nicht unbedingt klar, ob eine Funktion diesen Namens das ``Objekt'' erzeugt oder nur ein gegebenes Objekt in den Initialzustand versetzt.

Darueber hinaus ist es schwierig deine Frage zu beantworten, weil nicht klar ist wie dein Code sonst strukturiert ist. Du sollstest dich vielleicht fragen, auf welcher Ebene man weiss wieviel Speicher angefordert werden muss, weil das eben abstrahiert sein sollte, falls man die interne Struktur des Objekts (hier das 2D-Array) mal aendern will. Wenn du dir klar machst, wie deine Abstraktionen sind, dann sollte es klar werden wo du deinen Speicher anfordern musst.
Use ed once in a while!

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: C: malloc, Initialisierung, free auslagern?

Beitrag von paedubucher » 11.06.2017 19:15:51

Meillo hat geschrieben: Verwaltest du die einzelnen Unterarrays dynamisch? Weil sonst wuerde ich die komplette Speicherflaeche mit einem einzigen malloc(3) anlegen: malloc(sizeof(char *) * n * m)
Ich poste mal den tatsächlichen Code (es geht um das "Monty Hall"-Problem, siehe Smalltalk-Forum):

Code: Alles auswählen

char **init_games(int n_games, int doors)
{
    int game;
    char **games;

    games = (char**)malloc(sizeof(char*) * n_games);
    if (games == NULL) {
        fail("cannot allocate memory for games");
    }

    for (game = 0; game < n_games; game++) {
        games[game] = (char*)malloc(sizeof(char) * doors);
        if (games[game] == NULL) {
            fail("cannot allocate memory for doors");
        }
    }

    return games;
}
Und hier der Aufruf dazu:

Code: Alles auswählen

char **games = NULL;
// [,..]
games = init_games(n_games, 3);
Wenn ich das mit einem malloc versuche, bekomme ich später einen Segmentation-Fault:

Code: Alles auswählen

games = (char**)malloc(sizeof(char *) * n_games * doors);
Müsste ich nicht statt char * einfach char nehmen? (Geht leider auch nicht.) Irgendwie stehe ich da auf dem Schlauch.
Meillo hat geschrieben: Wenn du nur am Programmende den Speicher freigibst (weil der Speicher nur genau einmal angelegt wird und dann die ganze Programmlaufzeit konstant bleibt), dann kannst du dir das auch ganz sparen, weil am Programmende sowieso aller Speicher freigegeben wird. Dies wird von verschiedenen Personen wohl unterschiedlich gesehen werden, ich unterscheide in meiner Sichtweise halt zwischen Speicherflaechen, die nur einmal alloziert werden und dann die ganze Laufzeit lang konstant vorhanden sind und welchen, die im Betrieb angelegt und freigegeben werden.
Sagen wir es mal so: Bei meinem aktuellen Programm ist es eigentlich nicht nötig. Wenn aber die Simulation immer und immer wieder mit anderen Werten laufen sollte, die der Benutzer interaktiv eingibt, wäre es schon nötig.
Dazu noch eine weitere Frage: Ginge das free dann auch in einem Schritt? Im Moment mache ich:

Code: Alles auswählen

for (game = 0; game < n_games; game++) {
    free(games[game]);
}
free(games);
Meillo hat geschrieben:
Ich habe von der Faustregel gehört, dass eine Funktion, die Speicher reserviert, denselben Speicher auch immer wieder freigeben sollte.
Das kann natuerlich so nicht sein, weil wenn die Funktion den Speicher gleich wieder freigibt, dann hat man ja nichts davon.
Natürlich, aber dann wäre es ja ein Argument, das malloc nicht in eine Unterfunktion auszulagern, weil das Programm so gar nicht umzusetzen wäre.
Meillo hat geschrieben: Die Regel ist, dass Allozierung und Freigabe auf dem gleichen Layer passieren sollen. Wenn du also eine Funktion anbietest, die Speicher alloziert, dann sollest du auch eine anbieten, die diesen Speicher wieder freigibt ... z.B. createobj() und deleteobj(). Man sagt, dass man bei der Speicherverwaltung immer eine Symmetrie von Allozierung und Freigeben haben will.
Das dürfte die Regel sein, die ich damals aufgeschnappt, aber nicht richtig verstanden habe: malloc und free müssen nicht in der gleichen Funktion stehen, sie sollten einfach im Call-Stack auf der gleichen Ebene stehen, wenn man das so ausdrücken kann...?
Meillo hat geschrieben:
[...] die init()-Funktion nur für die Initialisierung der eigentlichen Werte verwendet werden?
Ich denke, es muss halt klar und einheitlich sein. Der Begriff ``init'' wird manchmal als ``create'' verstanden und manchmal nur als ``set initial state'', darum ist nicht unbedingt klar, ob eine Funktion diesen Namens das ``Objekt'' erzeugt oder nur ein gegebenes Objekt in den Initialzustand versetzt.
Mit dem oben behandelten wäre ja das Reservieren des Speichers legitim. Ich habe gerade erst gemerkt, dass ich die eigentliche Initialisierung doch in main drin hatte, von daher sollte die Funktion eher alloc statt init heissen.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: C: malloc, Initialisierung, free auslagern?

Beitrag von paedubucher » 14.06.2017 12:42:16

Jetzt habe ich das Problem verstanden, glaube ich. Wenn ich das alles auf einmal alloziere, habe ich ein eindimensionales Array, worauf ich dann nicht so:

Code: Alles auswählen

array[a][b];
sondern so:

Code: Alles auswählen

array[a*B+b];
Darauf zugreifen muss! Da mir die andere Variante des Zugriffs jedoch bequemer ist, bleibe ich bei der Allokation in mehreren Schritten. Hier ist der ganze Code.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Antworten