char[], String und "End"-Zeichen
-
Guten Tag,
inspiriert von diesem Thema https://www.c-plusplus.net/forum/262844
möchte ich fragen ob, wann und wie man sich um das "End"-Zeichen und die Größe/Länge einer Zeichenkette in C und C++ kümmern muss, ein Beispiel wäre mal sehr praktisch.Viele Grüße!
Edit: Ich ha hier Folgendes:
void combi(char* ca, int* len, int idx) { if (*len == idx) { return; } for (char c = 'A'; c <= 'Z'; c++) { ca[idx] = c; std::cout << ca << std::endl; char * neu = (char*) malloc(sizeof(char) * (*len)); strcpy(neu, ca); combi(neu, len, idx + 1); free(neu); } } /* oder */ void combi(char* ca, int* len, int idx) { if (*len == idx) { return; } for (char c = 'A'; c <= 'Z'; c++) { ca[idx] = c; std::cout << ca << std::endl; combi(ca, len, idx + 1); } } /* ) und */ int main(int cnt, char** args) { for (int i = 0; i < cnt; i++) { std::cout << args[i] << " "; } std::cout << std::endl; int len = 2; char ca[2 + 1]; combi(ca, &len, 0); return 0; }
-
Am besten nicht so wie du es da machst
C-Strings sind einfach nur ein durchgehender Speicherblock von chars (bzw andere typen zum speichern von zeichencodes wie char32_t etc). Da man diese in Form eines Zeigers auf das erste Zeichen handhabt, muss man natürlich noch wissen wieviele Zeichen denn dort gespeichert sind, zb indem man als das letzte zeichen einen besonders code verwendet, zb 0. Die meisten Funktionen zum behandeln von C-strings fügen automatisch diesen Marker an, vorausgesetzt der zielpuffer hat noch platz für diesen. strcat und konsorten fügen es dann nicht mehr an, da solltest du aber nochmal in die doku gucken. Aufjedenfall sollte das Endezeichen immer da sein. Unter C++ hast du entsprechende Container wie string, da kommst du mit solchen bedingungen eher selten in Berührung.
-
"Kümmern muss" - die Frage ist ja sehr präzise ...
-
manni66 schrieb:
"Kümmern muss" - die Frage ist ja sehr präzise ...
Dann substituiere halt durch behandle oder best/common practise... Stelle dich bloß nicht zu ... an.
Danke für alle bisherigen Antworten.
-
roflo schrieb:
Unter C++ hast du entsprechende Container wie string
#include <string> int main () { std::string str = "Hello World!"; std::cout << str << "\n"; }
Ich dachte das sollte nochmal hervorgehoben werden!.
-
Ok, mal was Konkretes, wenn ich alle Kombinationen der Länge 2 "bilden" möchte, muss ich dann schreiben
char ca[2 + 1];
und als letztes Zeichen '\0' "einfügen"?
-
So ist es.
-
//============================================================================ // Name : a.cpp // Author : ich // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <stdlib.h> #include <string.h> #include <iostream> #define LENN 2 void combi(char* ca, const int len, int idx) { if (len == idx) { return; } for (char c = 'A'; c <= 'Z'; c++) { ca[idx] = c; std::cout << ca << std::endl; combi(ca, len, idx + 1); } } int main(int count, char** args) { int x; x = 5; std::cout << x << std::endl; std::cout << "Hello World..." << std::endl; for (int i = 0; i < count; i++) { std::cout << args[i] << " "; } std::cout << std::endl; const int len = LENN; char ca[len + 1]; ca[len] = '\0'; combi(ca, len, 0); return 0; }
gib mir aber aus (achtung, lange ausgabe):
5 Hello World... D:\workspace\a\Debug\a.exe A, AA AB AC AD AE AF AG AH AI AJ AK AL AM AN AO AP AQ AR AS AT AU AV AW AX AY AZ BZ BA BB BC BD BE BF BG BH BI BJ BK BL BM BN BO BP BQ BR BS BT BU BV BW BX BY BZ CZ CA CB CC CD CE CF CG CH CI CJ CK CL CM CN CO CP CQ CR CS CT CU CV CW CX CY CZ DZ DA DB DC DD DE DF DG DH DI DJ DK DL DM DN DO DP DQ DR DS DT DU DV DW DX DY DZ EZ EA EB EC ED EE EF EG EH EI EJ EK EL EM EN EO EP EQ ER ES ET EU EV EW EX EY EZ FZ FA FB FC FD FE FF FG FH FI FJ FK FL FM FN FO FP FQ FR FS FT FU FV FW FX FY FZ GZ GA GB GC GD GE GF GG GH GI GJ GK GL GM GN GO GP GQ GR GS GT GU GV GW GX GY GZ HZ HA HB HC HD HE HF HG HH HI HJ HK HL HM HN HO HP HQ HR HS HT HU HV HW HX HY HZ IZ IA IB IC ID IE IF IG IH II IJ IK IL IM IN IO IP IQ IR IS IT IU IV IW IX IY IZ JZ JA JB JC JD JE JF JG JH JI JJ JK JL JM JN JO JP JQ JR JS JT JU JV JW JX JY JZ KZ KA KB KC KD KE KF KG KH KI KJ KK KL KM KN KO KP KQ KR KS KT KU KV KW KX KY KZ LZ LA LB LC LD LE LF LG LH LI LJ LK LL LM LN LO LP LQ LR LS LT LU LV LW LX LY LZ MZ MA MB MC MD ME MF MG MH MI MJ MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NZ NA NB NC ND NE NF NG NH NI NJ NK NL NM NN NO NP NQ NR NS NT NU NV NW NX NY NZ OZ OA OB OC OD OE OF OG OH OI OJ OK OL OM ON OO OP OQ OR OS OT OU OV OW OX OY OZ PZ PA PB PC PD PE PF PG PH PI PJ PK PL PM PN PO PP PQ PR PS PT PU PV PW PX PY PZ QZ QA QB QC QD QE QF QG QH QI QJ QK QL QM QN QO QP QQ QR QS QT QU QV QW QX QY QZ RZ RA RB RC RD RE RF RG RH RI RJ RK RL RM RN RO RP RQ RR RS RT RU RV RW RX RY RZ SZ SA SB SC SD SE SF SG SH SI SJ SK SL SM SN SO SP SQ SR SS ST SU SV SW SX SY SZ TZ TA TB TC TD TE TF TG TH TI TJ TK TL TM TN TO TP TQ TR TS TT TU TV TW TX TY TZ UZ UA UB UC UD UE UF UG UH UI UJ UK UL UM UN UO UP UQ UR US UT UU UV UW UX UY UZ VZ VA VB VC VD VE VF VG VH VI VJ VK VL VM VN VO VP VQ VR VS VT VU VV VW VX VY VZ WZ WA WB WC WD WE WF WG WH WI WJ WK WL WM WN WO WP WQ WR WS WT WU WV WW WX WY WZ XZ XA XB XC XD XE XF XG XH XI XJ XK XL XM XN XO XP XQ XR XS XT XU XV XW XX XY XZ YZ YA YB YC YD YE YF YG YH YI YJ YK YL YM YN YO YP YQ YR YS YT YU YV YW YX YY YZ ZZ ZA ZB ZC ZD ZE ZF ZG ZH ZI ZJ ZK ZL ZM ZN ZO ZP ZQ ZR ZS ZT ZU ZV ZW ZX ZY ZZ
Sprich, der Inhalt von char-Array ist undefiniert. Was kann ich dagegen tun?
Möchte jemand refactoren?
('Z'-'A'+1)^2 müsste es nach meinen Berechnungen sein, bei "so quasi Permutationen".
Grüßchen
-
Wenn du nur die Null Terminierung am Anfang einmal setzt, stimmt diese natürlich nur für 2 Zeichen lange Strings. Deine jetzige Funktion gibt aber auch kürzere Kombinationen aus. So wäre die Korrektur für unterschiedlich lange Strings:
void combi(char* ca, const int len, int idx) { if (len == idx) { return; } for (char c = 'A'; c <= 'Z'; c++) { ca[idx] = c; ca[idx+1] = '\0'; std::cout << ca << std::endl; combi(ca, len, idx + 1); } }
Bzw. so wenn du wirklich nur die 2 Zeichen langen Kombinationen haben möchtest:
void combi(char* ca, const int len, int idx) { if (len == idx) { std::cout << ca << std::endl; // Wir nehmen an das Ende ist schon Null terminiert return; } for (char c = 'A'; c <= 'Z'; c++) { ca[idx] = c; combi(ca, len, idx + 1); } }
-
Danke für deine Antwort!
Jetzt hab ich den Compiler glaube ich "kaputtgemacht"
:
void combi(char * const ca, const int len, const int idx) { if (len == idx) { std::cout << ca << std::endl; return; } for (char c = 'A'; c <= 'Z'; c++) { ca[idx] = c; combi(ca, len, idx + 1); } }
const int len = LENN; char * const ca; ca = [len + 1]; ca[len] = '\0'; combi((char * const ) ca, len, 0);
Also combi funktioniert, den ca-Zeiger kann ich in der Funktion nicht "umbiegen".
Aber beim Array ca meckert er:
assignment of read-only variable 'ca'
cannot convert 'main(int, char**)::__lambda0' to 'char* const' in assignment
Weiß jemand rat?
Übersetzt wird mit '-ansi -pedantic -Wall -Werror -o' ~ .
-
Setz den Terminator dann, wenn du weißt dass du am Ende bist.
if (len == idx) { ca[idx] = 0; .... }
-
DirkB schrieb:
Setz den Terminator dann, wenn du weißt dass du am Ende bist.
Danke für eure Antworten, aber laut Lesbarkeit, Konvention usw. doch '\0' anstatt "das nackte" 0?
Und ich möchte halt jede "lazy" implizite Typumwandlung vermeiden, um den Lesefluss zu erhöhen.
Außerdem ist das jedes Mal ein assignment (>= cycle) mehr, wenn ich '\0' nicht vorher von außen zuweise.
C++ soll doch performant bleiben.
(Jetzt werden sich viele aufregen, ich weiß).
-
EinGastredner schrieb:
char * const ca; ca = [len + 1];
So definiert man keine Arrays oder Pointer. Entweder
char * const ca = new char[len + 1];
oder
char ca[len + 1];
Warum jetzt überhaupt das
const
überall? Die Parameter in der Funktion sind sowieso schon lokale Kopien. Wenn die Funktion den Wert ändert betrifft es nicht die Variablen beim Aufruf.
-
sebi707 schrieb:
Warum jetzt überhaupt das
const
überall? Die Parameter in der Funktion sind sowieso schon lokale Kopien.Lokale Kopien des Zeigers auf das char-Array, oder hab ich etwas falsch verstanden? Wenn das ganze Array kopiert werden würde, wäre das ja tierisch langsam...
const, damit ich den Unterschied zwischen char * const und cont char * kenne, der Programmierer weniger Fehler macht usw.
-
Edit2: Damit funktioniert's, Danke, damit ist die Übungseinheit abgeschlossen und die nächste kann kommen:
#include <stdlib.h> #include <string.h> #include <iostream> #define LENN 3 void combi(char * const ca, const int len, const int idx) { if (len == idx) { return; } for (char c = 'A'; c <= 'Z'; c++) { ca[idx] = c; ca[idx + 1] = '\0'; // if (idx <= 1) std::cout << ca << std::endl; combi(ca, len, idx + 1); } } int main(int count, char** args) { int x; x = 5; std::cout << x << std::endl; std::cout << "Hello World..." << std::endl; for (int i = 0; i < count; i++) { std::cout << args[i] << " "; } std::cout << std::endl; const int len = LENN; char * const ca = new char[len + 1]; ca[len] = '\0'; combi((char * const ) ca, len, 0); return 0; }
Schönen Abend noch
-
Moin, ich muss noch mal nerven,
void combi(char * const ca, const int len, const int idx) {
was wird hier kopiert, was wird referenziert,
void combi(char * ca, int * len, int * idx) {
was wird hier kopiert, was wird referenziert,
zwischen
int
und einer Adresse "auf"int
gibt es keinen Geschwindigkeitsunterschied?Ist es so effizient, kann man es schneller/besser implementieren?
Was praktisch ist, ist, dass man
'\0'
einfach überall "einfügen" kann, in anderen Programmiersprachen ist das nicht so einfach/schnell möglich.Schreibt ihr öfter(s)
const
? Oder ist das Ballast, der in ASM/Maschinencode eh nicht auftaucht?Danke, wer es schafft, das zu lesen.
(Etwas flax ist es ja auch)
-
Für jeden Parameter kriegst du eine lokale Kopie. Wenn dein Parameter ein Pointer ist kriegst du eine Kopie der Adresse. Wenn dein Parameter ein int, enum, struct, class usw. ist kriegst du eine Kopie davon. Bei ints oder andere kleinen Objekten ist das schnell, da ein int auch etwa die größe eines Pointers hat (je nach System). Ohne nachgemessen zu haben würde ich sagen, dass bei kleinen Objekten wie int die Variante ohne Pointer schneller sein könnte, da man durch den Pointer eine zusätzliche Indirektion drin hat. Ich würde daher nur für größere Objekte Pointer oder Referenzen nehmen.
Beim anhängen von '\0' wird ein Byte geschrieben. Wenn du das nicht Millionen mal pro Sekunde machst wird das nicht auffallen.
Und nun zu const: Ich schreibe bei einfachen Variablen wie int eigentlich nie const dran. Wo ich const schreibe ist bei Pointern und Referenzen. Ist dir dabei der Unterschied zwischen
const char*
undchar* const
klar? Das erste ist ein Pointer auf einconst char
. Das zweite ist ein konstanter Pointer auf einchar
. Das ist ein wichtiger Unterschied! Jedenfalls schreibe ich davon höchstens die erste Variante. Die zweite habe ich glaube ich noch nie benutzt. Deine const Pointer verhindern, dass der Programmierer verändert wohin der Pointer zeigt. Das Hinzufügen von const ändert übrigens fast nie etwas am generierten Maschienencode (außer vielleicht bei globalen const Variablen).
-
Hallo Sebi, Danke für deine Antwort. Ich nehme an, es ist ein Zeiger/Pointer/Referenz. (Bedeuten alle dasselbe?)
Wäre diese Methode schneller (Millisekunden)?:
void combi2() { static const int len = LENN; static char * const ca = new char[len + 1]; static int idx = 0; if (len == idx) { return; } for (char c = 'A'; c <= 'Z'; c++) { ca[idx] = c; idx++; ca[idx] = '\0'; std::cout << ca << std::endl; combi2(); idx--; } }
Jetzt könnten len, ca und idx im Register liegen und müssten nicht kopiert werden. Was sagst du dazu?
-
Pointer und Zeiger sind das gleiche. Referenzen sind was anderes, aber so ähnlich. Steht in jedem C++ Buch.
Zu deinem Code: Keine Ahnung ob es schneller ist. Muss man wohl nachmessen. Das langsamste dürfte wohl eh die Ausgabe der Strings sein (unter Windows besonders, da ist jede Ausgabe auf der Konsole soooo langsam). Warum bist du überhaupt an dem letzten Stück Geschwindigkeit interessiert? Das ist eher ein Thema für Fortgeschrittene und den größten Geschwindigkeitsvorteil holt man meist noch aus den Algorithmen statt solchen Mikrooptimierungen raus.
-
sebi707 schrieb:
Das langsamste dürfte wohl eh die Ausgabe der Strings sein
Das stimmt, das dürfte der bottleneck sein.
Aber es könnte ja auch ein verschlüsseltes .zip "geknackt" werden.
Für variable Länge ist der Algo nicht zu verbessern, da dann muss man microoptimieren.
Microoptimierung ist nicht the root of all evil.^^
Danke nochmal.