komische Zeichen im String bei Verwendung von memcpy, strcat, strncat, strcpy und strncpy
-
Hallo c-Community,
habe ein Problem mit Strings. Ich erstelle rekursiv ein String mit n-Stellen. Der String wird mit Zeichen aus einem "Zeichensatz" erstellt.
Funktion:
char* CreateString(int Length, char* charSet) { char* tmpString = malloc(sizeof(char)); if (Length == 1) { memcpy(tmpString, charSet, 1); return tmpString; } else { int i = Length-1; memcpy(tmpString, charSet, 1); strncat(tmpString, CreateString(i, charSet), i); return tmpString; } }
Aufruf in main:
for (i = MinLength; i <= MaxLength; i++) { char String[] = ""; strncat(String, CreateString(i, CharSet), i); printf("%s\n", String); }
In der Ausgabe befinden sich aber "komische" Zeichen. Programmcode kompiliert ohne Fehler und Warnungen.
Ausgabebeispiel:
222
222�
22222
222222
2222222
22222222
222222222
2222222222
22222222222
222222222222Wieso erscheinen solche Zeichen in der Ausgabe?
Vieleicht kennt jemand das Phänomen oder sieht den Fehler den ich gemacht habe. Verwendete Bibliotheken sind stdio, stdlib und sting.
MfG
mirrowwinger[Edit1] Habe das Zeichen jetzt mal als int ausgeben lassen, ist eine -1. [/Edit1]
-
Du vergisst einfach die 0-Terminierung. btw. sizeof(char) ist immer 1. Um 1 Zeichen zu kopieren brauchst du kein memcpy. Und dein Code hinterlässt ein riesiges Speicherloch (zu jedem malloc ein free!). Und strncpy ist eine sinnlose Wurstfunktion, die man einfach nicht nehmen sollte.
-
Und dein
char String[] = "";
erzeugt ein char-Array der Länge 1.
Das wird nicht vergrößert wenn du da mit strncat Zeichen dranhängst.
Das Array muss groß genug sein um.
-
Du hast das (einfache) Prinzip der Strings in C nicht verstanden, du hast den Unterschied zwischen Kopieren von Stringinhalten und Definieren von Speicherplatz für Strings nicht verstanden.
Ein String "wächst" nicht dadurch, dass man an sein Ende weitere Zeichen anfügt, bzw. nur solange, wie der zuvor definierte Speicherplatz ausreicht.
Deine Rekursion macht es auch nicht einfacher für dich, diese Grundlagen zu erlernen/begreifen.
-
rüdiger schrieb:
Und strncpy ist eine sinnlose Wurstfunktion, die man einfach nicht nehmen sollte.
Diese Aussage hilft Menschen wie mir nur halb weiter - was mir hier im Forum schon öfters aufgfallen ist... Dass strncpy eine sinnlose Wurstfunktion ist, glaube ich einfach mal, viele von Euch haben deutlich mehr Erfahrung; aber wie sieht eine sinnvolle(re) Alternative aus?
-
Schock schrieb:
aber wie sieht eine sinnvolle(re) Alternative aus?
strmagiccpy
Die Verwendung der Stringfunktionen in C erfordert nunmal Mitdenken, da führt kein Weg dran vorbei. Strncpy ist nicht die "Hirn aus"-Variante von strcpy. Leider wird es aber fast immer so benutzt.
-
Mir fällt kein besonders tragfähiges Argument gegen die Verwendung von memcpy oder strncpy in einem derart übersichtlichen Anwendungsfall ein.
Wenn ich eine fixe Anzahl von Bytes (in allen überhaupt möglichen Fällen für die Anwendung dieser Routine gilt 1 Char == 1 Byte) an eine andere Speicherstelle kopieren möchte, spricht nochmal was genau gegen strncpy oder memcpy? ...
-
h0rst schrieb:
Wenn ich eine fixe Anzahl von Bytes (in allen überhaupt möglichen Fällen für die Anwendung dieser Routine gilt 1 Char == 1 Byte) an eine andere Speicherstelle kopieren möchte, spricht nochmal was genau gegen strncpy oder memcpy? ...
Wenn die "fixe Anzahl" immer 1 ist, tut es auch eine einfache Zuweisung.
-
Hallo und sorry, dass ich mich erst jetzt wieder melden kann.
Hab anscheinend etwas hier los getreten, dass einige Fortgeschrittene doch mit dem Kopf schütteln lässt
.
Also ich bin kein Profi, deswegen habe ich die Frage auch gestellt (dafür ist das Forum ja da, denke ich mal).
Leider kann man mit Kommentaren wie:
Du hast das (einfache) Prinzip der Strings in C nicht verstanden, du hast den Unterschied zwischen Kopieren von Stringinhalten und Definieren von Speicherplatz für Strings nicht verstanden.
Ein String "wächst" nicht dadurch, dass man an sein Ende weitere Zeichen anfügt, bzw. nur solange, wie der zuvor definierte Speicherplatz ausreicht.
Deine Rekursion macht es auch nicht einfacher für dich, diese Grundlagen zu erlernen/begreifen.nicht viel anfangen, denn das ich ein Problem damit habe, zeigt glaube ich mein Post hier im Forum. Also wurde mir und jedem anderen nur das anscheinend offensichtliche NOCHMALS dargestellt. Das für jemand mit 20 Jahren Programmiererfahrung eine "einfacher" String kein Problem ist, setze ich ebenso voraus, denn sonst sollte derjenige nachdenken, was er die 20 Jahre eigentlich getrieben hat. Mir steht dieses umfangreiche Wissen leider nicht zur Verfügung, deswegen probiere ich halt und stelle hier Fragen. Aber trotzdem danke für das Kommentar.
Also ich habe aus Teillösungsvorschlägen versucht wie folgt ein Null-terminierten String MIT ausreichender Größe rekursiv zu erstellen:
main.c
for (i = MinLength; i <= MaxLength; i++) { // String enthält i Charakter + '\0' char* String = malloc(sizeof(char) * (i +1)); strncat(String, CreateString(i, CharSet), i + 1); printf("%s\n", String); }
CreateString.c (Variante 1)
char* CreatePassWord(int Length, char* charSet) { // Ausreichend Speicher bereitstellen für Length Charakter und '\0' char* tmpString = malloc(sizeof(char) * Length + 1); if (Length == 1) { tmpString[0] = charSet[0]; tmpString[1] = '\0'; return tmpString; } else { int i = Length-1; tmpString[0] = charSet[0]; strncat(tmpString, CreatePassWord(i, charSet), i); return tmpString; } }
reateString.c (Variante 1)
char* CreatePassWord(int Length, char* charSet) { // Ausreichend Speicher bereitstellen für Length Charakter und '\0' char* tmpString = malloc(sizeof(char) * Length + 1); if (Length == 1) { strncpy(tmpString, charSet, 1); strcat(tmpString, '\0'); return tmpString; } else { int i = Length-1; strncpy(tmpString, charSet, 1); strncat(tmpString, CreatePassWord(i, charSet), i); return tmpString; } }
Beides führt bei mir zu einer Ausgabe ähnlich meinem Startbeitrag.
Ich hoffe ich habe niemanden mit meiner Antwort verschreckt. Wie man sieht habe ich versucht die konstruktiven Beiträge (außer altrnativen für strncpy, strncat, memcpy) aufzunehmen. Vieleicht hat ja jemand eine ähnliche funktionierende Funktion, die ich mir anschauen kann, um meine Fehler zu verstehen.
Vielen Dank
mirrowwinger
-
char und char-Arrays bzw char-Pointer sind in C komplett verschiedene Sachen.
Z.B.: deinstrcat(tmpString, '\0');
strncat erwartet zwei Zeiger auf char. Dein zweites Argument ist aber ein char. Ein Zeichen.
Und selbst wenn du da "\0" hinschreibst, ist auch das sinnfrei, da da schon ein "" reichtJeder Speicher der mit malloc geholt wird, muss auch wieder mit free freigegeben werden. Nur hast du die Adressen von dem Speicher gar nicht mehr.
Das gibt haufenweise Speicherlecks.Irgendwie kopierst du immer nur das erste Zeichen aus charSet.
Warum machst du das rekursiv? Du hast damit so viel Probleme.
Besorg dir einmal Speicher und befülle den.
-
mirrowwinger schrieb:
for (i = MinLength; i <= MaxLength; i++) { // String enthält i Charakter + '\0' char* String = malloc(sizeof(char) * (i +1)); strncat(String, CreateString(i, CharSet), i + 1); printf("%s\n", String); }
malloc besorgt Speicher, der dann irgendein Inhalt haben kann.
strcat sucht das erste '\0'-Zeichen und kopiert ab der Stelle den Inhalt von dem zweiten Argument. (strncat halt nur eine bestimmte Anzahl).
Je nach dem was in dem malloc-Speicher steht, kann da dein Programm schon abstürzen.strncpy kopiert (im Gegensatz zu strncat) das '\0'-Zeichen nicht mit, wenn die Längenbegrenzung erreicht ist.
Du hast also Speicherlecks und nicht-terminierte Zeichenketten.
-
Schock schrieb:
rüdiger schrieb:
Und strncpy ist eine sinnlose Wurstfunktion, die man einfach nicht nehmen sollte.
Diese Aussage hilft Menschen wie mir nur halb weiter - was mir hier im Forum schon öfters aufgfallen ist... Dass strncpy eine sinnlose Wurstfunktion ist, glaube ich einfach mal, viele von Euch haben deutlich mehr Erfahrung; aber wie sieht eine sinnvolle(re) Alternative aus?
Stimmt. Ich sollte das ein bisschen besser ausführen. strncpy kopiert in den meisten Fällen zu viel oder zu wenig \0en. Wenn die Größe des Zielstrings nicht ausreicht, dann wird der Zielstring nicht(!) nulterminiert! Wenn die Größe mehr als ausreicht, wird der Rest des Zielstrings unnötigerweise mit \0 aufgefüllt. Was die Implementierung unnötig langsam macht.
In C11 gibt es endlich strcpy_s. Das ist die sinnvollere Variante.
-
rüdiger schrieb:
Wenn die Größe mehr als ausreicht, wird der Rest des Zielstrings unnötigerweise mit \0 aufgefüllt. Was die Implementierung unnötig langsam macht.
Das ist wohl das schwächste Argument gegen strncpy.
Wichtiger ist zu wissen, dass bei Speicherbereichsüberlappung das Verhalten undefiniert ist und dass die Verwendung von Funktionen, die zusätzliche Abhängigkeiten schaffen (hier die Eventualität ob im Ergebnis nun ein gültiger (nullterminierter) String vorliegt oder nicht) nur zu Problemen führt und den Code fehleranfällig machen.
-
Wutz schrieb:
rüdiger schrieb:
Wenn die Größe mehr als ausreicht, wird der Rest des Zielstrings unnötigerweise mit \0 aufgefüllt. Was die Implementierung unnötig langsam macht.
Das ist wohl das schwächste Argument gegen strncpy.
Wichtiger ist zu wissen, dass bei Speicherbereichsüberlappung das Verhalten undefiniert ist und dass die Verwendung von Funktionen, die zusätzliche Abhängigkeiten schaffen (hier die Eventualität ob im Ergebnis nun ein gültiger (nullterminierter) String vorliegt oder nicht) nur zu Problemen führt und den Code fehleranfällig machen.Was ich erwähnt habe
-
Danke für die Erläuterungen.
-
Hallo alle miteinander,
also ich habe jetzt einmal das ganze "einfacher" versucht und es sieht sehr gut aus. Habe die Rekursion auf anraten vieler Kommentare jetzt fallen lassen und erstelle den String jetzt direkt über eine for Schleife. Das sieht folgender Maßen aus:
for (i = Min; i <= Max; i++) { char* string = malloc(sizeof(char) * (i + 1)); int j; // für meinen ersten Test habe ich einfach immer nur den 1. Buchstaben des // charSets reingeschrieben. Außerdem war das für die Fragestellung unerheblich for (j = 0; j < i; j++) string[j] = charSet[0]; string[i] = '\0'; printf("string:\t%s\n", string); free(string); }
Dies ergibt auch für Min = 2 und Max = 12 den gewünschten Ausdruck:
22
222
2222
22222
222222
2222222
22222222
222222222
2222222222
22222222222
222222222222Vieleicht könnt ihr ja nochmal drüber schauen, ob noch Fehler vorhanden sind. Ansonsten bedanke ich mich bei allen Mitwirkenden und Kommentatoren für ihre Teilnahme an meinem Problem.
Vielen Dank
mirrowwinger[Edit1] Fehler korrigiert (SeppJ)[/Edit1]
-
- sizeof(char) ist per Definition immer 1.
- Wenn du ganz paranoid bist, könntest du prüfen, ob das malloc überhaupt erfolgreich war.
string[i] ) '\0';
Bitte Code immer per Copy&Paste, nicht abtippen. Sonst finden wir nur Fehler, die gar nicht da sind und übersehen die echten Fehler.
-
Danke SeppJ,
Fehler war wirklich nur Abtippfehler (sitze an 2 Rechnern Linux und Windows). Einer mit Internet und der andere mit Linux halt
-
mirrowwinger schrieb:
Einer mit Internet und der andere mit Linux halt
Komisch, hätte ich genau anders herum erwartet
Und die Linuxkiste hat kein Netzwerk?
-
for (i = Min; i <= Max; i++) { char* string = calloc( 1, i+1 ); /* calloc = malloc + 0-Initialisierung */ int j; // für meinen ersten Test habe ich einfach immer nur den 1. Buchstaben des // charSets reingeschrieben. Außerdem war das für die Fragestellung unerheblich for (j = 0; j < i; j++) string[j] = charSet[0]; /* string[i] = '\0'; kann jetzt entfallen, weil oben calloc schon alle Zeichen mit '\0' initialisiert hat */ printf("string:\t%s\n", string); free(string); }