[C]Einfache Nutzung von malloc()

Hallo zusammen!
Mir scheint, es ist mir heute nicht vergönnt eigenständig zu lernen, denn ich verstehe nicht, wie man malloc() verwendet. Das, was bei mir angekommen ist, ist Folgendes:
malloc(size) reserviert Speicher der Größe size und liefert die Adresse des ersten Bytes.
Soweit hoffentlich richtig. Nur warum bekomme ich folgenden Fehler:
Code:
amshaegar@amshaegar-desktop:~/programming$ gcc -Wall malloc.c -o malloc
malloc.c:8:13: Warnung: Zeichenkonstante zu lang für ihren Typ
malloc.c: In Funktion ?main?:
malloc.c:8: Warnung: Überlauf in impliziter Konstantenkonvertierung
Programm malloc.c
Code:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  char *string;
  string = (char *) malloc(sizeof(char)*15);
  *string = 'Hallo Welt!';
  
  printf("Ausgabe: %s\n", string);
  
  return 0;
}
Ausgabe:
Code:
amshaegar@amshaegar-desktop:~/programming$ ./malloc 
Ausgabe: !
 
Wenn man einem String direkt etwas zuweist, braucht man kein malloc (string1). Wenn da aber was hinkopiert werden soll (das ist ja nur ein Zeiger auf einen Punkt im Speicher), muss zuerst mit malloc() Speicher alloziiert werden (string2).

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

int
main(void)
{
    char           *string1 = "Hallo Welt!";
    char           *string2 = (char*) malloc(strlen(string1));

    strcpy(string2, string1);

    printf("String 1:  %s\n"\
           "String 2:  %s\n", string1, string2);

    return 0;
}
ed: Folgendes ist evtl. vewirrend dabei...
Code:
char *string;
string = "Hallo";
Hier wird keinesfalls "Hallo" in 'string' kopiert, sondern der Wert von string (ein Integer, weils eine Adresse ist!) wird auf die Adresse des ersten Buchstabens von "Hallo" gesetzt.

Wenn du in C den Inhalt eines Zeigers kopieren willst, musst du alo immer strcpy(), memcpy() oder ähnliches benutzen.
 
Wenn du in C den Inhalt eines Zeigers kopieren willst, musst du alo immer strcpy(), memcpy() oder ähnliches benutzen.
Danke, gut zu wissen. In dem Tutorial, was ich gerade mache wurde davon bisher nichts erwähnt,

Folgendes funktioniert also, hab's getestet;
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
  char *string;
  string = (char *) malloc(sizeof(char)*15);
  strcpy(string, "Hallo Welt!");
  
  printf("Ausgabe: %s\n", string);
  
  return 0;
}
Herzlichen Dank!

Nun habe ich aber eine weitere Frage; malloc() wurde in besagtem Tutorial beschrieben als Funktion, die dazu dient dynamisch Speicher zu reservieren. Alle Beispiele sehen allerdings etwa so aus, wie mein kleines Programm: Es wird Speicher von der Größe sizeof(typ) * x reserviert, wobei x in meinem Beispiel 15 und typ char wäre. Wo ist denn das dynamisch? Das ist doch das gleiche, als schriebe ich Folgendes: char string[15] ( => typ var[x] )
Was bringt mir malloc() ?
 
Manchmal weißt du aber vorher nicht wie lang ein Datensatz ist, und dann kannst du mit malloc() zur Laufzeit den passenden Speicher für die Variable reservieren.
Gut, was dynamische Arrays sind und wie man sie erzeugt und verwendet ist mir klar. Was ich meine ist, wie sich das in Bezug auf ein char-Array, also für Strings, umsetzen lässt. Im Kapitel 16.7 wird zuerst abgefragt, wie groß das Array sein soll, aber ich kann von einem Benutzer, der einen Text eingeben soll wohl kaum verlangen, dass er vorher die Zeichen zählt oder? Und genau das ist das, was sich mir nicht erschließen will. Mein Programm hab ich jetzt dahingehen abgeändert, dass ich nicht die fixe Größe 15*sizeof(char), sondern sizeof("Hallo Welt!") verwende. Bevor ich allerdings eine Benutzereingabe auf ihre Größe untersuchen kann muss ich sie speichern und das ginge dann ja nur über ein char-Array mit der maximalen größe der Texteingabe, was ich ja aber gerade verhindern will mit malloc(), da dies ja Ressourcen verschwendet.

Btw: Wie lösen andere Programmiersprachen das? Wird da da die Maximalgröße reserviert?

P.S.@quux: Ob du's glaubst oder nicht, das hab ich sogar schon gelesen(d.h. überflogen)
 
Wenn du dynamisch lesen willst, musst du leider selbst Hand anlegen. D.h., immer nur ein Zeichen lesen und dann den Puffer vergrößern.

Was auch gehen würde, wären Konstruktoinen über scanf(), wobei ich das aber ungern mache, weil nie klar ist, was dabei passiert. Du kannst z.B. sowas machen:
Code:
char *d = malloc(1);
scanf("%s", d);
Da es funktioniert, würde ich sagen, dass scanf() den Puffer selbst vergrößert... Ich werde aber mal sehen, wie bei mir scanf() implementiert ist, du hast mich da auf was gebracht :D

Was andere Programmiersprachen angeht, kommt es auf die verwendete API an, d.h. die I/O Funktionen, die dir zur Verfügung stehen. Als krasse Beispiele mal LISP und Fortran:
Code:
(setq line (read-line))
> Bla bla bla.
(write-line line)
Bla bla bla.
LISP: Alles ist dynamisch, es gibt keine Typen. Werte können beliebig groß werden, ohne dass man sich um Speicherverwaltung kümmern müsste.
Code:
       PROGRAM READ
       CHARACTER*3 STR
       READ *,STR
       PRINT *,STR
       STOP
       END
Fortran: Das Programm liest drei Zeichen, der Rest verschwindet im Nichts. Da ist nichts dynamisch, also macht man den String "groß genug" (Fortranexperten, korrigiert mich, ich rate).

Siehe auch: Why Pascal is not my favorite programming language (da wird ganz nett beschrieben, wie das früher in Pascal gemacht wurde :)
 
Wenn du dynamisch lesen willst, musst du leider selbst Hand anlegen. D.h., immer nur ein Zeichen lesen und dann den Puffer vergrößern.
Was auch gehen würde, wären Konstruktoinen über scanf(), wobei ich das aber ungern mache, weil nie klar ist, was dabei passiert. Du kannst z.B. sowas machen:
Code:
char *d = malloc(1);
scanf("%s", d);
Da es funktioniert, würde ich sagen, dass scanf() den Puffer selbst vergrößert...
Da würde ich dir widersprechen: Da malloc() nur die Adresse auf das erste Byte des gefundenen zusammenhängenden Speicherbereichs liefert und ich davon ausgehe, das Scanf() diesen nur beschreibt, würde ich vermuten, dass beim einlesen einfach alle folgenden Bytes überschrieben werden, falls die Eingabe größer ist als der reservierte Speicher(in diesem Fall nur 1 Byte), was zu fatalen Abstürzen führen kann!

Genau Pascal war das, was ich bei meinem letzten Post im Hinterkopf hatte, weil das neben PHP momentan mein Hauptarbeitsgebiet darstellt.
 
Original von thyrael.lu
Was auch gehen würde, wären Konstruktoinen über scanf(), wobei ich das aber ungern mache, weil nie klar ist, was dabei passiert. Du kannst z.B. sowas machen:
Code:
char *d = malloc(1);
scanf("%s", d);
Da es funktioniert, würde ich sagen, dass scanf() den Puffer selbst vergrößert... Ich werde aber mal sehen, wie bei mir scanf() implementiert ist, du hast mich da auf was gebracht :D
So entstehen Heapoverflows ;).Standardmäßig ist scanf gegen Overflows nicht gesichert.

Was "Lösungen" angeht: da gibt es doch einige. Z.B in dem man einen Lesepuffer definiert -mit einer festen Länge. Man liest nun Zeichen für Zeichen ein und zählt fleißig die Länge mit. Anschließend reserviert man den nötigen Speicher und kopiert den String hinein.
Wenn der Buffer unzureichend groß ist, vergrößert man diesen.

Bewährt hat sich dabei die folgende Strategie (diese habe ich sowohl in C++ Vector implementierung wie auch bei Java Hashtables/Vectoren gesehen): man reserviert immer das doppelte und kopiert die Daten hinein. Ist der Buffer z.B am Anfang 512 Byte groß und die Eingabe droht über 512 Bytes zu gehen, so reserviert man nicht etwa 1 Byte dazu (was unsinnig wäre, weil bei der Heapverwaltung dabei so um 12 Bytes "flöten" gehen) sondern 1024, kopiert den Inhalt hinein und gibt den alten Speicher frei.
In den "üblichen" malloc Implementierungen (zumindest in der Windowswelt) wird ein malloc(1) 12 bis 16 Bytes reservieren (unter XP Sp2, die Microsoft C runtime DLLs).
 
Code:
amshaegar@amshaegar-desktop:~/programming$ ./malloc 
Ausgabe1: Hallo Welt!
BufferOverflow123456789   
Ausgabe2: BufferOverflow123456789
Ausgabe3(13): 909456435

amshaegar@amshaegar-desktop:~/programming$ ./malloc 
Ausgabe1: Hallo Welt!
A       
Ausgabe2: A
Ausgabe3(13): 13
Ich glaube man sieht deutlich, dass Scanf() nicht abgesichert ist, ja. Danke,

CDW! Dann bin ich ja beruhigt, dass es doch Möglichkeiten gibt und ich kann in Ruhe weiter lernen.
Bis zum Nächsten Problem ^^
 
Zurück
Oben