Zeiger aus Funktion zurückgeben



  • Hallo, ich möchte einen Zeiger per call-by-reference an eine Funktion übergeben. Die Funktion soll eine dynamische Speicherzuweißung durchführen, Werte hinzufügen und das "Array" zurückliefern.
    Meine Idee:

    #include <stdio.h>
    #include <stdlib.h>
    
    void speicher( char *feld)
    {
    	feld=(char *)calloc(3,sizeof(char));
    	feld[0]='b';
    	feld[1]='i';
    	feld[2]='\0';
    }
    
    main()
    {
    	char *feld=NULL;
    
    	speicher(feld);
    
    	printf("%s",feld);
    }
    

    Aber leider steht der Zeiger in der main trotzdem auf Null, obwohl die Funktion die Werte einfügen sollte. 😕


  • Mod

    Derzeit übergibst du den char "by reference". Wenn du den Zeiger verändern willst, brauchst du logischerweise einen Zeiger auf einen Zeiger.

    In einer Funktion ein verstecktes malloc durchzuführen ist jedoch sehr schlechter Stil, das gewöhnst du dir besser sofort wieder ab.

    P.S.: Weitere kleiner und mittlere Stilfehler:
    sizeof(char) ist per Definition immer 1. Wenn du es Datentyp unabhängig haben möchtest, dann schreib sizeof(*feld) .
    Der Cast des malloc-Rückgabewertes ist in C unnötig und überdeckt allerlei mögliche Fehler, die der Compiler ansonsten finden könnte. Lass es einfach weg.



  • Hallo,
    danke für deine schnelle Antwort.
    Eigentlich sollte in der Funktion eine Datei mit fopen geöffnet werden und der Inhalt in ein Array gepackt werden. Weil der Inhalt der Datei variiert, hab ich mir gedacht denn Speicher dynamisch zuzuweißen.
    Also der Zeiger würde ja erhalten bleiben, nur der String dahinter soll entstehen.
    Oder versteh ich da grad was nicht?

    Wie würdest du das Problem lösen?
    Ich versuche mein main() recht klein zu halten und die Funktionen auszulagern.



  • #include <stdio.h>
    #include <stdlib.h>
    
    void speicher( char **feld)
    {
    	*feld=(char *)calloc(3,sizeof(char));
    	feld[0]='b';
    	feld[1]='i';
    	feld[2]='\0';
    }
    
    main()
    {
    	char *feld=NULL;
    
    	speicher(&feld);
    
    	printf("%s",feld);
    }
    

  • Mod

    aendue schrieb:

    Eigentlich sollte in der Funktion eine Datei mit fopen geöffnet werden und der Inhalt in ein Array gepackt werden. Weil der Inhalt der Datei variiert, hab ich mir gedacht denn Speicher dynamisch zuzuweißen.

    ...

    Wie würdest du das Problem lösen?

    Etwas in dieser Art:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct 
    {
      char *data;
      long length;
    } memory_mapped_file;
    
    memory_mapped_file* create_memory_mapped_file(const char *file_name)
    {
      memory_mapped_file *file = malloc(sizeof(memory_mapped_file));
      if (file)
        {
          FILE *source = fopen(file_name, "r");
          if (source)
            {
              fseek(source, 0, SEEK_END);
              file->length = ftell(source);
              fseek(source, 0, SEEK_SET);
              file->data = malloc(file->length);
              fread(file->data, 1, file->length, source);
              fclose(source);
            }
          else
            {
              file->data = NULL;
              file->length = 0;
            }
        }
      return file;
    }
    
    void print_memory_mapped_file(memory_mapped_file *file)
    {
      fwrite(file->data, 1, file->length, stdout);
    }
    
    void destroy_memory_mapped_file(memory_mapped_file *file)
    {
      if (file)
        free(file->data);
      free(file);
    }
    
    int main()
    {
      memory_mapped_file *file = create_memory_mapped_file("test.c");
      print_memory_mapped_file(file);
      destroy_memory_mapped_file(file);
      return 0;
    }
    

    Das ist natürlich noch sehr, sehr primitiv und vieles kann noch besser gemacht werden, aber folgendes sollte erkennbar sein:
    1. Das Beispiel enthält im Gegensatz zu deinem keine Speicherlöcher.
    2. Es ist völlig egal, wie die Datenstruktur im Inneren aufgebaut ist. Der Nutzer in der main kann nichts falsch machen, solange er sich bloß dran hält, am Anfang die Struktur zu erzeugen und am Ende zu zerstören.
    3. Aus 2. folgt, das man nun auch beliebig an der Implementierung der Datenstruktur Änderungen vornehmen kann, ohne dass die main geändert werden müsste. Dies wäre bei dir nicht der Fall (wenn dein Beispiel richtig gewesen wäre und keinen Speicher lecken würde).



  • Hallo, wie du bereits an meinem Bespiel sicher gemerkt hast, bin ich noch C Anfänger. Dein Code sieht vielversprechend aus. Darauf wäre ich nie gekommen.

    Was hat folgendes zu bedeuten?

    memory_mapped_file* create_memory_mapped_file(const char *file_name)
    

    Du erstellst einen Zeiger auf eine Struktur mit dem Namen "create_memory_mapped_file", aber was hat das in Klammern zu bedeuten?


  • Mod

    aendue schrieb:

    Was hat folgendes zu bedeuten?

    memory_mapped_file* create_memory_mapped_file(const char *file_name)
    

    Du erstellst einen Zeiger auf eine Struktur mit dem Namen "create_memory_mapped_file", aber was hat das in Klammern zu bedeuten?

    Ganz kalt. Das ist der Kopf einer Funktion. 😕 Ich dachte, Funktionen kennst du schon. Du solltest wohl noch etwas an den Grundlagen üben, damit du ein besseres Gefühl für die Syntax bekommst. Von komplizierten malloc-Spielereien (d.h. jegliches malloc, denn es ist eigentlich immer kompliziert) solltest du da erst einmal die Finger lassen. In ein bis zwei Wochen guckst du dir das hier noch einmal an, dann verstehst du es auch (und zwar nicht nur was da steht, sondern auch, warum ich das so gemacht habe) und kannst hinterher selber Programme in diesem Stil schreiben.



  • Funktionen sagen mir schon was, hab bloß nicht nicht viel mit Strukturen gemacht.

    OK Kopf einer Funktion.
    Aber welcher Datentype ist das dann?
    Steht ja kein int oder char davor...
    Oder ist das ein selbst erstellter Datentyp mit typedef?
    Und der Datentyp ist dann "quasi" eine Struktur... 😕



  • ...


  • Mod

    aendue schrieb:

    Oder ist das ein selbst erstellter Datentyp mit typedef?

    Ja. Auch wieder so eine Grundlage, die man erst einmal gesehen haben muss, damit man fremden Code verstehen kann, weil es andauernd vorkommt.

    struct foo
    {
     // irgendwas
    };
    

    Definiert einen neuen Typen namens struct foo . Das ist kein Schreibfehler, das "struct" muss da wirklich hin. Um eine Variable namens bar von diesem Typen zu definieren muss man also schreiben

    struct foo bar;
    

    So geht das mit den structs und eigenen Datentypen in C.

    Viele Leute mögen die Schreibweise mit dem "struct typ" nicht. Hier kommt das typedef ins Spiel. Mit

    typedef typ1 typ2;
    

    kann man bekanntlich typ2 als einen Alias für typ1 bekannt machen. Also auf den obigen Typen struct foo angewandt könnte man schreiben

    typedef struct foo foo_typ;
    

    Und unser bar kann somit mittels

    foo_typ bar;
    

    definieren.
    Insgesamt sieht der Code also derzeit so aus:

    struct foo
    {
     // irgendwas
    };
    typedef struct foo foo_typ;
    
    int main()
    {
     foo_typ bar;
    }
    

    Diese Umbenennung von struct-Typen mittels typedef kommt sehr häufig vor. Das kann man abkürzen, da der Typ beim typedef nicht unbedingt vorher bekannt sein muss, er kann auch im typedef selbst definiert werden:

    typedef struct foo { 
     // irgendwas 
     } foo_typ;
    

    Das definiert einen Typen namens struct foo und einen Alias für diesen namens foo_typ .
    Den umständlichen Typennamen struct foo werden wir sowieso nie benutzen. struct-Definitionen müssen nicht zwangsweise einen Namen haben. Daher kann man den Namen auch weglassen und schreiben:

    typedef struct { 
    // irgendwas  
    } foo_typ;
    

    Dies definiert den Typen foo_typ , der so aussieht wie die struct-Definition.

    Letzteres kommt ungeheuer oft vor, also unbedingt merken. Dies ist auch die Schreibweise, die von mir genutzt wurde.
    Insgesamt:

    typedef struct { 
    // irgendwas 
    } foo_typ;
    
    int main()
    {
     foo_typ bar;
    }
    


  • Danke für die ausführliche Antwort. Das muss ich mir auf jeden Fall verinnerlichen und noch paar Aufgaben dazu machen.

    Ich wusste nicht das man das so abkürzen kann und das struct hat mich auch total verwirrt! Jetzt ist aber mehr Licht im Dunkel.

    Vielen Dank nochmals für die kompetente Antwort!!!


Anmelden zum Antworten