C Dynamische Speicherverwaltung für einen String

  • Ersteller Ersteller deep90sravioli
  • Erstellt am Erstellt am
D

deep90sravioli

Gast
Hi,

habe zurzeit kein Projekt dazu deswegen auch keinen Code, denke aber meine Frage dürfte trotzdem verständlich sein. Ich würde gerne für einen String ein Array/Zeiger erstellen. Da ich aber zurzeit noch nicht weiß wie groß dieser String sein soll, weiß ich demnach auch nicht wie groß ich mein Array erstellen soll. Da ich es ungerne mit 1024 Zeichen vorbelegen möchte, würde ich gerne malloc anwenden. Ist es an der Stelle möglich, mit Malloc eine größe zu reservieren, die sich quasi "selbst" mit einlesen des Strings erstelle? Z.B. lese ich "Hallo\0" ein, sodass malloc weiß, das es 6 Zeichen reservieren muss?

Nächster Punkt: Ich lese über die Kommandozeile Argumente ein. Ich möchte gerne, das mein erstes eingegebenes Argument (argv[1]) in ein separates Array gespeichert wird. Wäre es an der Stelle möglich, mit

C:
char *first_arg;
first_arg = malloc(strlen(argv[1]));

genau so viel Speicher zu reservieren, wie er für z.B. "hallo"/"hallo\0" gebraucht wird? (Ich weiß nicht ob bei Kommandoargumente ein Nullbyte dabei ist).


Danke schon mal im voraus,

MfG
 
C:
char *first_arg;
first_arg = malloc(strlen(argv[1]+1)); // Plus 1 wegen \0
strcpy(first_arg, argv[1]);

argv[1] enthält auch \0, aber strlen zählt den nicht.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: deep90sravioli
strlen gibt die Länge exkl. '\0' an, daher müsstest du ein Byte zusätzlich reservieren:
Code:
first_arg = (char*)malloc(strlen(argv[1]) + 1)


Für deinen Anwendungsfall bietet sich aber die Funktion strdup an. Diese macht ein Duplikat eines Strings mit '\0' am Ende und reserviert den dafür benötigten Speicher. Wie bei malloc musst du auch hier darauf achten, den Speicher wieder selbst freizugeben:
 
  • Gefällt mir
Reaktionen: deep90sravioli
Zum ersten Punkt:
Für das Einlesen von einem String wirst Du immer einen Puffer mit fester Größe brauchen. Funktionen wie fgets benötigen das einfach. Der Puffer selber kann aber temporär sein. Wenn Du das Einlesen in eine Funktion auslagerst, existiert der Puffer nur während dem Aufruf der Funktion.

Nicht getestet:
C:
char* Lesen()
{
    char puffer[1024];
    fgets (puffer, sizeof(puffer), stdin);
    return (char*)malloc(strnlen(puffer,sizeof(puffer))*sizeof(char));
}
 
  • Gefällt mir
Reaktionen: deep90sravioli
STRDUP(3) Linux Programmer's Manual STRDUP(3)

NAME
strdup, strndup, strdupa, strndupa - duplicate a string

SYNOPSIS
#include <string.h>

char *strdup(const char *s);

char *strndup(const char *s, size_t n);
char *strdupa(const char *s);
char *strndupa(const char *s, size_t n);

DESCRIPTION
The strdup() function returns a pointer to a new string which is a
duplicate of the string s. Memory for the new string is obtained with
malloc(3), and can be freed with free(3).

The strndup() function is similar, but copies at most n bytes. If s is
longer than n, only n bytes are copied, and a terminating null byte
('\0') is added.

strdupa() and strndupa() are similar, but use alloca(3) to allocate the
buffer. They are available only when using the GNU GCC suite, and suf‐
fer from the same limitations described in alloca(3).

RETURN VALUE
On success, the strdup() function returns a pointer to the duplicated
string. It returns NULL if insufficient memory was available, with
errno set to indicate the cause of the error.

ERRORS
ENOMEM Insufficient memory available to allocate duplicate string.
 
  • Gefällt mir
Reaktionen: deep90sravioli und Kanibal
Revan1710 schrieb:
strlen gibt die Länge exkl. '\0' an, daher müsstest du ein Byte zusätzlich reservieren:
Code:
first_arg = (char*)malloc(strlen(argv[1]) + 1)


Für deinen Anwendungsfall bietet sich aber die Funktion strdup an. Diese macht ein Duplikat eines Strings mit '\0' am Ende und reserviert den dafür benötigten Speicher. Wie bei malloc musst du auch hier darauf achten, den Speicher wieder selbst freizugeben:

Muss ich das (char*) vor dem malloc denn casten? Oder ist das "Ansichtssache"? Strdup werd ich mich mal etwas genauer angucken wenn ich wieder dazu komme! ^^

michi.o schrieb:
Zum ersten Punkt:
Für das Einlesen von einem String wirst Du immer einen Puffer mit fester Größe brauchen. Funktionen wie fgets benötigen das einfach. Der Puffer selber kann aber temporär sein. Wenn Du das Einlesen in eine Funktion auslagerst, existiert der Puffer nur während dem Aufruf der Funktion.

Nicht getestet:
C:
char* Lesen()
{
    char puffer[1024];
    fgets (puffer, sizeof(puffer), stdin);
    return (char*)malloc(strnlen(puffer,sizeof(puffer))*sizeof(char));
}

Danke, die Idee mit der Funktion zum einlesen ist mit nicht gekommen (vermutlich da ich mich mit der Speicherverwaltung in C generell noch nicht so gut auskenne). fgets habe ich bis jetzt selbst nur 2-3 mal benutzt, höre aber öfters das es die "bessere" Funktion zum einlesen sei, stimmt das eigentlich?
 
deep90sravioli schrieb:
fgets habe ich bis jetzt selbst nur 2-3 mal benutzt, höre aber öfters das es die "bessere" Funktion zum einlesen sei, stimmt das eigentlich?
Das liegt daran, dass Du da eine maximale Länge vorgeben kannst. Das ist sicherer als zum Beispiel scanf(). Bei scanf wird einfach weiter in den Speicher geschrieben. Angreifer könnten damit theoretisch eigenen Code einschleusen und ausführen.
 
deep90sravioli schrieb:
Muss ich das (char*) vor dem malloc denn casten? Oder ist das "Ansichtssache"?
Hab nochmal nachgelesen - ist wohl tatsächlich so, dass es in C besser ist, nicht nochmal explizit zu casten. Es sei denn du nutzt einen C++ Compiler, dann musst du es casten.
 
Revan1710 schrieb:
Hab nochmal nachgelesen - ist wohl tatsächlich so, dass es in C besser ist, nicht nochmal explizit zu casten. Es sei denn du nutzt einen C++ Compiler, dann musst du es casten.

Compiler müsste der Gnu GCC bei Linux sein, also denke ich mal ein C++ Compiler. Bei meinem Apple ist es der LLVM, aber den finde ich nicht so toll da er sehr oft Code verschluckt..
 
gcc ist ein C-Compiler (GNU C Compiler) - das C++ Pendant ist der g++

Du kannst ja mal dein Programm mit dem g++ bauen, dann müsste er an der Stelle ohne Cast auf die Klappe gehen.
 
Revan1710 schrieb:
Schon länger GNU Compiler Collection ;)

Je nach Dateiendung wird entsprechend kompiliert.
G++ kompiliert alles als c++

@Revan1710 anscheinend - ist aber ziemlich komisch und verwirrend, da ja gcc beides kompiliert.
 
Zuletzt bearbeitet:
Ich dachte GCC steht für GNU Compiler Collection und gcc für GNU C Compiler ? ;)

Genau - also auch wenn er mit dem gcc baut, aber aus main.c eine main.cpp macht, müsste er explizit casten.
 
deep90sravioli schrieb:
Bei meinem Apple ist es der LLVM, aber den finde ich nicht so toll da er sehr oft Code verschluckt.
Kannst du das bitte genauer erklären? clang (das Front-End für LLVM) ist ein absolut brauchbarer Compiler der gcc um nichts nachsteht. Code verschlucken (was auch immer das bedeuten soll) tun beide nicht. Du verwendest ihn wahrscheinlich einfach falsch, oder dein Code ist falsch.

Gruß
BlackMark
 
BlackMark schrieb:
Kannst du das bitte genauer erklären? clang (das Front-End für LLVM) ist ein absolut brauchbarer Compiler der gcc um nichts nachsteht. Code verschlucken (was auch immer das bedeuten soll) tun beide nicht. Du verwendest ihn wahrscheinlich einfach falsch, oder dein Code ist falsch.

Gruß
BlackMark

Hab letztens ein Programm gebaut welches Kommandoargumente entgegen nehmen sollte, in einem Argument einen Substring suchen sollte und dann mit "Check" sagen soll das dieser gefunden wurde und "BEEP" wenn dieser nicht gefunden wurde. Habe das Programm dann kompiliert, ausgeführt, meine 2 Argumente eingegeben und es kam keine Ausgabe. Haben es dann mit der Musterlösung vom Prof verglichen und es war korrekt geschrieben, also per scp auf den Uni-Server, dort nochmal mit gcc kompiliert ohne was am Code zu ändern und es ging einwandfrei. Das war dann auch einer der Gründe wieso ich mir einen Schrott Thinkpad gesucht habe, den repariert hab und jetzt darauf Linux nutze..
 
new Account() schrieb:
clang hat ziemlich komische Bugs, wenns um std::optional geht
Du meinst das hier? Das ist schon gefixed.
Jeder Compiler hat Bugs und neue Features haben tendenziell auch mehr Bugs, das wird mit der Zeit aber alles gefixed. Das ist sicherlich kein valides Argument um zu behaupten, dass gcc besser als clang wäre. Beide Compiler haben Bugs und beide Compiler generieren manchmal sehr guten und manchmal sehr schlechten Code, je nach Input.

@deep90sravioli Du hast mit hoher Wahrscheinlichkeit einfach falschen Code. Zeig uns den Code, dann können wir dir sagen warum das nicht funktioniert hat. Ich tippe darauf, dass das Newline Character im Input Stream nicht behandelt worden ist. Vielleicht als Lösung dafür sogar ein flush auf stdin, was undefined behavior ist und somit einfach falsch. Wenn das in gcc funktioniert und in clang nicht ist das in beiden Fällen richtig, denn bei undefined behavior darf der Compiler alles machen.
Wenn du nicht standardkonformen Code schreibst darfst du dich nicht über den Compiler beschweren, wenn etwas nicht funktioniert. Und die Musterlösung kann auch falsch sein. Zeig uns Code, dann können wir mehr sagen.

Gruß
BlackMark
 
BlackMark schrieb:
Du meinst das hier? Das ist schon gefixed.
Ich weiß nicht, könnte aber sein. Es war jedenfalls noch in diesem Jahr auf einem Apple Laptop vorhanden. Ein Wechsel auf gcc (gar nicht so trivial auf einem Apple Computer) hatte das Problem beseitigt.
BlackMark schrieb:
Das ist sicherlich kein valides Argument um zu behaupten, dass gcc besser als clang wäre.
Schlussfolgerungen kann jeder selbst ziehen - mit gcc oder mvsc hatte ich noch nie Probleme. Bei Clang hat gleich ein komplettes C++ Feature nicht funktioniert.
Für mich ein +-Punkt für gcc ;)
 
new Account() schrieb:
mit gcc oder mvsc hatte ich noch nie Probleme. Bei Clang hat gleich ein komplettes C++ Feature nicht funktioniert.
Für mich ein +-Punkt für gcc
Ich hatte mit allen drei schon Probleme. Vor allem bei der Einführung von C++11 gingen extrem viele neue Features nicht bzw. nicht richtig. Zum Beispiel User-defined literals sind erst mit VS 2015 vom msvc unterstützt worden, während gcc und clang das schon seit 2012 können. Welcher Compiler welches Feature seit wann kann, kann man hier nachlesen.
gcc und clang sind was performanten Code angeht auch oft dem msvc überlegen.
Jeder Compiler hat Vor- und Nachteile. Es gibt keinen besten Compiler. Je nach Feature oder Benchmark ist zwar immer ein Compiler der beste, aber eben nicht immer der gleiche.

Prinzipiell sollte man Code einfach standardkonform schreiben, dann kann man jeden Compiler verwenden. Der Compiler den man zum Entwickeln verwendet muss ja auch nicht der gleiche sein mit dem dann deployed wird.

Ich verwende clang zum Beispiel um bessere Fehlermeldungen zu bekommen. msvc liefert einfach unlesbare Fehlermeldungen bei Template Fehlern.

Gruß
BlackMark
 
Zuletzt bearbeitet:
BlackMark schrieb:
@deep90sravioli Du hast mit hoher Wahrscheinlichkeit einfach falschen Code. Zeig uns den Code, dann können wir dir sagen warum das nicht funktioniert hat. Ich tippe darauf, dass das Newline Character im Input Stream nicht behandelt worden ist. Vielleicht als Lösung dafür sogar ein flush auf stdin, was undefined behavior ist und somit einfach falsch. Wenn das in gcc funktioniert und in clang nicht ist das in beiden Fällen richtig, denn bei undefined behavior darf der Compiler alles machen.
Wenn du nicht standardkonformen Code schreibst darfst du dich nicht über den Compiler beschweren, wenn etwas nicht funktioniert. Und die Musterlösung kann auch falsch sein. Zeig uns Code, dann können wir mehr sagen.

Gruß
BlackMark

Code kann ich leider keinen mehr Vorlegen, den hab ich vergessen beim umbetten von Debian auf Ubuntu zu speichern. Was ich aber machen könnte ist mir die Musterlösung vom Prof holen und mit dieser auf meinem Macbook das Programm kompilieren und ausführen, ich werde den Beitrag editieren falls es geklappt hat oder nicht.
 
Zurück
Oben