- in sizeof den * löschen, und +1 bei laenge in malloc schreiben
- ein free fehlt
- du könntest in dem Beispiel str weglassen, ob Du 'ein' weglassen
kannst hängt davon ab was Du wirklich vorhast (vermutlich nicht)
Falls du auf einem Posix-kompatiblen System arbeitest, kannst du dir
auch mal getline() ansehen. Das ist (optional) in der Lage, den
Speicher selbst passend zu allozieren.
Du brauchst auf jeden Fall einen Puffer, um mit fgets etwas einzulesen.
'ein' ist in diesem Fall der Puffer.
Du könntest auch mit str=malloc(200) vorher einen Puffer anlegen, in den
eingelesen wird, und dann mit str=realloc(str,strlen(str)+1) auf den
wirklich benötigten Speicher kürzen.
Das malloc(sizeof(char*)*laenge)ist übrigens Unsinn, da du nicht Länge
mal Zeiger auf Char brauchst, sondern Länge+1. sizeof(char) ist 1,
deshalb kannst du es weglassen, und 1 Byte brauchst du noch für das
Nullbyte am Ende.
Daniel S. schrieb:> ich will einen String-Array mit einer dynamischen Speicher verwalten
Was du bisher hast, ist aber kein Stringarray, sondern ein String bzw
ein Char-array. Ein Stringarray ist ein Array von Strings, also von
Zeigern auf char.
Falls du das vorhast, ist ein lokaler Puffer wie 'ein' doch günstiger.
Das Kopieren, strlen und malloc lässt sich durch strdup ersetzen und
damit vereinfachen: str_array[i]=strdup(ein);
Jobst Q. schrieb:> Du brauchst auf jeden Fall einen Puffer, um mit fgets etwas einzulesen.> 'ein' ist in diesem Fall der Puffer.>> Du könntest auch mit str=malloc(200) vorher einen Puffer anlegen, in den> eingelesen wird, und dann mit str=realloc(str,strlen(str)+1) auf den> wirklich benötigten Speicher kürzen.>> Das malloc(sizeof(char*)*laenge)ist übrigens Unsinn, da du nicht Länge> mal Zeiger auf Char brauchst, sondern Länge+1. sizeof(char) ist 1,> deshalb kannst du es weglassen, und 1 Byte brauchst du noch für das> Nullbyte am Ende.
Okay und gibt es zufällig noch eine bessere Möglichkeit als einen string
zu verschieben als:
1
int main()
2
{
3
int i;
4
char *str;
5
str=malloc(200);
6
fgets(str, 200, stdin);
7
str=realloc(str,strlen(str)+1);
8
9
for(i=0 ;i<20;i++){ // Nur Ein Beispiel mit dem Index 5!
Du kannst dir in einem anderen Pointer den Anfang merken.
str brauchst du weiterhin für das free.
char *str5 = str+5;
oder du nimmst die Standardfunktion memmove.
Die kommt auch mit überlappenden Bereiche klar.
Daniel S. schrieb:> str=realloc(str,strlen(str)+1);
So sollte man realloc übrigens nie aufrufen.
Wenn nicht mehr genügend Speicher verfügbar ist, liefert realloc NULL,
ohne den Speicher zu verändern, auf den das erste Argument zeigt.
Weist man den Rückgabewert von realloc aber dem Pointer zu, der auf
den alten Speicherblock zeigt, ist damit der alte Speicherblock in den
ewigen Datengründen verschwunden. Man kann den Speicherblock nicht mehr
freigeben, und man kann nicht mehr auf die enthaltenen Daten zugreifen.
Daher:
Das Ergbnis von realloc immer einem (temporären) Hilfspointer
zuweisen.
for(i=0 ;i<20;i++){ // Nur Ein Beispiel mit dem Index 5!
str[5+i]=str[5+i+1];
}
Das darfst du nur machen, wenn die Länge des Strings > (20+5+1) ist,
weil ja nur die Länge des Strings reserviert ist.
Mit dem lokalen Puffer ein[200] wär das kein Problem. Wenn du das
realloc weglässt, auch nicht, dann wären ja auch 200 Bytes reserviert.
Wenn du nur nach vorne schieben willst, geht dasselbe auch mit strncpy.
strncpy(str+5,str+6,20);
Wenn der ganze Reststring (incl \0)nach vorn verschoben werden soll:
strcpy(str+5,str+6);
Um zu Deiner Ausgangsfrage zurückzukomnen: falls man die Summe aller
Stringlängeb in voraus kennt (z.B. durch Dateigrösse) reicht 1 malloc
aus. Und dann wird mit fgets in diesen Speicher eine Zeile eingelesen,
und ein neuer (zusätzlicher) Pointer auf das Ende der Zeile erzeugt, bis
der allokierte Speicher voll und die Datei vollständig eingelesen ist.
Dumdi D. schrieb:> Um zu Deiner Ausgangsfrage zurückzukomnen: falls man die Summe aller> Stringlängeb in voraus kennt (z.B. durch Dateigrösse) reicht 1 malloc> aus. Und dann wird mit fgets in diesen Speicher eine Zeile eingelesen,> und ein neuer (zusätzlicher) Pointer auf das Ende der Zeile erzeugt, bis> der allokierte Speicher voll und die Datei vollständig eingelesen ist.
Okay perfekt das werde ich gleich mal probieren!
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
5
int main()
6
{
7
int laenge,i;
8
char *str;
9
str=malloc(200);
10
fgets(str, 200, stdin);
11
str=realloc(str,strlen(str)+1);
12
laenge=strlen(str);
13
for(i=0;i<laenge;i++){ //Verschieben des strings, wenn str[i] kein Buchstabe ist
Wieso verlässt er die while Schleife nicht? Eigentlich müsste er doch so
lange den String verschieben bis ein Buchstabe kommt und dann müsste die
while-schleife != 0 sein. Oder übersehe ich da was?
Das kommt auf die Richtung an. Wenn das Ziel hinter der Quelle ist, geht
es natürlich nicht, da die Quelle teilweise überschrieben wird, bevor
sie gelesen wird.
Aber strcpy müsste schon ziemlich verrückt und umständlich implementiert
sein, wenn man damit nicht von hinten nach vorne kopieren könnte.
Bei memmove wird eben vorher die Richtung ermittelt, in der kopiert
werden darf.
Dumdi D. schrieb:> Um zu Deiner Ausgangsfrage zurückzukomnen: falls man die Summe aller> Stringlängeb in voraus kennt (z.B. durch Dateigrösse) reicht 1 malloc> aus. Und dann wird mit fgets in diesen Speicher eine Zeile eingelesen,> und ein neuer (zusätzlicher) Pointer auf das Ende der Zeile erzeugt, bis> der allokierte Speicher voll und die Datei vollständig eingelesen ist.
Wenn man die Gesamtgröße kennt reicht zwar 1 malloc, aber das Lesen mit
fgets ist dann eher kontraproduktiv. Besser man liest mit fread die
komplette Datei in einem Rutsch in den Buffer und sucht anschließend mit
strchr nach den Zeilenenden. Das ist deutlich schneller.
Daniel S. schrieb:> #include <stdio.h>> #include <stdlib.h>> #include <string.h>>> int main()> {> int laenge,i;> char *str;> str=malloc(200);> fgets(str, 200, stdin);> str=realloc(str,strlen(str)+1);> laenge=strlen(str);> for(i=0;i<laenge;i++){ //Verschieben des strings, wenn str[i] kein> Buchstabe ist> while(!((str[i]>='a' && str[i]<='z') || (str[i]>='A' &&> str[i]<='Z'))){> strcpy(str+i,str+i+1);> }> }> printf("%s",str);> }>> Wieso verlässt er die while Schleife nicht? Eigentlich müsste er doch so> lange den String verschieben bis ein Buchstabe kommt und dann müsste die> while-schleife != 0 sein. Oder übersehe ich da was?
Für jedes Zeichen, daß kein Buchstabe ist immer den kompletten String um
ein Zeichen zu verschieben ist ziemlich ungünstig. Überleg mal was
passiert, wenn am Anfang erstmal 50 Ziffern kommen :(.
Und da durch die Verschieberei der benutzte Teil des Buffers immer
kleiner wird, wäre es auch besser das realloc erst danach zu machen.
Und irgendwann den allokierten Speicher wieder freizugeben ist auch
keine schlechte Idee.
Jobst Q. schrieb:> Aber strcpy müsste schon ziemlich verrückt und umständlich implementiert> sein, wenn man damit nicht von hinten nach vorne kopieren könnte.
Du hast aber keinen Einfluß darauf, wie es implementiert ist.
Und es ist nicht vorgesehen, dass sich die Bereiche überlappen.
Es kann jetzt gut gehen, aber mit einem anderen System/Compiler/Version
muss es nicht mehr funktionieren.
Dirk B. schrieb:> Es kann jetzt gut gehen, aber mit einem anderen System/Compiler/Version> muss es nicht mehr funktionieren.
Schlimmer noch: keiner sagt, dass der Compiler überhaupt eine
separat implementierte Funktion benutzen muss. Der Compiler könnte
genausogut das undefinierte Verhalten erkennen und dann irgendwas
tun (beispielsweise auch gar nichts).
Jörg W. schrieb:> Schlimmer noch: keiner sagt, dass der Compiler überhaupt eine> separat implementierte Funktion benutzen muss. Der Compiler könnte> genausogut das undefinierte Verhalten erkennen und dann irgendwas> tun (beispielsweise auch gar nichts).
Woran sollte er das erkennen? In der string.h, aus der er seine
Informationen über strcpy bezieht, steht nichts davon.
Jobst Q. schrieb:> Woran sollte er das erkennen?
Am Namen der Funktion, das genügt.
Im hosted environment sind die Namen der Standardbibliothek
ausschließlich für die im Standard beschriebene Funktionalität
zu benutzen (reserved identifier), eben damit der Compiler dort
Optimierungspotenzial hat. Auf diese Weise darf er beispielsweise
1
sizeof("foo")
durch die Konstante 4 bereits zur Compilezeit ersetzen.
Jobst Q. schrieb:> Jörg W. schrieb:>> Schlimmer noch: keiner sagt, dass der Compiler überhaupt eine>> separat implementierte Funktion benutzen muss. Der Compiler könnte>> genausogut das undefinierte Verhalten erkennen und dann irgendwas>> tun (beispielsweise auch gar nichts).>> Woran sollte er das erkennen? In der string.h, aus der er seine> Informationen über strcpy bezieht, steht nichts davon.
Hat Jörg doch geschrieben: Moderne Compiler nutzen Funktionen wie strcpy
nicht unbedingt als klassische Bibliotheksfunktion, sondern
implementieren sie selber, direkt im Compiler.
Hier mal eine Liste der Funktionen, die GCC bereits eingebaut hat:
https://gcc.gnu.org/onlinedocs/gcc-6.3.0/gcc/Other-Builtins.html#Other-Builtins
In dieser recht großen Liste ist auch strcpy enthalten.
Jörg W. schrieb:> Auf diese Weise darf er beispielsweise> sizeof("foo")> durch die Konstante 4 bereits zur Compilezeit ersetzen.
Schlechtes Beispiel, da sizeof ein Operator ist und keine
Bibliotheks-Funktion. Besser:
Er kann
1
strlen("foo");
zur Compliezeit durch 3 ersetzen, statt zur Laufzeit die Funktion
aufzurufen.
Rolf M. schrieb:> Besser:
Ja, sorry, genau das war meine Intention. ;-)
Klar, sizeof kann er immer ersetzen, strlen() nur im hosted mode.
Rolf M. schrieb:> Moderne Compiler nutzen Funktionen wie strcpy nicht unbedingt als> klassische Bibliotheksfunktion, sondern implementieren sie selber,> direkt im Compiler.
Genau daher finde ich es gar nicht so völlig abwegig, dass der
Compiler die Funktion bei erkanntem undefiniertem Verhalten eben
auch komplett wegwirft. Für solch eine Optimierung muss er
zwangsläufig in gewisser Weise die Argumente analysieren, und bevor
man dann in einen internal compiler error rennt bei undefiniertem
Verhalten, sollte man es halt besser erkennen und reagieren.
Fair wäre es natürlich, dann auch 'ne Warnung zu werfen ;), aber
zu einer Warnung ist er nicht verpflichtet.
guest schrieb:> Für jedes Zeichen, daß kein Buchstabe ist immer den kompletten String um> ein Zeichen zu verschieben ist ziemlich ungünstig. Überleg mal was> passiert, wenn am Anfang erstmal 50 Ziffern kommen :(.
Die Verschieberei ist sowieso ineffizient. Besser ist es gleich nur die
erwünschten Zeichen zu kopieren. Durch Rückgabe des Stringendes erspart
man sich das strlen.
Rolf M. schrieb:> Hat Jörg doch geschrieben: Moderne Compiler nutzen Funktionen wie strcpy> nicht unbedingt als klassische Bibliotheksfunktion, sondern> implementieren sie selber, direkt im Compiler.
Ein Grund mehr, sie zu vermeiden und lieber selbstgeschriebene
Funktionen zu verwenden.
Noch was: m.M.n. muss man bei fgets auf Fehler prüfen (Rückgabewert mit
NULL vergleichen), da im Fehlerfall m.M.n. nicht garantiert ist, dass
'buf' mit einer '\0' ended. Das wäre dann mit jedem strcpy (strlen)
fatal. Sollte dieser Bug irgendwann einmal auftreten, würde man ihn auch
vermutlich nie finden, da vermutlich nicht reproduzierbar.
Jobst Q. schrieb:> Rolf M. schrieb:>> Hat Jörg doch geschrieben: Moderne Compiler nutzen Funktionen wie strcpy>> nicht unbedingt als klassische Bibliotheksfunktion, sondern>> implementieren sie selber, direkt im Compiler.>> Ein Grund mehr, sie zu vermeiden und lieber selbstgeschriebene> Funktionen zu verwenden.
Genau das Gegenteil ist der Fall. Nutzt du selbstgeschriebene
Funktionen, nimmst du dem Compiler Möglichkeiten der Optimierung.
Aber bitte: Wenn du meinst, es selber besser zu können als ein Team von
Compiler-Entwicklern, dann mach dir halt die zusätzliche Arbeit.
Dumdi D. schrieb:> Noch was: m.M.n. muss man bei fgets auf Fehler prüfen (Rückgabewert mit> NULL vergleichen), da im Fehlerfall m.M.n. nicht garantiert ist, dass> 'buf' mit einer '\0' ended.
Nicht nur im Fehlerfall, sondern auch bei einem EOF.
Rolf M. schrieb:>> Ein Grund mehr, sie zu vermeiden und lieber selbstgeschriebene>> Funktionen zu verwenden.>> Genau das Gegenteil ist der Fall. Nutzt du selbstgeschriebene> Funktionen, nimmst du dem Compiler Möglichkeiten der Optimierung.> Aber bitte: Wenn du meinst, es selber besser zu können als ein Team von> Compiler-Entwicklern, dann mach dir halt die zusätzliche Arbeit.
Meine Funktionen kann der Compiler optimieren wie alles andere im
Quelltext.
Er kann sie nur nicht durch irgendetwas anderes ersetzen, bloss weil er
den Namen kennt.
Die Arbeit, eine Funktion zu schreiben, mach ich mir nur einmal und kann
sie tausendfach einsetzen. Wobei ich genau weiß, was die Funktion macht
und nicht evt veraltete Dokumentationen wälzen muss.
Sich blind auf den Namen und vielleicht noch eine Ein-Satz-Beschreibung
zu verlassen, kann manchmal ganz schön daneben gehen.
Wem ist denn zB bewusst, dass strncpy eben nicht wie strcpy immer einen
gültigen String liefert, sondern im Begrenzungsfall nur ein Char-Array,
dem die Null fehlt?
Da schreib ich mir lieber meine eigene Funktion, die nebenbei auch das
Stringende zurückgibt statt dem eh schon bekannten Stringanfang:
1
char*stpcpylim(char*t,constchar*s,constchar*lim){
2
3
lim--;
4
while(*s&&t<lim)*t++=*s++;
5
*t=0;
6
returnt;
7
}
lim ist ein Zeiger auf das erste Byte, dass nicht beschrieben werden
darf. Damit kann man auch mehrere Strings hintereinander kopieren.
Jobst Q. schrieb:> Wem ist denn zB bewusst, dass strncpy eben nicht wie strcpy immer einen> gültigen String liefert, sondern im Begrenzungsfall nur ein Char-Array,> dem die Null fehlt?
Jedem, der die Dokumentation dazu liest.
Wer sie nicht liest, ist natürlich selbst schuld, dem ist aber auch
sonst nicht zu helfen.
Der Vorteil deiner selbst geschriebenen Funktionen: du machst dann
deine eigenen Bugs rein …
Jörg W. schrieb:> Der Vorteil deiner selbst geschriebenen Funktionen: du machst dann> deine eigenen Bugs rein …
Diese Funktionen sind durchaus noch überschaubar. Eventuelle Bugs wären
auch schnell zu finden.
Das Problem bei strncpy ist ja gerade, dass es kein Bug ist. Ein Bug
wäre ja wohl korrigiert worden. Es ist ein Fehldesign, dass man bei
jedem Einsatz durch Code drumherum ausbügeln muss.
Jobst Q. schrieb:> Es ist ein Fehldesign, dass man bei jedem Einsatz durch Code drumherum> ausbügeln muss.
Kannst du aber ganz einfach ausbügeln, indem du die Anzahl eins
kleiner als den Puffer übergibst und ans Ende des Puffers manuell
eine 0 setzt. Das genügt ja einmalig (bei globalen oder statischen
Variablen ohnehin automatisch), da diese dann nie wieder überschrieben
wird.
Nerviger finde ich, dass es den kompletten Rest des Puffers immer
mit Nullen füllt. Wozu sollte das denn gut sein?
Wenn du partout keine der Compiler-Optimierungen bezüglich der
Bibliothek haben willst, kannst du ja auch im freestanding mode
compilieren. Ich jedenfalls finde die Optimierungen sinnvoll
und benutze daher den hosted mode selbst auf Controllern (für die
ja eigentlich freestanding konzipiert ist).