Mit einem Cast vor dem String (z. B. typedef char ach6[6] => (ach6)
"123456") geht's auch nicht. Ich muss ein Array als Aggregat
initialisieren mit einigen hundert längeren Strings, die bereits fertig
formatiert vorliegen. Mit der Aufspaltung in einzelne Zeichen ('1', '2'
...) sprengt das das Gesamtbild und ist dann auch nicht mehr lesbar. Zur
Laufzeit möchte ich auch nichts umformatieren.
Käme man mit Macros weiter? Wenn ja, wie?
Ich habe ein Array mit fest vorgegebenen Strukturen. Dort sind an einer
Stelle 48 ASCII-Zeichen vorgesehen (größtenteils lesbarer Text), die
ganz einfach keine Null am Ende haben und entweder bis ans Ende mit
Spaces aufgefüllt oder komplett ausgenutzt sind.
Interessanterweise kann man Strings ja auch zusammensetzen, z. B. "123"
"456" "789", sogar mit Zeilenumbruch dazwischen. Hinter das letzte
Zeichen kommt dennoch unweigerlich eine Null.
Ich habe schon mit folgendem Macro getestet:
1
#typedef mTxt(a) {a[0],a[1],a[2],a[3],a[4],a[5]}
2
...
3
char achComment[6] = mTxt("123456");
... geht sytaktisch auch nicht. Ich denke aber, dass das die richtige
Richtung sein müsste.
df1as schrieb:> Ich habe ein Array mit fest vorgegebenen Strukturen. Dort sind an einer> Stelle 48 ASCII-Zeichen vorgesehen (größtenteils lesbarer Text), die> ganz einfach keine Null am Ende haben und entweder bis ans Ende mit> Spaces aufgefüllt oder komplett ausgenutzt sind.
Nochmal: Wozu sollen diese Felder taugen in C?
Du kannst keine Standard Stringfunktionen von C darauf anwenden. Weder
zur Ausgabe noch zur Manipulation. Die sind praktisch nutzlos.
Du wirst diese Felder dann wie binärdaten behandeln müssen und dir
irgendwo die Länge merken. Das macht in der Regel überhaupt keinen Sinn!
> char achComment[6] = "123456"; // zu lang
In C ist das in Ordnung und hat den gewünschten Effekt, in C++ ist es
ein Fehler. Dort kommst du um die Schreibweise mit den einzelnen Char-
Konstanten wahrscheinlich nicht herum.
df1as schrieb:> Ja, viele Dinge im Leben machen keinen Sinn ...
Auch dass irgend etwas Sinn machen könnte ist sinnlos, denn machen
bedeutet etwas (greifbares) schaffen.
Klaus Wachtler schrieb:> geht halt nicht.>> Bei "..." ist immer die 0 dabei,
Nicht ganz.
Es gibt eine Ausnahme.
Wenn der String (nur die Zeichen ohne abschliessendes \0) knirsch ins
Array passt, dann hängt der COmpiler kein \0 an.
char command[6] = "12345"; // ok, \0 Byte angehänt
char command[6] = "123456"; // auch ok, kein \0 Byte angehängt
char command[6] = "1234567"; // warning: initializer too long
(und genau aus dem Grund ist es normalerweise dämlich die Arraylänge
vorzugeben und nicht vom Compiler abzählen zu lassen)
df1as schrieb:> #typedef mTxt(a) {a[0],a[1],a[2],a[3],a[4],a[5]}> ...> char achComment[6] = mTxt("123456");> ... geht sytaktisch auch nicht.
... weil es nicht #typedef, sondern #define heißt. Dann funktioniert das
schon, allerdings eben nur mit einer festen Arraylänge.
>Es gibt eine Ausnahme.>Wenn der String (nur die Zeichen ohne abschliessendes \0) knirsch ins>Array passt, dann hängt der COmpiler kein \0 an.
Ist das per C Standard so definiert, oder liegt das in der
Interpretationsfreiheit des zufällig gewählten Compilers?
Peter S. schrieb:>>Es gibt eine Ausnahme.>>Wenn der String (nur die Zeichen ohne abschliessendes \0) knirsch ins>>Array passt, dann hängt der COmpiler kein \0 an.>> Ist das per C Standard so definiert, oder liegt das in der> Interpretationsfreiheit des zufällig gewählten Compilers?
Ist so definiert.
Frag mich aber nicht, wo da der Sinn liegt. Stammt wahrscheinlich noch
aus der Zeit als jedes Byte RAM kostbar war. Weit, weit vor unserer
Zeit, lange vor dem großen Krieg.
df1as schrieb:> Ja, viele Dinge im Leben machen keinen Sinn ...
Was man dir hier versucht zu erklären ist, dass du deine Daten auf
unterschiedlichen Wegen verwalten kannst.
1) Als String
Also zum Beispiel
char foo[4] = "abc"; // abc mit \0 als Abschluss
Das macht man, wenn man diese Daten als string weiter verarbeiten will.
Sprich, wenn man die String-Funktionen der C Bibliotheken verwenden
möchte. Diese Funktionen basieren alle darauf, dass der String mit einem
'\0' abgeschlossen ist. Sonst können sie das Ende nicht erkennen.
Wer damit arbeiten will, muss seine char-Buffer immer ein Byte größer
machen, als der String lang ist, damit man das '\0' noch unterbekommt.
2) Als "normale" Daten
Wenn man KEINE String-Funktionen benutzen will, hindert dich niemand
daran die Daten z.B. in ein uint8_t Array zu packen. Da hast du dann
immer die Länge der Daten == Länge des Arrays.
Initialisieren könnte man dass dann zum Beipiel mit memset. Oder aus
einer anderen Quelle kopieren mit memcpy.
Die Frage ist doch erstmal, woher kommen deine Daten und wie willst du
sie weiterverarbeiten?
Ja, sry. War schon #define, nicht typedef. Vom Prinzip her müsste das
mit dem Macro gehen. Vermutlich bin ich mit der Macro-Syntax mit einem
String als Parameter nur nicht firm.
Dass es mit C statt C++ mit dem Abschneiden der Null gehen soll, wäre
auch eine Lösung. C sollte aber eine Untermenge von C++ sein. Gibt es
vielleicht ein #pragma, mit dem man das String-Abschneiden (wieder)
einschalten kann?
df1as schrieb:> Dass es mit C statt C++ mit dem Abschneiden der Null gehen soll, wäre> auch eine Lösung. C sollte aber eine Untermenge von C++ sein.
Das ist schon mal der erste Fehler.
C ist schon lange keine Untermenge von C++ mehr.
C++ und C sind 2 verschiedene Sprachen. Und so sollte man das auch
behandeln.
> Gibt es> vielleicht ein #pragma, mit dem man das String-Abschneiden (wieder)> einschalten kann?
Wenn überhaupt, dann weiß das die Doku zu deinem Compiler (pragma ist
immer compilerspezifisch). Ich wüsste allerdings keinen einzigen
Compiler der das macht.
Aber: die meisten Systeme können C und C++ insofern mischen, dass man
die beiden einfach zusammenlinken kann. Gib deine Initialisierungen in
ein eigenes File und kompiliere es mit einem C-Compiler. Viele Compiler
sind Kombi-compiler, die sowohl C++ als auch C können. Entweder gibt es
dafür einen Commandline Switch oder sie benutzen die Dateiendung um zu
entscheiden, welcher Modus benutzt werden soll.
Das die meisten das Ganze trotzdem nicht für eine gute Idee halten, ist
ja jetzt schon oft genug ausgesprochen worden. Daher spar ich mir eine
weitere Wiederholung.
Also ... die Daten sind einige kB lang und zur Übertragung in eine ca.
15 Jahre alte Steuerung vorgesehen. Am Format kann man nicht mehr
rütteln. Die Inhalte der Daten liegen auch fertig formatiert vor.
Die Schreibweise mit einzelnen Zeichen ist ja möglich, aber aus 48
Zeichen werden dann schon mal 192 geplenkt, sonst 240. Wenn's mit dem
Macro nicht klappt, muss ich diesen Weg dann wohl nehmen - oder auf
Assembler, ggf. inline, umsteigen.
df1as schrieb:> Also ... die Daten sind einige kB lang und zur Übertragung in eine ca.> 15 Jahre alte Steuerung vorgesehen. Am Format kann man nicht mehr> rütteln. Die Inhalte der Daten liegen auch fertig formatiert vor.
Ich seh da jetzt das Problem immer noch nicht.
Wenn dein C++ Programm, das alles übertragen soll, hindert dich ja
niemand daran, im C++ Programm das abschliessende \0 Byte nicht zu
übertragen, wenn es die Steuerung so haben will. Deswegen muss man sich
doch keine Krücken ins Übertragungsprogramm holen.
Aber dazu wissen hier alle zu wenig von deinem System um da irgendeine
vernünftige Aussage zu machen. Die Erfahrung ist halt, dass sich
Programmierer oft in einen Weg verbeissen, der neutral gesehen nicht der
schlaueste ist. Und derartige 'Künstelaktionen' sind halt oft nicht der
schlaueste Weg, weil umständlich und fehleranfällig.
Und das fängt schon damit an, warum die Texte im Programm sind und nicht
von einem File eingelesen werden.
df1as schrieb:> char achComment[6] = {'1', '2', '3', '4', '5', '6'}; // zu umständlichdf1as schrieb:> Wenn's mit dem> Macro nicht klappt, muss ich diesen Weg dann wohl nehmen - oder auf> Assembler, ggf. inline, umsteigen.
Und das ist weniger umständlich?
Na dann mach mal.
Ja, das Macro muss ich nur einmal schreiben, nicht ein paar hundert
Male.
1
#define mT(a) ...
2
3
tstStrukturArray[] = {
4
{32, 0, 100, 600, mT("Hier steht der lange Kommentar "),{{101,18},{...
5
{32, 1, 100, 600, mT("Hier steht der naechste Kommentar "),{{100,10},{...
6
{32, 2, 100, 600, mT("Hier steht der uebernaechste "),{{105,11},{...
So in etwa.
Natürlich kann ich auch das Kommentarfeld ein Zeichen länger definieren
und dann per Programm selbiges wieder aussparen oder von einer
Initialisierungsstruktur in eine Ausgangsstruktur umformatieren. Das
wollte ich nicht, weil es vielleicht schneller gegangen wäre, wenn ...
aber erstens kommt es mal wieder anders und zweitens als man denkt.
Ich hab allerdings das ganz starke Gefühl, dass da wieder mal wer "Daten
die über eine Schnittstelle übertragen werden" und "Daten die im
Programm gespeichert werden" durcheinanderwirft.
df1as schrieb:> Natürlich kann ich auch das Kommentarfeld ein Zeichen länger definieren> und dann per Programm selbiges wieder aussparen oder von einer> Initialisierungsstruktur in eine Ausgangsstruktur umformatieren. Das> wollte ich nicht, weil es vielleicht schneller gegangen wäre, wenn ...
Nur so als Tipp:
Du wärst schon längst damit fertig.
Zumal es sowieso unklug ist eine STruktur einfach so als Ganzes über
eine Schnittstelle an ein bereits bestehendes System zu übertragen.
Schon mal was von Padding Bytes gehört? Oder davon, dass int je nach
System mal 2, mal 4 oder auf den neuen Systemen gar 8 Bytes hat. Oder
dass die Byte Order nicht genormt ist oder ...
Mach dir eine Funktion, die die Einzelteile der STruktur schön
nacheinander einzeln auf den Weg bringt. Und dort kommt dann die
Sonderbehandlung für den Text rein. Da kannst du dann auch zu kurze
Texte mit Leerzeichen auffüllen.
Du ignorierst du hilfreichen Gegenfragen geflissentlich...
Die Frage, ob dein gewünschtes Vorgehen überhaupt notwendig, hilfreich
oder gar nützlich ist würde ich mir durch den Kopf gehen lassen.
Manchmal ist man ja auch dem Holzweg. Oder bekommt eine Aufgabe die
ebenfalls auf einem Holzweg basiert.
edit. OK hat sich einiges getan während des Schreibens.
df1as schrieb:> Ja klar wäre ich schon fertig. So gesehen. Aber eine kleine> Herausforderung ist das schon und da gebe ich eigentlich nur ungern> klein bei.
Ein Problem mit einem völlig falschen Programmieransatz lösen zu wollen
ist keine "Herausforderung" sondern dämlich.
Bitte erst die Aufgabenstellung lesen, dann verstehen, dann sich zu Wort
melden, wenn man einen undämlichen Beitrag beisteuern kann ...
In dieser "Aufgabenstellung" geht es ja gerade nicht um ein Programm,
sondern um eine (etwas spezielle) Aggregatinitialisierung in lesbarer
Form, nicht weniger aber auch nicht mehr. Nur Präprozessor und Compiler
sollten etwas zu tun bekommen.
Danke erstmal für alle konstruktiven Vorschläge. Der Wechsel von C++ auf
C hatte im Visual Studio keine Auswirkungen, alle Einstellungen sind
dort unter der Rubrik "C/C++" vereint - wohl wegen der gewünschten
Abwärtskompatibilität. Derweil benutze ich die "unlesbare" Variante.
...-.-
{32, 0, 100, 600, mT("Hier steht der lange Kommentar "),{{101,18},{...
5
{32, 1, 100, 600, mT("Hier steht der naechste Kommentar "),{{100,10},{...
6
{32, 2, 100, 600, mT("Hier steht der uebernaechste "),{{105,11},{...
Entspricht bereits meinem ersten Makroversuch, nur der Compiler benötigt
wohl noch eine Typdeklaration. Mit "String"[n] geht's also nicht, mit
((const char*)"String")[n] schon.
df1as schrieb:> In dieser "Aufgabenstellung" geht es ja gerade nicht um ein Programm,> sondern um eine (etwas spezielle) Aggregatinitialisierung in lesbarer> Form
Und wer macht das? Das Programm.
Die Lösung wurde schon mehrfach genannt. Arbeite mit 0-terminierten
Strings. Kein Compiler und keine Sprache zwingt dich, die mitzusenden,
falls du sie nicht brauchst.
Bei Array-Verwendung brauchst du einen index für die Länge oder musst
mit festen Längen arbeiten und du kannst die Stringfunktionen der
Standardlibrary nicht verwenden.
df1as schrieb:> In dieser "Aufgabenstellung" geht es ja gerade nicht um ein Programm,> sondern um eine (etwas spezielle) Aggregatinitialisierung in lesbarer> Form, nicht weniger aber auch nicht mehr. Nur Präprozessor und Compiler> sollten etwas zu tun bekommen.
Du versuchst gerade die Aufgabenstellung zu lösen
"Wie kann ich in 3000 Meter Wassertiefe nur mit einer Badehose 4 Wochen
überleben"
wenn die richtige Problemlösung darin besteht, einfach eine Reling am
Schiff anzubringen, so dass du gar nicht erst über Bord gehst und für
den Fall des Falles eine Schwimmweste anlegen musst.
Manchmal ist eine sich ergebende "Aufgabenstellung" einfach nur ein
Indiz, dass das Problem einer anderen Lösungsstrategie bedarf. Es bringt
nichts, dieser Aufgabenstellung nachzulaufen, weil man dann einfach nur
einen Haufen Aufwand für nix hat.
df1as schrieb:> In dieser "Aufgabenstellung" geht es ja gerade nicht um ein Programm,
Alles klar, mein Fehler. Ich dachte es geht ums programmieren, vorallem
weils in der Rubrik "gcc" steht.
> sondern um eine (etwas spezielle) Aggregatinitialisierung in lesbarer> Form, nicht weniger aber auch nicht mehr.
Namentlich um das Befüllen von structs, mit Strings welche aber nicht 0
terminiert sind und deshalb im engeren Sinne von C also auch überhaupt
gar keine Strings sind. Aha. Aber WARUM und zu welchem Zweck diese
absolute Krücke von Initaliserung gemacht wird inkl. einem
Monster-Define bleibt schleierhaft und genau dieses WARUM scheint die
Wurzel deines Übels zu sein. Aber du hörst einfach nicht auf die Leute
hier die dir das sagen wollen.
Beratungsresistent hoch 10. Denn es gibt einfach keinen vernünftigen
Grund warum dies so machen sollte wie du es vor hast.
Hast du denn überhaupt überprüft ob das direkt gesendete Array und
dessen Structs von Zielsystem auch so verstanden werden? Wie die
Vorredner schon angemerkt haben kann da im Bereich Datentyplänge und
Byteorder einiges schiefgehen.
Nein, nix geht schief und alles passt jetzt. Danke für die Nachfrage.
Zumindest packt der MS-Compiler alles fix und fertig in nur die eine
Variable bzw. Konstante. Kann aber schon sein, dass ein GCC oder
sonstiger Compiler das nicht so elegant hinbekommt. Ohne eingeschaltete
Optimierung sieht's mit Visual auch leicht verwegen aus (fummelt dann
die Strings in den Strukturen im Startup-Code zusammen), aber dazu
gibt's die Optimierung ja u. a.
Besser, wieder was dazugelernt zu haben (übrigens nach über 30 Jahren
Programmiererfahrung auf etlichen Plattformen meinerseits), als durch
wie auch immer geartete Beratung in Richtung Lernresistenz unterwegs zu
sein.
...-.-
huhu,
ich schalte mich kurz mal ein..
für das problem des fragestellers:
ich würde ein kleines script in scriptsprache meiner wahl schreiben
welches mir aus den strings eine hübsche variablendefinition in c array
ausgibt.
also aus "abc" dann 'a','b','c',' ',.. macht.
für das anliegen: strings ohne null
hab mal gelesen dass die erfinder der sprache c eben diese
nullterminierten strings als eine ihrer größten fehlentscheidungen
ansahen - führte zu buffer overflows und anderen problemen.
in der schule hatte ich damals pascal gelernt - dort haben die strings
zuerst ein byte mit länge, dann folgen die zeichen. beschränkung ist so
auf max. 255 zeichen festgesetzt.
meine frage ist: gibt es ähnliches für c? wohl nicht, sonst hätte ich es
schon gefunden.
und festbreitenstrings könnten manchmal auch ihren sinn haben - als eben
alternative, ähnlich wie integer mit signed/unsigned.
superstru schrieb:> in der schule hatte ich damals pascal gelernt - dort haben die strings> zuerst ein byte mit länge, dann folgen die zeichen. beschränkung ist so> auf max. 255 zeichen festgesetzt.
Das ist auch nicht besser. Als ich damals von Pascal auf C umgestiegen
bin waren nicht längenbegrenzte Strings einer der wichtigsten Vorteile
von C. Genauso wie die konsequente und transparente Verwendung von
Zeigern.
Aber die Welt ist seitdem ja nicht stehen geblieben
Karl Heinz Buchegger schrieb:> Du versuchst gerade die Aufgabenstellung zu lösen> "Wie kann ich in 3000 Meter Wassertiefe nur mit einer Badehose 4 Wochen> überleben"> wenn die richtige Problemlösung darin besteht, einfach eine Reling am> Schiff anzubringen, so dass du gar nicht erst über Bord gehst und für> den Fall des Falles eine Schwimmweste anlegen musst.
ROFL der trifft es ganz gut.
df1as schrieb:> In dieser "Aufgabenstellung" geht es ja gerade nicht um ein Programm,> sondern um eine (etwas spezielle) Aggregatinitialisierung in lesbarer> Form,df1as schrieb:> Als Nachtrag hier noch eine lauffähige Makrolösung:#define mT(a) {((const
char*)a)[0],((const char*)a)[1],((const char*)a)[2] ...}
> ...> tstMyStruct astMyArrayOfStructs[] = {> {32, 0, 100, 600, mT("Hier steht der lange Kommentar "),{{101,18},{...> {32, 1, 100, 600, mT("Hier steht der naechste Kommentar "),{{100,10},{...> {32, 2, 100, 600, mT("Hier steht der uebernaechste "),{{105,11},{...
Und DAS nennst du lesbar???????????????
Klar der Text ist lesbar, aber der Rest....
superstru schrieb:> hab mal gelesen dass die erfinder der sprache c eben diese> nullterminierten strings als eine ihrer größten fehlentscheidungen> ansahen - führte zu buffer overflows und anderen problemen.
Hast du dafür eine Quelle? Buffer Overflows wären ja auch mit der
Pascal-Längenbyte möglich, dafür ist die Gesamtlänge des Strings
willkürlich beschränkt.
Detlev T. schrieb:> superstru schrieb:>> hab mal gelesen dass die erfinder der sprache c eben diese>> nullterminierten strings als eine ihrer größten fehlentscheidungen>> ansahen - führte zu buffer overflows und anderen problemen.>> Hast du dafür eine Quelle?
Würde mich jetzt auch interessieren.
Denn das größte Problem in C sind nicht 0-terminierte Strings, sondern
die Art und Weise, wie Arrays behandelt werden. Der Array-Pointer
Dualismus hat seine Vorteile, hat aber auch seine Nachteile.
0-terminierte Strings können da genau gar nichts dafür.
Das, und die Einführung der Funktion gets(), sind die IMHO die größten
Problemkreise in C. Wobei man letzteres einfach korrigieren könnte,
ersteres aber nicht.
Das eigentlich Bescheuerte an nullterminierten Strings ist doch die
Komplexität von O(N) zum Bestimmen ihrer Länge, aber das hat nichts mit
Bufferoverruns zu tun, die kann ich auch mit std::string produzieren
wenn ich will :D
Bartli schrieb:> Das eigentlich Bescheuerte an nullterminierten Strings ist doch die> Komplexität von O(N) zum Bestimmen ihrer Länge
die Frage ist für was man die länge überhaupt braucht. Zur ausgabe
nicht, da kann man ausgeben bis eine 0 kommt. Zum kopieren auch nicht
weil man bis zur 0 kopieren kann. (wenn man die maximale länge weiss)
Damit ist das nicht in jedem Fall unsinnig, warum eine Länge mitführen
wenn sie nicht gebraucht wird.
> die Frage ist für was man die länge überhaupt braucht.
Bereits für relativ triviale Stringoperationen?
1
const char *a = "irgendwas";
2
const char *b = " und sowieso";
3
char *c = malloc(strlen(a) + strlen(b) + 1);
4
/* Zusammenfuegen von a und b nach c mit strcat darfst du dir selber ausmalen. */
> Damit ist das nicht in jedem Fall unsinnig, warum eine Länge mitführen> wenn sie nicht gebraucht wird.
Ein nullterminierter String führt implizit seine Länge mit, in Form der
terminierenden 0, was, darauf bestehe ich, bescheuert ist.
Bartli schrieb:>> die Frage ist für was man die länge überhaupt braucht.>> Bereits für relativ triviale Stringoperationen?
Die du jetzt so konstruiert hast, dass man ohne Kentniss der Stringlänge
nicht weiterkommt :-)
Aber auch du musst zugeben, dass man ohne weiteres Stringoperationen
konstruieren kann, in denen man die Länge genau gar nicht braucht
sondern einfach nur durch das abschliessende \0 Zeichen genug
Information hat. Und die sind ebenfalls gar nicht mal so selten.
> Ein nullterminierter String führt implizit seine Länge mit, in Form der> terminierenden 0, was, darauf bestehe ich, bescheuert ist.
Und ich bestehe darauf, dass das explizite Mitführen einer Länge
bescheuert ist. :-) Wie groß machst du denn diese Längenangabe? 1 Byte,
2 Byte, n Byte? Mit der 0-terminierung kann ein String so groß werden,
wie es ein Pointer und der Speicher hergibt. Mit einer expliziten Länge
hast du eine künstliche Beschränkung eingezogen.
So what?
Für jede Designentscheidung gibt es immer Dinge die dafür und Dinge die
dagegen sprechen. Nur muss man sich halt für irgendwas entscheiden. Und
da Arrays (bzw. allgemeiner Speicherallokierungen) in C nicht wissen wie
groß sie sind, macht es auch keinen Sinn, für Strings da jetzt was
anderes einzufordern.
> Wie groß machst du denn diese Längenangabe? 1 Byte, 2 Byte, n Byte?> Mit der 0-terminierung kann ein String so groß werden, wie es ein> Pointer und der Speicher hergibt.
Du gibst die Antwort fast selber (und weisst sie natürlich auch):
Natürlich nimmst du sinnvollerweise als Längenangabe size_t o.Ä. und
nicht 8 Bit wie Pascal.
(Ich höre bereits das Geschrei von wegen Speicherverschwendung bei
kleinen Strings etc:)
was spricht dagegen, in den paar Fällen, wo die Länge sinnvoll ist, sie
getrennt mitzuführen (als Länge oder als Zeiger auf das Ende)?
Wenn ich lange Strings zusammenfüge, kann ich doch das Ende mit einem
Pointer verfolgen, der z.B. auch mit dem Rückgabewert von sprintf()
weiter gesetzt werden kann.
Haben alle Strings zwangsweise die Länge, kann ich sie nicht mehr
weglassen, wo sie nur Ballast ist.
C ist halt kein Pascal o.ä., wozu also krampfhaft dazu vergewaltigen?
Bartli schrieb:> Du gibst die Antwort fast selber (und weisst sie natürlich auch):
Klar weiß ich sie.
> (Ich höre bereits das Geschrei von wegen Speicherverschwendung bei> kleinen Strings etc:)
Wenns nur das wäre.
Nehmen wir mal eine andere Funktion her
void toUpperS( char * str )
{
while( *str )
*str = toupper( *str++ );
}
das lässt sich wunderbar mit minimalem Registerverbrauch in Object-Code
übersetzen. Das jewilige Zeichen muss sowieso in ein Register gelesen
werden. Die Abfrage fürs Schleifenende tut nicht mehr besonders weh. Ist
einfach nur ein conditional branch an geeigneter Stelle.
void toUpperS( string * str )
{
size_t len = Slen( str );
size_t i;
for( i = 0; i < len; ++i )
str[i] = toupper( str[i] );
}
kann weit nicht so effizient umgewandelt werden. Ich brauch mindestens 1
zusätzliches Register für entweder die Schleifenvariable oder für den
Endwert des Pointers (wenn der Compiler die Indexbenutzung wegoptimiert)
Bartli schrieb:> (Ich höre bereits das Geschrei von wegen Speicherverschwendung bei> kleinen Strings etc:)
Ja und, dann nimmt man Integer mit variabler Länge. Codierung ähnlich
wie UTF-8 man hat pro Byte 7 Nutzbit das hüchstwertige Bit gibt an daß
das nächste Byte noch zur Länge dazugehört.
Und die, die jetzt meckern wollen das braucht zuviel Rechenzeit um die
Länge wieder zusammenzusetzen sind genau diejenigen deren Programme auf
einem modernen Quadcore langsamer laufen als unsere damals auf einem
8088.
Bartli schrieb:> Natürlich nimmst du sinnvollerweise als Längenangabe size_t o.Ä.
Ja, macht Sinn; in C sind ja auch Sachen wie z. B. int
plattformabhängig; da kommt es auf den Stringlängenindikator auch nicht
mehr an...
Interchangeability lebe hoch!
</sarcasm>
> Ja, macht Sinn; in C sind ja auch Sachen wie z. B. int> plattformabhängig; da kommt es auf den Stringlängenindikator auch nicht> mehr an...
Tatsächlich tut es das nicht. Wenn du dich von deinen Sarkasmusanfall
erholt hast, schaust du dir dann z.B. auch mal an, was der Typ von
strlen's Rückgabewert ist.
> Wenns nur das wäre.> Nehmen wir mal eine andere Funktion her
Du magst mit diesem Beispiel recht haben, gehst aber wieder davon aus
dass man mit Strings so oder so nichts anderes tut als über die Zeichen
zu iterieren.
Bartli schrieb:>> Wenns nur das wäre.>> Nehmen wir mal eine andere Funktion her>> Du magst mit diesem Beispiel recht haben, gehst aber wieder davon aus> dass man mit Strings so oder so nichts anderes tut als über die Zeichen> zu iterieren.
... so wie du davon ausgegangen bist, dass man die Länge des Strings als
Ganzes braucht :-)
Ich glaube ich sagte schon: Es hat alles sein für und wieder.
Für mich war immer klar, daß man für z.B. rechtsbündiges schreiben von
Strings deren Länge braucht, um entsprechend weit links zu beginnen. Und
selbst wenn ich grafikmäßig von rechts nach links schriebe, muss ich
doch dann auch das letzte Zeichen kennen, um da anzufangen.
Ich glaube Karl Heinz hat die notwendige Erfahrung um sagen zu können,
daß die Stringlänge nicht wichtig ist, daher interessiert mich, wie du
z.B. Text rechtsbündig hinschreibst ohne strlen - vielleicht habe ich da
immer viel zu viel Zeit vergeudet.
Clemens M. schrieb:> daher interessiert mich, wie du> z.B. Text rechtsbündig hinschreibst ohne strlen
"strlen" gibt es, weil man manchmal eben die Länge braucht.
Man muß sie aber nicht ständig mitführen.
Schon unter DOS wurde ein Text bis zum Endezeichen $ ausgegeben.
Peter
Effizienz ist kein gutes Argument. Die Frage ob Stringverarbeitung
überhaupt nennenswert zur Ausführungszeit beiträgt mal außen vor
gelassen: das byteweise Abklappern bis zur Endmarkierung mag auf dem AVR
effizient sein, nicht aber auf aktuellen Prozessoren die gerne mit
mindestens 32 oder 64 Bit pro Operation arbeiten. Relevant könnte das
sein für Hash-Berechnung z.B. in Datenbanken. Und für die Darstellung im
GUI, Schreiben in eine Datenbank, praktisch immer muss man sowieso die
Länge wissen bevor man anfangen kann etwas zu tun.
"Datenbank, GUI..."
Nicht wirklich Bereiche in denen man auf Anwendungsebene C nehmen sollte
:)
"Die Frage ob Stringverarbeitung überhaupt nennenswert zur
Ausführungszeit"
Naja, wenn man XML usw ins Spiel bringt sieht es wieder ganz anders
aus...
> Ich glaube Karl Heinz hat die notwendige Erfahrung um sagen> zu können, daß die Stringlänge nicht wichtig ist,
Holla. Das ist falsch rüber gekommen.
Ich wollte damit nicht aussagen, dass sie überhaupt nicht wichtig ist.
Ich seh aber auch, dass oft Funktionen unnötigerweise zuerst die
Stringlänge bestimmen, obwohl die eigentlich keiner braucht, wenn man
nur das Verfahren umstellt.
Zu guter Letzt:
niemand hindert einen daran sich selbst eine entsprechende struct in C
zu machen, die am Anfang ein Längenbyte besitzt. Man muss sich dann eben
ein paar Stringhilfsfunktionen als Ersatz für die str... Funktionen
machen, aber so viele sind das dann ja auch wieder nicht.
Andreas Schwarz schrieb:> Effizienz ist kein gutes Argument. Die Frage ob Stringverarbeitung> überhaupt nennenswert zur Ausführungszeit beiträgt mal außen vor> gelassen: das byteweise Abklappern bis zur Endmarkierung mag auf dem AVR> effizient sein, nicht aber auf aktuellen Prozessoren die gerne mit> mindestens 32 oder 64 Bit pro Operation arbeiten. Relevant könnte das> sein für Hash-Berechnung z.B. in Datenbanken. Und für die Darstellung im> GUI, Schreiben in eine Datenbank, praktisch immer muss man sowieso die> Länge wissen bevor man anfangen kann etwas zu tun.
Effizienz ist kein Argument? Was denn dann? Dann können ja sämtliche
Anwendungen als interpretierte JAVA-Skripte laufen lassen, das reicht ja
vom Zeitverhalten aus. Handoptimierte Interruptroutinen? Nicht nötig,
nehmen wir einfach einen schnelleren Prozessor ;-)
Die Nullterminierung von C-Strings stammt aus der EDV-Urzeit und machte
damals wegen der geringen Speichergrößen zumindestens noch etwas Sinn.
Nach heutigen Maßstäben bzw. für aktuelle Anwendungen ist das Ganze
etwas unglücklich. Gerade wenn ein von extern kommender String auch das
Zeichen 0 enthalten kann!
Ich sehe die C-eigene Stringdarstellung trotz einiger Nachteile nicht
als Probelm an:
Es gibt viele unterschiedliche Möglichkeiten der Repräsentation von
Strings, von denen jede ihre Vor- und Nachteile hat. Da C als schlanke
Sprache konzipiert ist, haben sich Ritchie & Co wahrscheinlich gesagt,
sie nehmen für die Stringrepräsentation einfach eine der einfachsten.
Die allereinfachste wäre die aus Fortran gewesen: Dort sind Strings
Character-Arrays ohne Längeninformation und ohne Endmarker. Ist das
Wort, das man in eine Stringvariable schreiben möchte, kürzer als die
Länge des Arrays, füllt man den Rest üblicherweise mit Leerzeichen auf.
Das ist natürlich sehr unbefriedigend, weil es damit keine Möglichkeit
gibt, zwischen Strings unterschiedlicher Länge zu unterscheiden. Ein
Vorteil besteht aber neben dem minimalen Speicherverbrauch darin, dass
man keinen speziellen Stringdatentyp braucht.
Die zweiteinfachste Möglichkeit ist ein Character-Array mit Endmarker,
was letztendlich in C implementiert worden ist. Die Vorteile sind die
gleichen wie bei der Fortran-Lösung, wobei zusätzlich die Stringlänge
variabel ist. Viele, aber nicht alle Stringoperationen lassen sich damit
halbwegs effizient implementieren.
Eine Repräsentation mit Längeninformation wie bspw. in Pascal wäre eine
in einigen Fällen effizientere Alternative gewesen, hätte aber einen
zusätzlichen Datentyp erfordert. Und auch Strings mit Längeninformation
sind nicht der Weisheit letzter Schluss, denn Operationen wie das
Einfügen oder Löschen von Zeichen in einem String sind immer noch
aufwendig.
In Textverarbeitungsanwendungen ist das Einfügen und Löschen von Zeichen
in sehr langen Texten eine wichtige Operation. Deswegen wird man denn
Text weder als Fortran- noch al C- noch als Pascal-String anlegen, son-
dern als doppelt verkettete Liste aus relativ kleinen Textfragmenten.
Dadurch geht zwar die direkte Indizierbarkeit einzelner Zeichen verlo-
ren, was aber bei diesen Anwendungen auch kaum gebraucht wird. Wenn
doch, kann man immer noch einen zusätzlichen Index anlegen.
Generell kann man sagen, dass fast überall, wo zeitkritisch große Text-
mengen bearbeitet werden müssen (also bspw. auch in Datenbanksystemen),
sowieso eine für die entsprechende Anwendung optimierte Textdarstellung
verwendet wird. Für alle nicht zeitkritischen Fälle (wie bspw. Benutzer-
ein-/ausgabe) ist die interne Darstellung ziemlich egal, so dass man
diejenige nimmt, die von der eingesetzten Programmiersprache nativ
unterstützt wird. In C sind das eben die nullterminierten Character-
Arrays, die in meinen Augen ganz gut in die C-Philosphie passen.
Bartli schrieb:>> die Frage ist für was man die länge überhaupt braucht.>> Bereits für relativ triviale Stringoperationen?
1
> const char *a = "irgendwas";
2
> const char *b = " und sowieso";
3
> char *c = malloc(strlen(a) + strlen(b) + 1);
4
> /* Zusammenfuegen von a und b nach c mit strcat darfst du dir selber
5
> ausmalen. */
Und wie würdest du das hinschreiben, wenn die Strings nicht
0-terminiert wären sondern die Länge mitführten?
Nicht viel anders, wohl.
Ich hab deinen Code mal durch einen "realen C Compiler" gelassen
1
#include<string.h>
2
#include<stdlib.h>
3
4
char*foo(void)
5
{
6
constchar*a="irgendwas";
7
constchar*b=" und sowieso";
8
char*c=malloc(strlen(a)+strlen(b)+1);
9
returnc;
10
}
avr-gcc sagt:
1
foo:
2
ldi r24,lo8(22)
3
clr r25
4
jmp malloc
>> Damit ist das nicht in jedem Fall unsinnig, warum eine Länge mitführen>> wenn sie nicht gebraucht wird.>> Ein nullterminierter String führt implizit seine Länge mit, in Form der> terminierenden 0, was, darauf bestehe ich, bescheuert ist.
Ich sag mal so: Es ist angehehmen, einen Punkt am Ende eines Satzes zu
machen, als am Anfang des Satzes hinzuschreiben, wie lange er ist :o)
Selbst du machst nen Punkt am Satzende.
Ach komm, jetzt stellst du dich doof an. Dir sollte ja eigentlich
schon klar sein dass ein gescheiter Compiler solche Codeschnippsel
komplett zusammenfalten kann: In diesem Fall kennt er den Inhalt der
Strings und strlen() ist anscheinend eine Builtinfunktion. Wenn du etwas
messen willst musst du schon was komplexeres hernehmen :P
Johann L. schrieb:> Ich sag mal so: Es ist angehehmen, einen Punkt am Ende eines Satzes zu> machen, als am Anfang des Satzes hinzuschreiben, wie lange er ist :o)
Und? Deshalb muß das für einen Computer nicht automatisch auch ideal
sein.
Johann L. schrieb:> Und wie würdest du das hinschreiben, wenn die Strings nicht> 0-terminiert wären sondern die Länge mitführten?>> Nicht viel anders, wohl.
Anders hinschreiben nicht, aber die Operation, die in strlen()
ausgeführt wird, wäre eine andere.
Johann L. schrieb:> #include <string.h>> #include <stdlib.h>>> char* foo (void)> {> const char *a = "irgendwas";> const char *b = " und sowieso";> char *c = malloc (strlen(a) + strlen(b) + 1);> return c;> }
Dann versuch mal einen Fall, in dem die Stringinhalte an der Stelle
nicht schon dem Compiler bekannt sind:
Das ist freilich ein komplett anderer Code.
Mit dem ursprünglichen Code ist es wie mit mindestens 50% aller
Codebeispiele, die ein Problem oder "Bug" demonstrieren sollen: Der Code
ist Märchencode und das Problem versteckt sich im
Pünktchen-Pünktchen-Pünktchen.
Johann L. schrieb:> Das ist freilich ein komplett anderer Code.>> Mit dem ursprünglichen Code ist es wie mit mindestens 50% aller> Codebeispiele, die ein Problem oder "Bug" demonstrieren sollen: Der Code> ist Märchencode und das Problem versteckt sich im> Pünktchen-Pünktchen-Pünktchen.
Märchencode ist vor allem deiner. Wann kommt es in der Realität schon
vor, daß ich zwei Strings zusammensetzen will, die ich beide direkt
davor im Code wörtlich stehen habe? Warum ist es nicht gleich ein
String? Meistens wird man die Strings eben nicht zur Compilezeit schon
kennen.
Und während die Variante mit expliziter Länge immer performant ist, ist
es die mit der Nullterminierung nur in manchen Fällen, wo der Compiler
halt gut optimieren kann.
Rolf Magnus schrieb:> Und während die Variante mit expliziter Länge immer performant ist
naja
Die Länge braucht irgendwo Platz (mind. 4 Byte, wenn die Strings nicht
gleich wieder zu limitiert sein sollen), und ggf. dafür ein Register zur
Laufzeit (oder entsprechend viele auf einem uC).
Evtl. hat man weiterhin noch je eine weitere Dereferenzierung, um an die
Zeichen zu kommen, wenn man in einer struct o.ä. neben der Länge erst
den Zeiger auf die eigentlichen Zeichen hat.
Bei 0-terminierten Strings braucht man nur den Zeiger auf das erste
Zeichen, der Rest ergibt sich.
In vielen Fällen ist das performanter, in den anderen Fällen kann man
bei Bedarf die Länge explizit mitführen.
Streiten kann man darüber, was jetzt wie häufig vorkommt - aber kann man
kaum behaupten, daß ein pascalähnlicher String generell performanter
wäre.
Jedenfalls würde es der ganzen Philosophie hinter C widersprechen,
nämlich in einfachen Fällen auch einfache Lösungen zu haben, um nicht
zwangsweise zuviele Resourcen zu verbrauchen.
Das Rundum-Wohlfühl- und Sicherheitspaket ohne Nachdenken ist C nun mal
nicht, dafür nimmt man besser etwas anderes (wenn man es haben möchte).
Rolf Magnus schrieb:> Johann L. schrieb:>> Das ist freilich ein komplett anderer Code.>>>> Mit dem ursprünglichen Code ist es wie mit mindestens 50% aller>> Codebeispiele, die ein Problem oder "Bug" demonstrieren sollen: Der Code>> ist Märchencode und das Problem versteckt sich im>> Pünktchen-Pünktchen-Pünktchen.>> Märchencode ist vor allem deiner.
Hallo? Das ist der Code von
Beitrag "Re: Strings ohne Null am Ende?"
allerdings so geschrieben, daß er compilierbar ist.
> Wann kommt es in der Realität schon> vor, daß ich zwei Strings zusammensetzen will, die ich beide direkt> davor im Code wörtlich stehen habe?
Guckst du zB in den Quellen von avr-gcc
http://gcc.gnu.org/viewcvs/trunk/gcc/config/avr/avr.c?content-type=text%2Fplain&view=co
nach dem Makro STR_PREFIX_P sowie der inline-Funktion avr_replace_prefix
und wie sie verwendet werden.
Ich behaupte jetzt mal: GCC-Quellen sind kein Märchen-Code ;-)
> Und während die Variante mit expliziter Länge immer performant ist, ist> es die mit der Nullterminierung nur in manchen Fällen, wo der Compiler> halt gut optimieren kann.
Es ist nun mal so in C. Du kannst ebensogut darüber disputieren, ob
Hamburg nicht besser an der Elbe gebaut worden wäre. Ist es aber nun
mal nicht. Jeder, der sich mit C auskennt, weiß auch um die Nachteile
und Macken der Sprache.
Brian Kernighan: "It seemed like a good idea at the time."
Johann L. schrieb:> ob> Hamburg nicht besser an der Elbe gebaut worden wäre. Ist es aber nun> mal nicht.
wäre aber bestimmt eine tolle Idee: leichter Zugang zur Nordsee, schicke
Wohngegend am Elbufer, nette Museumsschiffe, Landungsbrücken...
Aber ohne Elbe ist es vielleicht doch besser - kein Pieselwetter, nicht
so viele lästige Brücken und keine stinkenden Schiffe.
Das haben sie damals bestimmt richtig gemacht!
>Hallo? Das ist der Code von>Beitrag "Re: Strings ohne Null am Ende?">allerdings so geschrieben, daß er compilierbar ist.
Ja, der ist ursprünglich von mir, und du hast in aus dem Kontext
gerissen, durch einen Compiler gejagt und damit irgendwas beweisen
wollen.
Das Blöde an der Stringverarbeitung von C ist, dass sie schon scheitert,
wenn man damit einen 8-Bit Datenstrom von einer seriellen Schnittstelle
verarbeiten möchte.
Werner schrieb:> Das Blöde an der Stringverarbeitung von C ist, dass sie schon scheitert,> wenn man damit einen 8-Bit Datenstrom von einer seriellen Schnittstelle> verarbeiten möchte.
Was verstehst du unter "8-Bit Datenstrom"? Wenn du damit Daten meinst,
die eben keine Strings sind, dann sollte dafür auch keine
Stringverarbeitung zum Einsatz kommen.
Johann L. schrieb:> Ich behaupte jetzt mal: GCC-Quellen sind kein Märchen-Code ;-)
Ich will ja gar nicht sagen, daß es nie vorkommt, sondern nur daß es
nicht gerade der Standardfall ist.
>> Und während die Variante mit expliziter Länge immer performant ist, ist>> es die mit der Nullterminierung nur in manchen Fällen, wo der Compiler>> halt gut optimieren kann.>> Es ist nun mal so in C. Du kannst ebensogut darüber disputieren, ob> Hamburg nicht besser an der Elbe gebaut worden wäre. Ist es aber nun> mal nicht. Jeder, der sich mit C auskennt, weiß auch um die Nachteile> und Macken der Sprache.
Ich dachte, daß wir darüber diskutieren, ob nullterminierte Strings
besser sind oder schlechter als welche mit Längenangabe und nicht
darüber, ob C geändert werden sollte.
Rolf Magnus schrieb:> Was verstehst du unter "8-Bit Datenstrom"? Wenn du damit Daten meinst,> die eben keine Strings sind, dann sollte dafür auch keine> Stringverarbeitung zum Einsatz kommen.
Irgendwelche Zeichenfolgen, die in 8-Bit Paketen daherkommen. Wo siehst
du den Unterschied von Zeichen und Daten?
> Ich dachte, daß wir darüber diskutieren, ob nullterminierte Strings> besser sind oder schlechter als welche mit Längenangabe und nicht> darüber, ob C geändert werden sollte.
Nullterminierte Strings scheitern schon bei 8-Bit Zeichen. Was für ein
Anchronismus aus den Zeiten der frühen Datenübertragung.
Beliebige Daten können Nullbytes enthalten, Textdaten nicht.
Wer nullterminierte Strings für beliebige Daten mißbraucht ist selbst
schuld.
Anachronismus ist, nicht dazu lernen zu wollen.
Werner schrieb:> Rolf Magnus schrieb:>> Was verstehst du unter "8-Bit Datenstrom"? Wenn du damit Daten meinst,>> die eben keine Strings sind, dann sollte dafür auch keine>> Stringverarbeitung zum Einsatz kommen.> Irgendwelche Zeichenfolgen, die in 8-Bit Paketen daherkommen. Wo siehst> du den Unterschied von Zeichen und Daten?
Daten werden so wie sie sind verarbeitet. Bei Text ist das eben nicht
der Fall. In C ist das mit den 0-Bytes noch am simpelsten. Bei anderen
Sprachen müßtest du beim Einlesen erstmal ein Character Set angeben, und
alles wird dann für die interne Darstellung erstmal nach UTF8 oder gar
in einen 16-Bit-Zeichensatz konvertiert. Aber auch in C gibt's bei Files
z.B. einen Text- und einen Binary-Modus, und wenn man den Textmodus
nutzt, werden systemspezifische Konvertierungen beim Lesen vorgenommen.
>> Ich dachte, daß wir darüber diskutieren, ob nullterminierte Strings>> besser sind oder schlechter als welche mit Längenangabe und nicht>> darüber, ob C geändert werden sollte.>> Nullterminierte Strings scheitern schon bei 8-Bit Zeichen. Was für ein> Anchronismus aus den Zeiten der frühen Datenübertragung.
Welcher 8-Bit-Zeichensatz würde dir denn einfallen, bei dem das nicht
funktioniert?