So wie ich das sehe enthält der Code einen Buffer-Overflow-Bug, weil das
Byte für die abschließende Null nicht allokiert wurde (strlen() liefert
nur die Länge der sichtbaren Zeichenkette zurück).
Mein Code sieht so aus:
1
char*StringToUpper1(char*txt){
2
char*tmp;
3
inti;
4
for(i=0;txt[i]!='\0';i++);
5
tmp=(char*)malloc(i+1);
6
for(i=0;txt[i]!='\0';i++)
7
if(txt[i]>='a'&&txt[i]<='z')
8
tmp[i]=txt[i]-32;
9
else
10
tmp[i]=txt[i];
11
tmp[i]='\0';
12
returntmp;
13
}
Also auch nicht viel anders.
Persönlich würde ich das direkt auf der original Zeichenkette machen:
1
char*StringToUpper2(char*txt){
2
for(inti=0;txt[i]!='\0';i++)
3
if(txt[i]>='a'&&txt[i]<='z')
4
txt[i]-=32;
5
returntxt;
6
}
Dem oben verlinktem Thread entnehme ich, dass einige es ganz anders
machen würden.
Mich interessiert wie?
Angenommen dass die Aufgabe ist die Umwandlung ohne C-Standardfunktionen
(außer malloc) zu machen (zu Lernzwecken) und einen neuen String zu
allokieren:
1
#include<stddef.h>
2
#include<stdlib.h>
3
4
char*strToUpper(constchar*src,size_tlen){
5
char*res=(char*)malloc(len+1);
6
for(size_ti=0;i<len;i++){
7
if(src[i]>='a'&&src[i]<='z')
8
res[i]=src[i]-('a'-'A');
9
else
10
res[i]=src[i];
11
}
12
res[len]=0;
13
returnres;
14
}
Die Länge von Strings sollte man immer mitschleppen als extra size_t
-Variable, um sich ständige strlen() -Aufrufe zu sparen. Falls man die
Länge des zu übergebenen Strings nicht kennt, dann muss man eben doch
strlen() aufrufen wenn man diese Funktion verwenden will. Die
for-Schleife im standardformat "for (size_t i = 0; i < len; i++)" kann
von Compilern gut optimiert werden. Für Speicher-Indices und
Längenangaben sollte man size_t verwenden (zB für x86_64 macht das einen
Unterschied). Wichtig ist es in der Dokumentation der Funktion
anzugeben, dass der Aufrufer den zurückgegebenen String wieder freigeben
muss (mit free()). Der explizite Cast des Rückgabewerts von malloc()
macht den Code C++-Aufwärtskompatibel. Der Funktionskopf ist außerdem
const-Korrekt, d.h. es wird explizit angegeben dass strToUpper den
Inhalt von 'src' nicht verändert. So kann man zB auch string-Literale
übergeben (die ja bekanntermaßen "const" sind).
Danke.
Wozu eigentlich strlen()?
Macht das etwas anderes als
1
for(i=0;txt[i]!='\0';i++);
size_t...
Komische Datentypen gibt es.
Auf der Uni haben wir nur mit den Standardtypen gearbeitet.
Klar kann ein String nicht kürzer als Null Zeichen sein.
Aber warum dann nicht einfach unsigned int?
Länge des Strings immer mitschleppen mag der Performance zuträglich
sein.
Aber ohne ist es komfortabler für den Programmierer, der die Funktion
anwenden muss.
Würden gute Compiler die oben gelistete For-Schleife bei bekannten
Längen nicht eh wegoptimieren?
Thomas schrieb:> Wozu eigentlich strlen()?> Macht das etwas anderes alsfor (i = 0; txt[i] != '\0'; i++);
Nein. Hier ist das sozusagen eingebaut. Aber das bedeutet auch dass man
keine 0-Bytes im String haben kann; das hat schon viel Gram gebracht...
> size_t...> Komische Datentypen gibt es.> Auf der Uni haben wir nur mit den Standardtypen gearbeitet.
Das ist ein Standard-Typ. Alles was im adressierbaren Speicher sein
kann, hat eine Größe, und dieser Typ ist genau für solche Größen.
> Aber warum dann nicht einfach unsigned int?
Weil der evtl. zu klein sein kann. zB unter x86_64 GCC ist "unsigned
int" 32bit, während man einen 64bit-Adressraum hat, d.h. Strings können
auch größer als "unsigned int" sein. Deswegen nimmt man size_t, das ist
immer groß genug für die jeweilige Ziel-Plattform (64bit auf x86_64).
> Länge des Strings immer mitschleppen mag der Performance zuträglich> sein.
Und der Funktionalität & Sicherheit, wenn man 0-Bytes im String hat.
> Aber ohne ist es komfortabler für den Programmierer, der die Funktion> anwenden muss.
Wenn man Komfort haben will, sollte man kein C verwenden, sondern z.B.
C++, da geht das nämlich automatisch (ohne Performance-Einbuße).
> Würden gute Compiler die oben gelistete For-Schleife bei bekannten> Längen nicht eh wegoptimieren?
Möglich, aber das ist sie innerhalb der Funktion natürlich nicht... Wäre
sie es, wär der Aufruf wohl eh sinnlos...
Gerade getestet: Da wird nichts wegoptimiert, auch bei -O3 nicht, auch
dann nicht, wenn der komplette Code in main() steht.
Was meinst du mit "0-Bytes im String"?
Ich denke jeder String hat genau ein 0-Byte zum Abschließen (und wenn
nicht, dann ist das ein Bug).
Habe ich da was falsch verstanden?
Thomas schrieb:> Ich denke jeder String hat genau ein 0-Byte zum Abschließen (und wenn> nicht, dann ist das ein Bug).
Was wenn man einen String hat der Binärdaten enthält? Wäre ziemlich
bitter wenn die immer beim ersten 0-Byte abgeschnitten würden.
Text-Strings zur Ausgabe für den Benutzer haben üblicherweise genau ein
0-Byte, aber es wäre doch schön wenn die Funktionen grundsätzlich für
alle Strings funktionieren würden...
Dr. Sommer schrieb:> Was wenn man einen String hat der Binärdaten enthält?
Dann ist das kein (C-)String mehr, und eine Funktion "alles in
Großbuchstaben" macht dann wirklich keinen Sinn...
Dr. Sommer schrieb:> aber es wäre doch schön wenn
Man nicht immer mehr erwarten würde als spezifiziert ist...
Der ganze Orginalcode ist unnötig kompliziert und fehlerbehaftet, solche
"Vergleiche" haben aber den Vorteil das man gleich sieht, das der Autor
zumindest eine der beiden Sprache über die er meint zu Urteilen nicht
verstanden hat ;-)
(Trifft man übrigens nicht nur bei C vs X immer wieder an...)
>>aber es wäre doch schön wenn die Funktionen grundsätzlich für
alle Strings funktionieren würden...
Ja, und wir sind im Jahr 2013. Da gibt es auch so etwas wie Umlaute.
Und es gibt auch so etwas wie Unicode.
Von daher nehme ich diese Funktionen aus dem K&R gar nicht mehr, sondern
verwende Klassenbibliotheken, die auch so etwas behandeln können.
Läubi .. schrieb:> Dann ist das kein (C-)String mehr, und eine Funktion "alles in> Großbuchstaben" macht dann wirklich keinen Sinn...
Angenommen man schreibt so etwas wie ein Archivprogramm, man erhält beim
Entpacken einen Dateinamen aus der Archivdatei der so aussieht:
"hallo.exe\0.doc\0". Mit etwas Pech zeigt die GUI des Archivprogramms
den kompletten Namen inklusive .doc an, aber beim Speichern der Datei
auf die Platte wird der String aufgrund genau einer solchen
Stringfunktion bei dem 0-Byte abgeschnitten und der User freut sich über
eine .exe . Genau diese Art von C-Schlamperei ist für Fehler und
Sicherheitslücken verantwortlich. Das Mit-Übergeben der Länge ist nun
wirklich nicht schwer, und wie gesagt gibt es auch noch Sprachen die es
richtig machen (C++ mindestens, Pascal weiß ich nicht).
Läubi .. schrieb:> Der ganze Orginalcode ist unnötig kompliziert und fehlerbehaftet, solche> "Vergleiche" haben aber den Vorteil das man gleich sieht,
Das stimmt wohl, aber darum gings mir nicht.
Läubi .. schrieb:> Man nicht immer mehr erwarten würde als spezifiziert ist...
Pech wenn sich wie oben erläutert die Eingabedaten nicht an die
Spezifikation halten :-)
PittyJ schrieb:> Ja, und wir sind im Jahr 2013. Da gibt es auch so etwas wie Umlaute.> Und es gibt auch so etwas wie Unicode.
Das ist natürlich auch richtig, aber darum ging es in der Übung nicht;
eher grundlegenden Umgang mit C lernen, und dazu gehört mMn auch die
korrekte Verarbeitung von Strings.
Thomas schrieb:> http://www.bernd-leitenberger.de/pascal-und-c.shtml
PS: Auf der Seite steht nur Unsinn, glaube kein Wort was da steht.
Abschließend kann man aber sagen dass C alt & überholt ist und es nur
noch sehr sehr wenig Grund gibt es überhaupt zu verwenden, und sich im
Jahre 2013 immer noch mit zB den Schwächen der String-Verarbeitung
herumzuplagen.
Läubi schrieb:
> Der ganze Orginalcode ist unnötig kompliziert
Wie würdest du es denn machen?
Dr. Sommers und meinen Code finde ich jedenfalls ähnlich kompliziert wie
das Original...
> und fehlerbehaftet
Findest du noch weitere Fehler außer den oben beschriebenen?
> solche "Vergleiche" haben aber den Vorteil das man gleich sieht,> das der Autor zumindest eine der beiden Sprache über die er meint zu> Urteilen nicht verstanden hat ;-)
Ich gehe davon aus, das der Autor in seiner Absicht ein Pamphlet gegen C
zu verfassen diesen Code möglicherweise absichtlich vermurkst hat.
Eigentlich muss ihm ja noch nicht einmal der Bug peinlich sein,
unterstützt er doch genau seine Argumentation.
Und so unsinnig sein Text an viele Stellen auch ist, hier hat er Recht.
Solche Bugs schleichen sich in C schnell mal ein, ich glaube da kann
sich keiner von freisprechen.
Und das gemeine dabei ist ja, dass das Programm auch unter den meisten
Umständen genau das tut was es soll, mitunter jahrelang...
Wir hatten im ersten Semester Pascal, und im zweiten dann C.
Von Pascal habe ich das meiste inzwischen vergessen, von C nur die
hälfte ;)
Ich weiß nur noch, dass mir C damals wesentlich besser gefallen hat und
ich es auch irgendwie einfacher und logischer fand.
Das lag vor allem an den ganzen Sichtbarkeiten in Pascal.
Im Nachhinein denke ich, dass mir C vielleicht auch einfach nur mehr
Spaß gemacht hat, weil es so schön kniffelig ist mit den Zeigern zu
jonglieren ;)
Dr. Sommer schrieb:
> Abschließend kann man aber sagen dass C alt & überholt ist und es nur> noch sehr sehr wenig Grund gibt es überhaupt zu verwenden
Naja, ich denke Betriebsysteme und Anwendungen würde man damit
heutzutage wahrscheinlich wirklich nicht mehr schreiben, wenn man von
vorne anfangen müsste.
Aber gerade auf uCs und DSPs ist C doch immer noch mit Abstand am
weitesten verbreitet.
Zu Recht, würde ich annehmen. Oder wie siehst du das?
Thomas schrieb:> Aber gerade auf uCs und DSPs ist C doch immer noch mit Abstand am> weitesten verbreitet.
Ja, leider
> Zu Recht, würde ich annehmen. Oder wie siehst du das?
Nein, zu Unrecht; C++ ist sicherer, einfacher, kompakter, eleganter,
konsistenter, und effizienter. Der Hauptgrund warum hier noch C
verwendet wird ist Unkenntnis und obskure Plattformen ohne verfügbare
C++ Compiler.
Dr. Sommer schrieb:> Angenommen man schreibt so etwas wie ein Archivprogramm, man erhält beim> Entpacken einen Dateinamen aus der Archivdatei der so aussieht:> "hallo.exe\0.doc\0". Mit etwas Pech zeigt die GUI des Archivprogramms> den kompletten Namen inklusive .doc an, aber beim Speichern der Datei> auf die Platte wird der String aufgrund genau einer solchen> Stringfunktion bei dem 0-Byte abgeschnitten
Vielleicht liege ich ja daneben, aber ich glaube das ist ein
Trugschluss. Die Kombination '\0' zur Kennzeichnung der Null gibt's doch
nur im Quelltext. Falls jetzt so eine Zeichenkette, die man von
irgendwoher bekommt, diese Kombination enthält, wird dort auch weiterhin
ein '\' und ein '0' stehen. Denke ich mal.
Ralf G. schrieb:> wird dort auch weiterhin> ein '\' und ein '0' stehen. Denke ich mal.
Das meinte ich doch, der String war in C-Schreibweise. Das heißt ein
String wo mitten drin ein 0-Byte steht. Hätte ich "hallo.exe0.doc0"
geschrieben hätte keiner gewusst was gemeint ist...
Ralf G. schrieb:> Wer macht denn sowas?????
Wie in dem Beispiel, ein so genannter "Hacker". Oder Leute die
Binärdaten verarbeiten zur Speicherung in Dateien oder Transfer über
Netzwerk.
Naja, etwas an den Haaren herbeigezogen...
Dr. Sommer schrieb:> Wie in dem Beispiel, ein so genannter "Hacker".
Fix in Großbuchstaben umgewandelt und zack, der Hack funktioniert nicht
mehr. Ist doch gut! ;-)
> Oder Leute die> Binärdaten verarbeiten zur Speicherung in Dateien oder Transfer über> Netzwerk.
Dann sind das aber keine Strings mehr. Was nützt mir eine Funktion, die
von allen Zahlen in einen Array(!!) 32 abziehen kann?
Ralf G. schrieb:> Fix in Großbuchstaben umgewandelt und zack, der Hack funktioniert nicht> mehr. Ist doch gut! ;-)
Doch, genau deswegen funktioniert der Hack, weil aus der "harmlosen"
hallo.exe.doc eine hallo.exe wird. Das ist natürlich kein direktes
Problem, kann aber eines werden...
Ralf G. schrieb:> Dann sind das aber keine Strings mehr. Was nützt mir eine Funktion, die> von allen Zahlen in einen Array(!!) 32 abziehen kann?
Da ist diese konkrete Funktion etwas nutzlos, ja. Aber andere
String-Funktionen kann man gut darauf anwenden - wenn sie mit 0-Bytes
klarkommen.
Dr. Sommer schrieb:> Da ist diese konkrete Funktion etwas nutzlos, ja. Aber andere> String-Funktionen kann man gut darauf anwenden - wenn sie mit 0-Bytes> klarkommen
Das ist doch eine akademische Diskussion... C-Strings sind so definiert,
dass sie Null-Terminiert sind, hätte und könnte zählt da nicht. Wenn der
"String" nicht Null-Terminiert ist, dann ist es kein C-String und es
steht dir frei alle C-Stringfunktionen nochmal neu für deine Strings zu
schreiben. Spätestens bei strlen wird es abstrus wenn du der Funktion
die Länge des Strings mitgeben musst damit diese den Wert gleich wieder
zurückliefert...
Thomas schrieb:> Wie würdest du es denn machen?
Direkt auf dem Array arbeiten, eine Kopie ist hier unnötig und schränkt
die Verwendung ein, wenn der Aufrufer unbedingt die
Orginalzeichenkette behalten will, so kann er sie selber kopieren.
Das ganze läßt sich dan etwa so zusammenschrumpfen[1]:
1
voidStringToUpper(char*zeile){
2
intlen=strlen(zeile);
3
for(inti=0;i<len;i++){
4
zeile[i]=toupper(zeile[i]);
5
}
6
}
Man könnte das jetzt noch in eine einzige Zeile mit while "schrumpfen"
und würde dann auch ohne strlen auskommen... So ist es aber erst mal
lesbarer.
Nun sieht das natürlich nicht mehr ausreichen monströs und komplex aus
um zu zeigen wie böse die Sprache X ist ;-)
Allgemein kann man sagen: In jeder Sprache kann ich sch...okoladen code
schreiben wenn ich mich nur genug anstrenge oder mich zu wenig auskenne.
Das kann man aber nicht primär der Sprache anlasten, bestenfalls kann
eine Sprache mich unterstützen sowas nicht zu tun, verhindern aber
seltenst.
[1] http://www.tutorialspoint.com/ansi_c/c_toupper.htm
Frank M. schrieb:> Dein Aufruf von strlen() ist überflüssig :-)
Siehe mein Kommentar am ende, bitte wenigstens die Beiträge bis zu ende
lesen:
Läubi .. schrieb:> Man könnte das jetzt noch in eine einzige Zeile mit while "schrumpfen"> und würde dann auch ohne strlen auskommen... So ist es aber erst mal> lesbarer.Oliver S. schrieb:> Na, wenn schon, dann noch so
Wie gesagt, eine Anweisung reicht ;-)
Naja, wenn's jetzt primär um's Golfen geht, ist C vielleicht nicht die
richtige Ausrüstung dafür. So ist bspw. in Haskell der Funktionsrumpf
noch kürzer als der Name:
1
stringToUpper = map toUpper
Und das ist kein schlechter, sondern sogar sehr guter Stil, für jeden im
Club sofort verständlich (weil es keiner anders machen würde) und frei
von jeglichen undefined Behaviors :)
Läubi .. schrieb:> Das ist doch eine akademische Diskussion... C-Strings sind so definiert,> dass sie Null-Terminiert sind, hätte und könnte zählt da nicht.
Das ist natürlich praktisch. Wenn man dann doch mal Char-Arrays mit
0-Bytes verarbeiten will muss man "nur" alle Funktionen neu schreiben
und leicht anpassen. Und jedes mal beim Einlesen/Empfangen von "fremden"
Strings genau überlegen ob ein bösartig eingefügtes 0-Byte nicht doch
Schaden anrichten könnte (wie es beim PHP-Interpreter, wimre, der Fall
war, der intern Strings immer korrekt mit Längenangabe verwendete aber
beim Aufruf von C-Stdlib-Funktionen plötzlich gemeine Probleme bekam).
> Spätestens bei strlen wird es abstrus wenn du der Funktion> die Länge des Strings mitgeben musst damit diese den Wert gleich wieder> zurückliefert...
Die Idee ist natürlich strlen ganz wegzulassen und die Länge immer
explizit zu haben. Wie es ziemlich viele Sprachen machen... Hätte man
damals ein
1
structString{char*data;size_tlength;};
in den Standard aufgenommen wäre alles halb so schlimm gewesen, aber zu
spät.
Hier mal ein interessanteres Beispiel über "gute" Programmiersprachen
(C++) und Code-Wiederverwendbarkeit:
In test1() wird ein Array aus structs angelegt, initialisiert und dann
durchiteriert. Später entscheidet man sich um, doch lieber eine
verkettete Liste zu verwenden, und muss nur die Typangabe auf
std::list ändern, und der gesamte restliche Code kann genau so bleiben
wie er ist und funktioniert immer noch (siehe test2). In test3 das
gleiche Spiel noch einmal für einen vector, eine Art automatisch
wachsendes Array mit dabei ammortisiert konstanter Laufzeit. Da der
Compiler an jeder Stelle den Typ kennt und genau weiß was getan werden
muss, ist das mindestens so effizient wie äquivalenter C-Code. Das ganze
ist in C quasi unmöglich, Pascal weiß ich nicht, und überall verwendbar
wo es einen GCC gibt...
Für jedes hätte, wäre, könnte, müsste, ist es bei C einfach ein paar
Jahrzehnte zu spät.
Und C++ als "gute" Sprache zu bezeichnen, ist auch gewagt. Dazu gibt's
auch da zu viele Unschönheiten.
Oliver
Dr. Sommer schrieb:> Läubi .. schrieb:>> Das ist doch eine akademische Diskussion... C-Strings sind so definiert,>> dass sie Null-Terminiert sind, hätte und könnte zählt da nicht.> Das ist natürlich praktisch.
Das ist überhaupt nicht praktisch. Das ist eine reine Definitionssache.
Du kannst dir natürlich deine eigene Defintion machen, was für dich ein
String sein soll, nur nenne es dann nicht C-String. Denn damit ist
gegenseitiges aneinander vorbeireden vorprogrammiert.
> Wenn man dann doch mal Char-Arrays mit> 0-Bytes verarbeiten will muss man "nur" alle Funktionen neu schreiben> und leicht anpassen.
Ja und?
In C ist definiert, was ein C-String ist und welche Eigenschaften er
hat. Diese definierten Eigenschaften schliessen das enthalten sein eines
0 Bytes aus.
> explizit zu haben. Wie es ziemlich viele Sprachen machen... Hätte man> damals ein
1
structString{char*data;size_tlength;};
in den
> Standard aufgenommen wäre alles halb so schlimm gewesen, aber zu spät.
Es hat alles seine Vor und Nachteile.
So schleppe ich einen size_t mit, der auch Speicher verbraucht.
Dr. Sommer schrieb:> Wenn man dann doch mal Char-Arrays mit 0-Bytes verarbeiten will muss man> "nur" alle Funktionen neu schreiben und leicht anpassen.
Wo ist das Problem? Char-Arrays, die beliebige Daten enthalten, sind
halt keine Strings. Punkt. Die C-Runtime-Library enthält diverse
Funktionen zum Verarbeiten von Strings.
Wenn man irgendwelche eigenen Datentypen definiert, dann muss man
Funktionen, die damit irgendwas anstellen sollen, tatsächlich anpassen
oder gar neu schreiben.
Deine "string"-Struktur übrigens ist auch nicht besonders praktisch; was
ist, wenn so ein String verkürzt oder verlängert werden soll?
Informationen über den maximal zur Verfügung stehenden Speicher sind
nicht enthalten, wie also würdest Du das in C angehen?
Zeig mal musterhaft, wie Du strncat implementieren würdest.
Oliver S. schrieb:> Für jedes hätte, wäre, könnte, müsste, ist es bei C einfach ein paar> Jahrzehnte zu spät.
Wenn das wenigstens allgemein akzeptiert würde und man C in Friede ruhen
lassen würde...
> Und C++ als "gute" Sprache zu bezeichnen, ist auch gewagt. Dazu gibt's> auch da zu viele Unschönheiten.
Das stimmt. Aber C++ ist die eleganteste Sprache in der Effizienzklasse
der klassischen maschinennahen Sprachen inkl. C, Pascal etc. Und dank
der letzten Neuerungen kann man auf viele Altlasten verzichten...
Karl Heinz Buchegger schrieb:> Das ist überhaupt nicht praktisch. Das ist eine reine Definitionssache.
Ich vergaß die Leidenschaftslosigkeit der C-Programmierer "Hauptsache es
läuft irgendwie, wurst wie hässlich, fehleranfällig, unwartbar es ist".
Du hast natürlich recht, aber das ganze ist ein Grund C nicht zu
verwenden...
Karl Heinz Buchegger schrieb:> Es hat alles seine Vor und Nachteile.> So schleppe ich einen size_t mit, der auch Speicher verbraucht.
Der Speicher ist typischerweise billiger als das ständige strlen() und
überprüfen auf '0'.
Dr. Sommer schrieb:> wachsendes Array mit dabei ammortisiert konstanter Laufzeit. Da der> Compiler an jeder Stelle den Typ kennt und genau weiß was getan werden> muss, ist das mindestens so effizient wie äquivalenter C-Code. Das ganze> ist in C quasi unmöglich, Pascal weiß ich nicht, und überall verwendbar> wo es einen GCC gibt...
Du rennst hier bei vielen offene Türen ein.
Nur die wenigsten werden dem Argument, dass C++ bessere Möglichkeiten
als C zur Verfügung hat, etwas wirklich großartiges entgegensetzen. Vor
allen Dingen dann nicht, wenn sie selber C++ programmieren.
Aber das ändert nichts daran, was ein C-String ist und wie er definiert
ist. Wenn es in der Aufgabenstellung um C-Strings geht, dann geht es um
C-Strings. Und da ist dann deine private String-Definition ganz einfach
fehl am Platze.
Dr. Sommer schrieb:>> So schleppe ich einen size_t mit, der auch Speicher verbraucht.> Der Speicher ist typischerweise billiger als das ständige strlen() und> überprüfen auf '0'.
Ähm.
90% der C-String-Arbeit kommt gänzlich ohne strlen aus.
Bernd Leitenberger in dem anfangs verlinkten Artikel (und um den geht es
ja im weitesten Sinne immer noch) ist das krasse Beispiel eines
Programmierers, der eben nicht String Aufgaben mit Pointer löst, obwohl
dies problemlos möglich wäre. Er benutzt eben nicht das typische C-Idiom
um etwas mit jedem Character eines Strings zu machen, sondern erfindet
sein eigenes umständliches Süppchen und kreidet das dann der Sprache an.
Er arbeitet in seinem C Programm nicht mit der C Systematik der String
Behandlung sondern gegen sie.
Und nur damit das nicht in den falschen Hals kommt:
Ja, C-Strings sind nicht das Gelbe vom Ei.
Ja, in C gibt es einige Themenkreise, die ein 'Pain in the ass' sind, um
es freundlich auszudrücken. Das wird wohl keiner abstreiten oder schön
reden wollen.
Karl Heinz Buchegger schrieb:> haben undefiniertes Verhalten
Deshalb hab ich ja auch die Korrekte und lesbare Variante vorgeschlagen,
danke für den Hinweis, da ich solche "Tricks" nicht nutze ist diese
ganze Thematik nicht 100% präsent im Kopf :-)
Rufus Τ. Firefly schrieb:> Deine "string"-Struktur übrigens ist auch nicht besonders praktisch; was> ist, wenn so ein String verkürzt oder verlängert werden soll?
Mir ist das völlig klar, diese Struktur hätte mehr einen psychologischen
Effekt: Es gäbe einen einheitlichen Weg Strings mit Länge zwischen
verschiedenen Programmen&Libraries auszutauschen, und würde die
Implementation von strncat & Friends vereinfachen, aber nicht obsolet
machen.
> Zeig mal musterhaft, wie Du strncat implementieren würdest.
Jetzt schreibe ich schon wieder hässliches C, wenns denn sein muss:
1
#include<stddef.h>
2
#include<stdio.h>
3
#include<stdlib.h>
4
5
#define LSTR(x) { x, sizeof(x)-1 }
6
7
typedefstruct{
8
char*data;
9
size_tlength;
10
}String;
11
12
StringmyCat(constStringa,constStringb){
13
size_tlen=a.length+b.length;
14
Stringres={malloc(len),len};
15
for(size_ti=0;i<a.length;i++){
16
res.data[i]=a.data[i];
17
}
18
for(size_ti=0;i<b.length;i++){
19
res.data[a.length+i]=b.data[i];
20
}
21
returnres;
22
}
23
24
intmain(){
25
constStringa=LSTR("Hel\0lo "),b=LSTR("World!\n");
26
Stringres=myCat(a,b);
27
fwrite(res.data,1,res.length,stdout);
28
free(res.data);
29
}
Das 0-Byte sieht man am normalen Terminal nicht, aber es wird
übertragen, bei Umleitung in eine Datei oder ein Socket etc. ist es da.
Ich muss mir leider mit einem ekligen Makro zur Definition des
Literal-"String" behelfen, da C keine templates hat.
Karl Heinz Buchegger schrieb:> 90% der C-String-Arbeit kommt gänzlich ohne strlen aus.
Außer strcat was 90% der Stringfunktion-Aufrufe ausmacht? :D
Programmierer aller herren Programmiersprachen sind immer so enttäuscht
wenn "Hello" + "World" nicht geht...
Karl Heinz Buchegger schrieb:> Er benutzt eben nicht das typische C-Idiom
Gehören die chronischen Buffer-Overflows auch zu den C-Idiomen? ;-) Ja
ich weiß, die ist der Programmierer schuld. Aber eine Sprache kann dabei
helfen, so etwas zu vermeiden; C tut das nicht.
Karl Heinz Buchegger schrieb:> Das wird wohl keiner abstreiten oder schön> reden wollen.
Sag das mal Mr. Torvalds und seiner Gefolgschaft aus renitenten
C-Programmierern die Unmengen an geradezu gefährlichem Copypasta-C-Code
produzieren... ;-D
Dr. Sommer schrieb:> Jetzt schreibe ich schon wieder hässliches C, wenns denn sein muss:
Naja, und anders als strncat kopierst Du beide Strings um.
Effizient ist das nicht.
Außerdem verwendest Du dynamische Speicherverwaltung, was strncat
nicht macht.
Wenn schon dynamische Speicherverwaltung, warum dann nicht realloc?
Rufus Τ. Firefly schrieb:> Naja, und anders als strncat kopierst Du beide Strings um.> Effizient ist das nicht.
Das stimmt. Könnte man anders machen. Oder std::string verwenden, da ist
das nämlich alles schon so implementiert.
Rufus Τ. Firefly schrieb:> Außerdem verwendest Du dynamische Speicherverwaltung, was strncat> nicht macht.
Dafür funktioniert es für alle String-Längen und overflowt nicht
plötzlich (wir erinnern uns, #1 oder #2 der Sicherheitslücken (vor oder
nach SQL-Injection?))... Man könnte diese Funktion natürlich leicht
erweitern, bei Bedarf mehr Speicher anzufordern. Das kann strncat
nicht... Und überhaupt, strncat, das ist doch viel zu "sicher", strcat
ist doch viel schönerer C-Style.
Rufus Τ. Firefly schrieb:> Wenn schon dynamische Speicherverwaltung, warum dann nicht realloc?
Hatte ich nicht dran gedacht - wäre auch ungesund wenn "data" nicht das
Ergebnis von "malloc" war.
Dr. Sommer schrieb:> Könnte man anders machen. Oder std::string verwenden, da ist das nämlich> alles schon so implementiert.
Wir reden von C.
> Dafür funktioniert es für alle String-Längen
Das macht strncat auch.
> und overflowt nicht plötzlich
Das macht strncat auch nicht.
> Man könnte diese Funktion natürlich leicht erweitern, bei Bedarf mehr> Speicher anzufordern. Das kann strncat nicht...
Nö, aber das soll es ja auch nicht.
Im Embedded-Bereich, auf µCs, die teilweise nur wenige hundert Bytes RAM
haben, möchte man weder dynamische Speicherverwaltung verwenden, noch
irgendwelche Funktionen nutzen, die von sich aus auf die Idee kommen,
Speicher anzufordern.
strncat aber kann auch dort sehr gut verwendet werden.
Rufus Τ. Firefly schrieb:> Dr. Sommer schrieb:>> Könnte man anders machen. Oder std::string verwenden, da ist das nämlich>> alles schon so implementiert.>> Wir reden von C.
Ich denke, genau hier liegt der springende Punkt.
Dr. Sommer möchte die Diskussion auf C++ ausdehnen. Wogegen ich
prinzipiell nichts hätte, wenn sich dieser Thread nicht explizit auf C
und String-Verarbeitung in C beziehen würde.
> Buffer Overflow .... so etwas zu vermeiden; C tut das nicht.
Natürlich nicht. Das war auch nie die Idee hinter C, das man ein
Rundum-Sorglos-Paket schnürt. C war von Anfang an nach der Maxime
entworfen: You don't get, what you didn't ask for.
> Wogegen ich prinzipiell nichts hätte, wenn sich dieser Thread nicht> explizit auf C und String-Verarbeitung in C beziehen würde.
Kein Problem.
Meine Frage ist ja geklärt.
Ich gebe hiermit den Thread für C++ Diskussionen frei ;)
Wer bei C bleiben will, aber dennoch Komfort-Strings haben will, der
kann sich ja der GLib (nicht glibc) bedienen, da ist dies uvm.
implementiert. Mit GObject hat's sogar OOP.
Interessant, wie funktioniert der Overflow-Schutz zB hier?
Rufus Τ. Firefly schrieb:> Im Embedded-Bereich, auf µCs, die teilweise nur wenige hundert Bytes RAM> haben, möchte man weder dynamische Speicherverwaltung verwenden, noch> irgendwelche Funktionen nutzen, die von sich aus auf die Idee kommen,> Speicher anzufordern.
Das stimmt sogar. Aber auch hier kann ein Overflow unschöne Dinge
anrichten, weswegen man die Längen überprüfen sollte, wofür man sie
überhaupt erst einmal braucht, wärend man die maximale Kapazität eines
gegebenen C-Strings (=char*) überhaupt nicht ermitteln kann.
Karl Heinz Buchegger schrieb:> Wogegen ich> prinzipiell nichts hätte, wenn sich dieser Thread nicht explizit auf C> und String-Verarbeitung in C beziehen würde.
Dabei eignet sich das so schön die Vorteile von C++ zu zeigen...
Karl Heinz Buchegger schrieb:> Natürlich nicht. Das war auch nie die Idee hinter C, das man ein> Rundum-Sorglos-Paket schnürt.
Blöderweise fangen immer noch viele mit C an, erwarten eine Sorglospaket
und fallen auf die Nase, bzw lassen den Kunden auf die Nase fallen.
Ich finde es daher legitim die Nachteile von C aufzuzeigen um die Leute
zu ermutigen etwas besseres zu verwenden.
Lukas K. schrieb:> Wer bei C bleiben will, aber dennoch Komfort-Strings haben will, der> kann sich ja der GLib (nicht glibc) bedienen, da ist dies uvm.> implementiert. Mit GObject hat's sogar OOP.
Das ist großartig, weil man C++ "irgendwie nicht mag" alles in C
nachzubauen. Das "OOP" in GObject ist ausschließlich dazu da, um ein
generisches OOP-Interface für viele Sprachen, wie Scriptsprachen,
bereitzustellen. Das ist nicht dazu gedacht, um damit in C
OOP-Anwendungsentwicklung zu betreiben. Wer es dennoch macht und C++
ablehnt braucht eine Jacke die hinten zugeknöpft wird...
Dr. Sommer schrieb:> Karl Heinz Buchegger schrieb:>> Natürlich nicht. Das war auch nie die Idee hinter C, das man ein>> Rundum-Sorglos-Paket schnürt.> Blöderweise fangen immer noch viele mit C an, erwarten eine Sorglospaket> und fallen auf die Nase, bzw lassen den Kunden auf die Nase fallen.
Da bin ich wiederrum bei dir.
Wobei ich das sogar noch ausweiten würde.
Blöderweise lernen mittlerweile viele ihre Programmiersprache nicht mehr
ordentlich und systematisch, EHE sie sich damit in Projekte wagen. Man
kann schon mit C arbeiten - aber man muss halt eben auch viel rundherum
wissen und vorher geübt haben.
Dr. Sommer schrieb:> Interessant, wie funktioniert der Overflow-Schutz zB hier?
Wenn Du strncatfalsch aufrufst, dann funktioniert er nicht.
Ich denke, daß es müßig ist, mit Dir weiterzudiskutieren.
Interessant, wie funktioniert der Overflow-Schutz zB hier?
Du versuchst, "Fehler" in C zu hereinzuinterpretieren, die in C++
vielleicht/bestimmt [ich habe keine Ahnung von C++] solche wären, es in
C aber einfach nicht sind, denn irgendwo steht zu strncat etwas wie
"the user needs to provide sufficient space in dest for the result".
Ist das nicht der Vorteil von C, dass die paar Befehle und die ganzen
Bibliotheksfunktionen ohne Netz und doppelten Boden zur Verfügung
gestellt werden? Die Sicherheit reinzubringen, ist Sache des
Programmierers. Sonst kann man ja bei BASIC bleiben.
Fred S. schrieb:> Du versuchst, "Fehler" in C zu hereinzuinterpretieren, die in C++> vielleicht/bestimmt [ich habe keine Ahnung von C++] solche wären
Der springende Punkt ist, dass sich die std::string Klasse selbst intern
um so banale Dinge wie Speicherverwaltung kümmert. D.h. dort hat man
damit nichts zu tun
1
intmain()
2
{
3
std::stringa("Hello");
4
5
a+=" cruel C-World!";
6
}
ist ja auch sinnvoll, wenn man C++ programmiert und man sich dynamische
Speicherverwaltung erlauben kann. Gerade letzteren Luxus hat man halt
auf einem µC nicht immer.
Die ganze Diskussion ist dahingehend müssig. C++ hat einen anderne
Ansatz als C und ist ein ganzes Stück jünger. Es macht keinen Sinn ein
heutiges Auto mit einem Ford Model T zu vergleichen. Beim Mdel T hat man
selbst noch händisch den Scheibenwischer betätigt wenn es regnet.
Heutige Autos haben eine Automatik, die Regentropfen auf der Scheibe
erkennt und selbsttätig den Scheibenwischer aktiviert, so dass sich der
Fahrer darum nicht mehr kümmern muss.