char[], String und "End"-Zeichen
-
//============================================================================ // 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.
-
Wenn ich aber nur mit einzelne Zeichen des arrays arbeiten will, dann kann ich mir die Null-Terminierungen sparen solange ich mir die Anzahl der Zeichen merke?
Oder ist das unschön?
-
Kopfsalat schrieb:
Wenn ich aber nur mit einzelne Zeichen des arrays arbeiten will, dann kann ich mir die Null-Terminierungen sparen solange ich mir die Anzahl der Zeichen merke?
Kannste machen.
Kopfsalat schrieb:
Oder ist das unschön?
Es ist nicht nur nicht unschön, sondern es ist der ordentliche Weg. Die Nullterminierung macht fast alles langsamer als es sein müßte.
Puh, mal angenommen, jemand würde für so Supi-Strings auch noch eine Klasse schreiben, die wäre sehr praktisch. Man sollte die Compilerbauer zwingen, so eine Klasse mitauszuliefern, finde ich.
-
Einfach alles umkehren was volkard sagt und schon weißt du was er mitteilen möchte.
-
Kopfsalat schrieb:
Wenn ich aber nur mit einzelne Zeichen des arrays arbeiten will, dann kann ich mir die Null-Terminierungen sparen solange ich mir die Anzahl der Zeichen merke?
Oder ist das unschön?
Dann kannst du aber auch nicht mehr die Standardfunktionen (aus cstring und cstdio oder cout) benutzen.
D.h du musst dir die entsprechend Funktionen selber schreiben.Wenn das die Compilerbauer machen, gibt es wieder unzählige unterschiedliche Klassen.
Das sollte genormt werden.
-
roflo schrieb:
Einfach alles umkehren was volkard sagt und schon weißt du was er mitteilen möchte.
Seine Beitrage lese ich sowieso nur noch selten. Bei der durchschnittlichen Sinnhaftigkeit dieser riskiere ich da eher wenig.
DirkB schrieb:
Dann kannst du aber auch nicht mehr die Standardfunktionen (aus cstring und cstdio oder cout) benutzen.
D.h du musst dir die entsprechend Funktionen selber schreiben.Das ist richtig, aber die braucht man ja auch nicht in allen Fällen.
Und wenn man sie eben nicht braucht, dann lass ich die paar Zeilen Code für die zusätzliche Null-Terminierung eben weg, solange ich weiß, dass ich mit fest definierter Länge arbeite. (Und die ganzen +1 bei größen, Schleifen Durchläufen etc.)
War jetzt aber auch eher aus C-Perspektive bzw. halt in dem Array-Fall, Alternativen sind in C++ natürlich gegeben.
-
Ich glaube volkards Beitrag sollte eine Anspielung auf
std::string
sein. Ich weiß, dass diese Klasse die Größe separat speichert und beliebige Zeichen speichern kann (auch den Null Terminator'\0'
). Daher muss der intern gespeicherte String wohl nicht ständig Null Terminiert sein, außer wenn man sich den String perc_str()
geben lässt (Allerdings istc_str()
const, hat zufällig gerade jemand eine Stelle im Standard die besagt ob und wannstd::string
den internen Buffer mit'\0'
terminieren muss?). Statt den stdio Funktionen nutzt man dann natürlich diestd::string
Funktionen, die sowieso einfacher zu benutzen sind. Hast du deine Funktion mal damit programmiert und die Zeit gemessen?
-
sebi707 schrieb:
Hast du deine Funktion mal damit programmiert und die Zeit gemessen?
Noch nicht, aber es stehen jetzt 3 Varianten/Versionen zur Auswahl:
1. 3 Parameter (auch wenn nur Pointer) müssen immer kopiert und mitgeschleppt werden,
2. 3 Parameter müssen nicht mitgeschleppt werden, dafür muss 1-mal mehr dekrementiert werden,
3. man verwendet die Klasse String/string/str/std (weiß ich nicht genau darüber Bescheid), welche nicht immutable ist?Was dauert denn länger, ein increment oder 3-mal ein load/store?
Wenn ein String ein "richtiges" Objekt ist, dann dürfte das gegenüber dem "einfachen" Array char am langsamsten sein, imo.