wenn ich char name[1];
definiere, sollte das char nicht länger als 2 Zeichen sein können, aber
wieso kann ich auch mehr Zeichen einlesen und Ausgeben?!
Schreibe ich nur
char name;
funktioniert es nicht...
Pail P. schrieb:> wenn ich char name[1];> definiere, sollte das char nicht länger als 2 Zeichen sein können, aber> wieso kann ich auch mehr Zeichen einlesen und Ausgeben?!
Nein, in name[1] hat nur 1 Zeichen platz. Wenn du da mehr rein
schreibst, überschreibst du teile des Stacks. Das ist dann ein
Bufferoverflow. Danach kann alles mögliche passieren, es kann auch sein,
dass man das erstmal nicht merkt, das ist reine Glückssache.
Klassischer Buffer Overflow. Gib mal einen langen Namen ein.
Der Compiler richtet den Stack auf 8(oder mehr?) Bytes aus, weil dann
die Zugriffe schneller sind.
Pail P. schrieb:> wenn ich char name[1];> definiere, sollte das char nicht länger als 2 Zeichen sein können,
Nein ein Zeichen.
Das wäre bei einem C-String allerdings schon das '\0' (Stringende)
> aber> wieso kann ich auch mehr Zeichen einlesen und Ausgeben?!
Weil scanf über die Größe des dahinterliegenden Platz keine Kenntnis
hat.
Du kannst aber die Länge im Formatstring mit angeben.
Da ist aber der Platz für die '\0' nicht mit drin.
1
scanf("%0s",name);// das & vor Name ist auch nicht nötig
Geht - geht nicht - geht - geht nicht...
Ob es geht ist, Glücksache, aber auf keinen Fall sollte man es machen.
name ist ein Zeichen, name[1] ein (viel zu kleiner)Speicherbereich und
zugleich ein Pointer auf diesen Bereich.
scanf ist eine Fehlkonstruktion, die man am besten garnicht verwendet,
schon garnicht, um einen String einzulesen.
Da man nie weiß, wie lang eine Eingabe ist, braucht es eine Begrenzung
auf den benutzten Puffer. Man kann es zB mit fgets(buf,
sizeof(buf),stdin);
Dirk B. schrieb:> scanf("%0s",name); // das & vor Name ist auch nicht nötig
Das ist nicht optional sondern schlichtweg falsch und wird vom Compiller
auch moniert:
1
$ cat s.c
1
#include<stdio.h>
2
#include<stdlib.h>
3
4
voidAbfrage()
5
{
6
charname[1];
7
printf("Wie heisst Du?:");
8
scanf("%s",&name);
9
printf("Hallo %s",name);
10
getchar();
11
}
12
13
intmain()
14
{
15
Abfrage();
16
return0;
17
}
1
$ gcc s.c && ./a.out
2
s.c: In function ‘Abfrage’:
3
s.c:8:11: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[1]’ [-Wformat=]
Pail P. schrieb:> wenn ich char name[1];> definiere, sollte das char nicht länger als 2 Zeichen sein können, aber> wieso kann ich auch mehr Zeichen einlesen und Ausgeben?!
Was wäre das von dir erwartete Verhalten gewesen?
In Standard-Sprech hast du undefiniertes Verhalten hervorgerufen. Und
das kann alles sein, auch dass der Code zu funktionieren scheint.
Effektiv hast du in Speicher gespuckt, der dir nicht gehört.
Pail P. schrieb:> "Was wäre das von dir erwartete Verhalten gewesen?"> Ein Compilerfehler..und das es nicht kompiliert wird mit der> Fehlermeldung "String too long"
Und woher weiß der Compiler die Länge des Strings? Die ist doch erst
bekannt, wenn das Programm schon läuft.
Pail P. schrieb:> Warum muss es dann mal mit & und mal ohne & sein?
Weil name ein Array ist und automatisch in einen Zeiger auf sein erstes
Element zerfällt.
Tu dir einen Gefallen und arbeite ein gutes Buch über C durch. Mit
Trial&Error und Nachfrage jedes einzelnen Details im Forum wird es für
dich und für uns extrem mühsam.
leo schrieb:> I.e. das Programm schreibt auf die Adresse von name.
Nein. Die Adresse von Name steht nirgendwo im Speicher. Falsch ist es
aber trotzdem, denn:
Pail P. schrieb:> Warum muss es dann mal mit & und mal ohne & sein?
"name" ist ein Array, und wenn man Arrays übergibt werden sie
automatisch zu Pointern auf das 0. Element ("decay"). scanf erwartet
genau so einen Pointer. &name hingegen ist ebenfalls ein Pointer, aber
auf das ganze Array, nicht auf das 0. Element. Die Adresse, die drin
steht, ist die selbe wie bei der Übergabe von "name", aber der Zeiger
ist vom Typ des Arrays (char (*)[1]), und nicht vom Typ der Elemente
(char*). Genau das sagt auch o.g. Warnung. Da in C Typen nur den
Compiler interessieren, und zur Laufzeit verschwinden, würde es trotz
Warnung doch funktionieren, wenn das Array groß genug wäre. Der Compiler
sieht das aber nicht und warnt, daher ist &name falsch.
&name[0] wäre übrigens auch richtig, denn das ist ja ebenfalls ein
Zeiger auf das 0. Element. Ist aber umständlicher.
Pail P. schrieb:> "> Und woher weiß der Compiler die Länge des Strings?">> Wie in anderen Sprachen auch, anhand seiner Definition...
Wenn du ein Array definierst, weiß der Compiler natürlich dessen Größe
(also Anzahl der enthaltenen Elemente). Bei einem Pointer ist das nicht
der Fall, denn diesem könnte beispielsweise erst zur Laufzeit dynamisch
Speicher zugewiesen werden:
1
charname[123];// sizeof( name ) = 123 - Der Compiler kennt die Größe
2
char*p=NULL;// sizeof( p ) = 4
3
p=name;// sizeof( p ) = 4
4
p=newchar[64];// sizeof( p ) = 4
Ein "sizeof( pointer )" ergibt daher immer die Größe des Pointers
selbst, nicht die des Objekts auf die er zeigt. Bei einem 32 Bit System
sind dies 4 Byte, bei 64 Bit eben 8 Byte.
Die Funktion "scanf" erwartet für jedes Element das sie einlesen soll
einen Pointer auf eine Speicheradresse in der das Ergebnis abgelegt
werden soll. Da "scanf" aber eben nur die Speicheradressen (also
Pointer) sieht, fehlt jegliche Information über die Speichergöße die
sich hinter dem Pointer verbirgt. "scanf" geht daher davon aus das du
genügend Speicher allokiert hast um das Ergebnis darin zu speichern.
Pail P. schrieb:> "> Und woher weiß der Compiler die Länge des Strings?">> Wie in anderen Sprachen auch, anhand seiner Definition...
Man muss in C aber nicht in ein Array schreiben.
Du kannst auch scanf_s benutzen.
Da musst du als Programmierer aber die Größe mit übergeben.
Ein Fehler wird da nicht gemeldet.
Es wird einfach aufgehört einzulesen.
Du kannst auch eine andere Programmiersprache benutzen, die freundlicher
zu Anfängern ist.
Pail P. schrieb:> "Was wäre das von dir erwartete Verhalten gewesen?"> Ein Compilerfehler..und das es nicht kompiliert wird mit der> Fehlermeldung "String too long"
C kennt keine Strings, weshalb der Compiler auch nicht meckern kann. C
kennt noch nicht einmal die Länge des Zeichenarrays. Der Compiler
reseviert bei der Deklaration des Zeichenarrays lediglich den
Speicherplatz. Ansonsten muß der Programmierer dafür sorgen, daß nicht
in irgendwelche Speicherbereiche geschrieben wird.
Pail P. schrieb:> "> Und woher weiß der Compiler die Länge des Strings?">> Wie in anderen Sprachen auch, anhand seiner Definition...
Der String kommt von der Eingabe. Wie soll ein Compiler wissen, wieviele
Zeichen von einem User irgendwann mal eingegeben werden?
Das können auch Compiler anderer Sprachen nicht wissen. Es ist Sache des
Programmierers, den Schaden bei unzulässigen Eingaben zu begrenzen.
Zeno schrieb:> C> kennt noch nicht einmal die Länge des Zeichenarrays.
Doch, kennt er. Sonst würde das sizeof(array) nicht funktionieren.
Was er nicht kennt, ist die Größe des zugehörigen Speichers, auf den ein
Pointer verweist.
> Der Compiler> reseviert bei der Deklaration des Zeichenarrays lediglich den> Speicherplatz.
Eine Deklaration ist nur eine Information, das ein Objekt existiert.
Erst bei der Definition wird auch Speicherplatz belegt.
Was mir auch gerade auffällt:
Pail P. schrieb:> void Abfrage()
Ist falsch, bzw. wahrscheinlich nicht das was gemeint ist: Eine Funktion
ohne Parameter-Deklaration kann eine beliebige Anzahl beliebiger
Parameter bekommen. Um eine Funktion zu definieren, welche gar keine
Parameter bekommt, muss man in C schreiben:
1
voidAbfrage(void){
In C++ kann man sich die Fummelei mit der maximalen String-Länge sparen
und eine "echte" String-Verarbeitung nutzen:
1
#include<string>
2
#include<iostream>
3
4
voidAbfrage()
5
{
6
std::stringname;// String anlegen
7
std::cout<<"Wie heisst Du?:";// Ausgabe
8
std::getline(std::cin,name);// Eingabe bis zum Zeilenumbruch einlesen
9
std::cout<<"Hallo "<<name<<std::endl;// Ausgeben
10
std::cin.ignore();// Ein Zeichen einlesen und ignorieren (warte auf Tastendruck)
11
}
12
13
intmain(){
14
Abfrage();
15
return0;
16
}
getch() ist eine Windows-eigene Funktion; ignore() ist portabel. C++
allokiert automatisch genug Speicher, um die gesamte Eingabe
aufzunehmen. Es kann im Gegensatz zur Nutzung von scanf() hier nicht
passieren, dass über die Arraygrenze hinweg geschrieben wird und somit
eine Sicherheitslücke entsteht (Buffer Overflow). Die Eingabe kann
höchstens so lang sein, dass der Speicher voll läuft, wodurch das
Programm dann "sauber" abstürzt (std::bad_alloc).
Niklas G. schrieb:> Dirk B. schrieb:>> Das Element mit drm Index 0 ist das 1. Element im Array>> Das ist irgendwie unklar.
Das 1. Lebensjahr beginnt direkt nach der Geburt, da ist man aber 0
Jahre alt.
Ähnlich ist es mit dem Zaunpfahlfehler.
Dirk B. schrieb:> Das 1. Lebensjahr beginnt direkt nach der Geburt, da ist man aber 0> Jahre alt.
Im deutschen Sprachgebrauch ja. Im Programmier-Kontext würde ich die
Unterscheidung weglassen und direkt vom "0. Element" reden.
Niklas G. schrieb:> Im Programmier-Kontext würde ich die Unterscheidung weglassen und direkt> vom "0. Element" reden.
Das halte ich für äußerst ungeschickt. Wenn nämlich jemand, der so
redet, vom ersten Element redet, meint er tatsächlich das zweite, was
wiederum jemand, der nicht so redet, missversteht.
Nein, sinnvoller dürfte es sein, vom "Element mit dem Index 0" zu reden.
Das ist eindeutig und bietet keinen Interpretationsspielraum.
Pail P. schrieb:> "> Und woher weiß der Compiler die Länge des Strings?">> Wie in anderen Sprachen auch, anhand seiner Definition...
Stellst du dich mit Absicht dumm? Was der Compiler höchstens wissen
kann, ist, wie groß der Puffer ist, in den der Text eingelesen werden
soll. Aber woher wissen diese anderen Sprachen denn, wieviel Text du
später, nach dem Compilieren, wenn das Programm läuft, dann eingeben
wirst? Können die in die Zukunft schauen?
Rufus Τ. F. schrieb:> Niklas G. schrieb:>> Im Programmier-Kontext würde ich die Unterscheidung weglassen und direkt>> vom "0. Element" reden.>> Das halte ich für äußerst ungeschickt. Wenn nämlich jemand, der so> redet, vom ersten Element redet, meint er tatsächlich das zweite, was> wiederum jemand, der nicht so redet, missversteht.
Ja. Für mich wäre das erste Element immer das, mit dem es anfängt. Dass
es sinnvoller wäre, mit der 0 zu zählen anzufangen, mag sein, wird aber
eben außerhalb der Programmierung meistens nicht gemacht (mit ein paar
Ausnahmen).
"Stellst du dich mit Absicht dumm? "§
nä, aber Du tust dich offenbar sehr schwer...
es geht darum das er länger überhaupt zulässt..das hast Du offenbar
nicht begriffen.
Wenn die Stringlänge von 20 Zeichen definiert ist, sollten auch max 19
Zeichen + \0 zulässig sein.
Wie eben in Basic oder Pascal z.B. und nicht wie in C das er dann
einfach Speicherbereiche ungefragt überschreibt.
Wie oben von anderen genannt, kann man die Eingabelänge von Scanf
begrenzen, ist zumindest ein Ansatz...
Pail P. schrieb:> printf("Wie heisst Du?:");> scanf("%s",&name);
Mal was anderes gefragt: Funktioniert bei euch solche Eingabe überhaupt?
Ohne fflush(stdout) nach printf wird bei mir zuerst der Name eingelesen.
Dann kommt die Frage "wie heißt du?".
Nochmal: bei %s kommt kein & an den
Variablennamen.
Und ja, es funktioniert.
Das ist ein Problem deiner Konsole.
Ein \n beim printf könnte helfen.
Benutzt du Eclipse?
Dirk B. schrieb:> Nochmal: bei %s kommt kein & an den> Variablennamen.>> Und ja, es funktioniert.>> Das ist ein Problem deiner Konsole.
Nein, das ist kein Problem, sondern im Standard vorgesehen. Ein Stream
kann "unbuffered", "line buffered" oder "fully buffered" sein. In der
Praxis ist stdout zur Optimierung meist das zweite, und dann kommt genau
dieses Verhalten.
> Ein \n beim printf könnte helfen.
Das fügt aber einen zusätzlichen Zeilenumbruch ein. fflush(stdout) ist
schon genau das richtige hier.
> Benutzt du Eclipse?
Was hat das damit zu tun?
ich wollte die Frage "wie heisst du" gleich in der scanf-Funktion
unterbringen. Zum Testen ;-)
>
strcpy gibt ja einen Zeiger zurück und scanf braucht diesen Zeiger.
und bei strncpy kann man auch im dritten Parameter eine Zahl mitgeben -
puts / printf liefern ja Zahlen zurück.
>
Also....
zitter_ned_aso schrieb:> ich wollte die Frage "wie heisst du" gleich in der scanf-Funktion> unterbringen. Zum Testen ;-)
In der scanf-Funktion kannst du sie nicht unterbringen, die ist in der
lib. Du meinst im Aufruf. Und was willst du testen?
zitter_ned_aso schrieb:> Mist, geht auch nicht ))))
Was geht nicht?
Thomas M. schrieb:> Wenn die Stringlänge von 20 Zeichen definiert ist, sollten auch max 19> Zeichen + \0 zulässig sein.> Wie eben in Basic oder Pascal z.B. und nicht wie in C das er dann> einfach Speicherbereiche ungefragt überschreibt.
In C wird eben kein String definiert, sondern ein Speicherbereich, in
dem sich dann ein oder mehrere Strings befinden können. Dazu bei Bedarf
noch Pointer, die auf Strings zeigen.
Das hat viele Vorteile, die aber erst klar werden, man das Wesen von
Pointern verstanden hat.
zitter_ned_aso schrieb:> scanf("%s", strncpy(str, "", puts("wie heisst du?")));
Kennst du das Verhalten von strncpy, wenn die maximale Anzahl Zeichen
erreicht wird?
Dirk B. schrieb:> zitter_ned_aso schrieb:>> scanf("%s", strncpy(str, "", puts("wie heisst du?")));>> Kennst du das Verhalten von strncpy, wenn die maximale Anzahl Zeichen> erreicht wird?
Das ist völlig egal, weil sowie nur Nullzeichen kopiert werden
(Leerstring im 2. Parameter), und zwar soviele wie die Länge von "wie
heisst du?". Dann kommt scanf und überschreibt das nochmal mit der
Eingabe.
Mit strncpy muss man sowieso aufpassen, es ist nicht, wie erwartet ein
begrenztes strcpy.
Das Ganze ist unsinnig, würde aber funktionieren bei Eingaben unter 30
Zeichen. scanf bricht übrigens einen String bei Leerzeichen ab, sollte
man auch wissen, wenn man es verwenden will.
Jobst Q. schrieb:> Das ist völlig egal, weil sowie nur Nullzeichen kopiert werden> (Leerstring im 2. Parameter), und zwar soviele wie die Länge von "wie> heisst du?".
Nein, die Länge von "wie heißt du?" ist vollkommen irrelevant.
puts liefert im Erfolgsfall irgendeine beliebige positive Zahl zurück.
Aber das ist egal, denn es wird sowieso nur maximal ein \0-Zeichen
kopiert.
> Mit strncpy muss man sowieso aufpassen, es ist nicht, wie erwartet ein> begrenztes strcpy.
Eigentlich ist es genau das. Es hört auf zu kopieren, entweder nachdem
ein \0 im Quellstring erreicht worden ist, oder nachdem n Zeichen
kopiert worden sind.
Jobst Q. schrieb:> zitter_ned_aso schrieb:>> Mist, geht auch nicht ))))>> Was geht nicht?
Na dass ich den Puffer leere. Ohne fflush.
Es kommt nicht zuerst die Frage, dann die Eingabe und Ausgabe.
Sondern zuerst die Eingabe, dann die Frage und die Ausgabe.
Das will man doch haben:
Dirk B. schrieb:> Zeno schrieb:>> C>> kennt noch nicht einmal die Länge des Zeichenarrays.>> Doch, kennt er. Sonst würde das sizeof(array) nicht funktionieren.
Nein er kennt die Länge des (Zeichen)Array's nicht. sizeof() gibt
lediglich die Länge des reservierten Speicherbereiches in Byte zurück.
Um die Anzahl der Elemente des Arrays zu ermitteln ist zusätzlich noch
der Typ der Arrayelemente erforderlich. Beim Zeichenarray passt es
zufälligerweise zusammen, weil char eben genau ein Byte ist.
Deshalb ist es zum Beispiel bei der Übergabe von Arrays an Funktionen
auch erforderlich die Anzahl der Arrayelemente mit zu übergeben.
Dirk B. schrieb:>> Der Compiler>> reseviert bei der Deklaration des Zeichenarrays lediglich den>> Speicherplatz.>> Eine Deklaration ist nur eine Information, das ein Objekt existiert.>> Erst bei der Definition wird auch Speicherplatz belegt.
Du weist genau was ich damit gemeint habe. Die Unterscheidung von
Deklaration und Definition ist auch wieder so ein C-Spezifikum. In den
meisten Fällen wird jedoch beides in einem Schritt gemacht (int x vs.
extern int x)
Niklas G. schrieb:> In C++ kann man sich die Fummelei ...
Es geht hier aber nun mal um C
Jobst Q. schrieb:> In C wird eben kein String definiert, sondern ein Speicherbereich, in> dem sich dann ein oder mehrere Strings befinden können. Dazu bei Bedarf> noch Pointer, die auf Strings zeigen.>> Das hat viele Vorteile, die aber erst klar werden, man das Wesen von> Pointern verstanden hat.
Jetzt redest Du aber das vermurkste oder besser gesagt nicht vorhandene
Stringhandling in C schön.
Rolf M. schrieb:> puts liefert im Erfolgsfall irgendeine beliebige positive Zahl zurück.> Aber das ist egal, denn es wird sowieso nur maximal ein \0-Zeichen> kopiert.
In der man-page von strncpy steht:
The strncpy() function is similar, except that at most n bytes of src
are copied. Warning: If there is no null byte among the first n bytes of
src, the string placed in dest will not be null-terminated.
If the length of src is less than n, strncpy() writes additional null
bytes to dest to ensure that a total of n bytes are written.
https://linux.die.net/man/3/strncpy
Jobst Q. schrieb:> In der man-page von strncpy steht:
Das ist ja richtig, aber er meinte den Return-Wert von puts.
printf wäre besser. Da wird die Anzahl der "gedruckten" Zeichen
zurückgegeben.
Bei puts wohl irgendeine positive Zahl zurückgeben.
zitter_ned_aso schrieb:> Bei puts wohl irgendeine positive Zahl
Dann könnte die Funktion vielleicht auch .... 100 zurückgeben? Dann wäre
das ja problematisch:
1
charstr[10];
2
strncpy(str,"",100);
C library function - puts()
.....
Return Value
If successful, non-negative value is returned.
Zeno schrieb:> Um die Anzahl der Elemente des Arrays zu ermitteln ist zusätzlich noch> der Typ der Arrayelemente erforderlich.
Den kennt der Compiler aber auch.
> Deshalb ist es zum Beispiel bei der Übergabe von Arrays an Funktionen> auch erforderlich die Anzahl der Arrayelemente mit zu übergeben.
Das liegt eher daran, dass in der Funktion nur noch eine Adresse
ankommt.
Denn auch in der Funktion kennt der Compiler den Typ vom Zeiger.
zitter_ned_aso schrieb:> Das will man doch haben:> Wie heisst Du?> Manfred> Dein Name ist Manfred.
Der TO wollte ganz offensichtlich:
Wie heisst Du?: Manfred
Hallo Manfred
Also die Antwort nach der Frage, aber in der selben Zeile. Und genau
dafür braucht man das fflush(), da sonst ggf. der Puffer nicht
rausgeschrieben wird.
zitter_ned_aso schrieb:> zitter_ned_aso schrieb:>> Bei puts wohl irgendeine positive Zahl>> Dann könnte die Funktion vielleicht auch .... 100 zurückgeben?
Ja.
> Dann wäre das ja problematisch:
Nein.
> char str[10];> strncpy(str, "", 100);
Und wo ist das Problem? In diesem Fall wird genau ein '\0' in das erste
(oder je nach Sichtweise nullte) Byte von str kopiert, und fertig.
Rolf M. schrieb:> Und genau dafür braucht man das fflush(), da sonst ggf. der Puffer nicht> rausgeschrieben wird.
Oder man schaltet das auf normalen Systemen eher unnötige Buffering
einfach ab.
Rufus Τ. F. schrieb:> Rolf M. schrieb:>> Und genau dafür braucht man das fflush(), da sonst ggf. der Puffer nicht>> rausgeschrieben wird.>> Oder man schaltet das auf normalen Systemen eher unnötige Buffering> einfach ab.
Nur um sich einen Aufruf von fflush() zu sparen?
Das Abschalten des Buffering macht die Ausgabe je nach System um
Größenordnungen langsamer, was in den meisten Fällen zwar nichts
ausmacht, aber nun auch nicht gerade von Vorteil ist.
Rolf M. schrieb:>> char str[10];>> strncpy(str, "", 100);>> Und wo ist das Problem? In diesem Fall wird genau ein '\0' in das erste> (oder je nach Sichtweise nullte) Byte von str kopiert, und fertig.
Eben nicht.
denn:
Jobst Q. schrieb:> If the length of src is less than n, strncpy() writes additional null> bytes to dest to ensure that a total of n bytes are written.
strncpy schreibt immer n Zeichen. Es wird mit '\0' aufgefüllt.
Dirk B. schrieb:> strncpy schreibt immer n Zeichen. Es wird mit '\0' aufgefüllt.
Oh, stimmt. Sorry, da hab ich Blödsinn geschrieben. Also ja, dann ist
das tatsächlich ein Problem.
Rolf M. schrieb:> Das Abschalten des Buffering macht die Ausgabe je nach System um> Größenordnungen langsamer,
Auf welchen realen Systemen ist das so? Mir scheint das ein Relikt aus
pdp11- und verwandten Zeiten zu sein.
Rufus Τ. F. schrieb:> Auf welchen realen Systemen ist das so?
Mindestens mal auf allen, welche User- und Kernelspace trennen, wie
x86-Linux oder Windows. Jeden kleinen String einzeln per Syscall an den
Kernel zu schicken ist ein ziemlicher Overhead; die Strings vorher im
Userspace zusammenzubauen und am Stück zu übergeben reduziert diesen.
Dirk B. schrieb:> Zeno schrieb:>> Um die Anzahl der Elemente des Arrays zu ermitteln ist zusätzlich noch>> der Typ der Arrayelemente erforderlich.>> Den kennt der Compiler aber auch.
Dennoch ist C nicht in der Lage eine Funktion bereitzustellen die die
korrekte Anzahl der Arrayelemente zurück gibt. Da muß ich immer so was
in der Art machen:
1
N=sizeof(a)/sizeof(a[0])
Aber sei's drum ist mir auch so ziemlich Rille. Ob zu dem vergurkten
Stringhandling nun auch noch ein vergurktes Arrayhandling hinzukommt ist
eh wurscht. Schreiben über die Arraygrenzen hinaus ist eben auch wieder
ein Alleinstellungsmerkmal von C und dies ist eben auf deutsch gesagt
Sch.... . Auch an dieser Stelle haben andere Programmiersprachen die
Hausaufgaben wieder mal besser gemacht.
Aber egal darüber zu diskutieren bringt eh nichts und in diesem Forum
schon gleich gar nicht.
Niklas G. schrieb:> Jeden kleinen String einzeln per Syscall an den Kernel zu schicken ist> ein ziemlicher Overhead;
Das mag betriebssystemtheoretisch zutreffen, aber es geht hier um I/O
mit Benutzerein- und ausgaben.
Die sind so irrwitzig langsam, da könnte man auch jedes Bit einzeln per
Syscall an den Kernel schicken und der würde sich trotzdem langweilen.
Niklas G. schrieb:> Rufus Τ. F. schrieb:>> Auf welchen realen Systemen ist das so?>> Mindestens mal auf allen, welche User- und Kernelspace trennen, wie> x86-Linux oder Windows.
Ganz besonders, wenn man eine Remote-Verbindung z.B. über ssh hat. Dann
muss ohne Pufferung jedes Byte einzeln verschlüsselt und in einem
eigenen Paket verschickt werden. Wenn das Programm dann über eine
langsame Handy-Internet-Verbindung ein paar Hundert Zeilen auf einmal
ausgeben will, muss man da schon ein bisschen warten.
Zeno schrieb:> Jobst Q. schrieb:>> In C wird eben kein String definiert, sondern ein Speicherbereich, in>> dem sich dann ein oder mehrere Strings befinden können. Dazu bei Bedarf>> noch Pointer, die auf Strings zeigen.>>>> Das hat viele Vorteile, die aber erst klar werden, man das Wesen von>> Pointern verstanden hat.>> Jetzt redest Du aber das vermurkste oder besser gesagt nicht vorhandene> Stringhandling in C schön.
Deine Beschwerden darüber sind etwa so sinnvoll wie das Jammern darüber,
dass ein Mountainbike keine Stützräder hat.
Entweder entschließt man sich, das Fahren von Zweirädern zu lernen oder
man bleibt eben bei Fahrzeugen mit mehr als zwei Rädern.
Messer, Schere, Pointer, Licht
sind für kleine Kinder nicht.
Rolf M. schrieb:> Dann muss ohne Pufferung jedes Byte einzeln verschlüsselt und in einem> eigenen Paket verschickt werden
Naja, puffert ssh nicht die per pipe ankommenden bevor es sie per TCP
absendet?
Niklas G. schrieb:> Rolf M. schrieb:>> Dann muss ohne Pufferung jedes Byte einzeln verschlüsselt und in einem>> eigenen Paket verschickt werden>> Naja, puffert ssh nicht die per pipe ankommenden bevor es sie per TCP> absendet?
Wenn das nochmal eine heimliche zusätzliche Pufferung machen würde, wäre
das eher ungünstig. Es soll ja durchaus möglich sein, Zeichen auch
einzeln zu schicken, aber eben nur dann, wenn es auch wirklich nötig
ist.
Rolf M. schrieb:> Wenn das nochmal eine heimliche zusätzliche Pufferung machen würde, wäre> das eher ungünstig
Der TCP Stack puffert auch, dazwischen liegende Router ggf. auch...
nirgendwo ist garantiert, dass einzeln abgesendete Bytes auch mit
einzelnen Paketen übertragen werden. Daher kann SSH da reinen Gewissens
ebenfalls puffern. Sowas ist natürlich intelligent umgesetzt - wenn auf
ein einzelnes Zeichen innerhalb einer bestimmten Zeit nichts mehr kommt,
wird es eben doch einzeln abgeschickt. Sowohl pipes, Terminals als auch
TCP sind Strom orientiert. Da gibt es konzeptuell keine eindeutigen
Paketgrenzen; die sieht nur der Kernel.
Niklas G. schrieb:> Sowohl pipes, Terminals als auch TCP sind Strom orientiert. Da gibt es> konzeptuell keine eindeutigen Paketgrenzen; die sieht nur der Kernel.
Dann such mal nach TCP_NODELAY.
Jobst Q. schrieb:> Deine Beschwerden darüber sind etwa so sinnvoll wie das Jammern darüber,> dass ein Mountainbike keine Stützräder hat.
Ich beschwere mich nicht - ich habe lediglich festgestellt, das Du da
was schön redest.
Rolf M. schrieb:> Dann such mal nach TCP_NODELAY.
Eine Linux-spezifische Erweiterung, welche den Nagle-Algorithmus
deaktiviert, der Anwendung aber immer noch keinen direkten Zugriff auf
Paketgrenzen gibt. Ob ssh die wirklich verwendet und ob das ein Argument
ist, print Statements zusammenzufassen?
Niklas G. schrieb:> Rolf M. schrieb:>> Dann such mal nach TCP_NODELAY.>> Eine Linux-spezifische Erweiterung,
Nein, das ist POSIX-Standard.
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_tcp.h.html
Selbst Windows kennt die Option übrigens.
> welche den Nagle-Algorithmus deaktiviert, der Anwendung aber immer noch> keinen direkten Zugriff auf Paketgrenzen gibt.
Es wird halt alles sofort rausgeschickt und nicht mehr gewartet, bis
sich eine gewisse Menge Daten gesammelt hat. Genau das, was man bei
einer interaktiven Shell will.
> Ob ssh die wirklich verwendet
Gerade so Dinge wie ssh verwenden das:
https://de.wikipedia.org/wiki/Nagle-Algorithmus :
"Ist dieses Verhalten nicht gewünscht, so lässt sich der
Nagle-Algorithmus unter POSIX-kompatiblen Betriebssystemen und unter
Windows mit der setsockopt-Option TCP_NODELAY abschalten. In der Praxis
wird das zum Beispiel bei interaktiven Sitzungsprotokollen wie Telnet
oder SSH getan, um die Reaktionszeit der Gegenseite auf Tastatureingaben
oder bei Bildschirmausgaben zu verkürzen."
> und ob das ein Argument ist, print Statements zusammenzufassen?
Vom ursprünglichen Thema haben wir uns schon etwas entfernt. Da hab ich
einfach nur gemeint, dass man für printf nicht an irgendwelchem
voreingestellten Buffering rumschrauben, sondern einfach fflush(stdout)
verwenden soll, wenn man eine Zeile rausschreiben will, die kein Newline
am Ende hat.
Rolf M. schrieb:> Nein, das ist POSIX-Standard.
Achso, Google lieferte nur Linux Dinge.
Rolf M. schrieb:> Gerade so Dinge wie ssh verwenden das:
Okay... dann müsste man messen wie sich das auswirkt, und ob wirklich
jeder write Syscall Aufruf auf dem Stdout-SSH Pipe in einem TCP Paket
resultiert.
Rolf M. schrieb:> Es wird halt alles sofort rausgeschickt und nicht mehr gewartet, bis> sich eine gewisse Menge Daten gesammelt hat.
Ja. Dein erster Beitrag zu TCP_NODELAY klang so als würde man damit die
Strom-Orientierte Charakteristik abschalten und auf Paketgrenzen
zugreifen können.
Zeno schrieb:> Ich beschwere mich nicht - ich habe lediglich festgestellt, das Du da> was schön redest.
Ich muss es nicht schönreden. Es ist einfach schön, nicht mehr wie in
BASIC S$=RIGHT$(S$, LEN (S$)-1) schreiben zu müssen, sondern einfach nur
s++ . Und dabei sicher zu sein, dass der Prozessor dafür keine unnötigen
Umwege gehen muss.
Jobst Q. schrieb:> Zeno schrieb:>> Ich beschwere mich nicht - ich habe lediglich festgestellt, das Du da>> was schön redest.>> Ich muss es nicht schönreden. Es ist einfach schön, nicht mehr wie in> BASIC S$=RIGHT$(S$, LEN (S$)-1) schreiben zu müssen, sondern einfach nur> s++ . Und dabei sicher zu sein, dass der Prozessor dafür keine unnötigen> Umwege gehen muss.
Mit BASIC setzt du die Messlatte aber schon gewaltig hoch.
Jobst Q. schrieb:> Es ist einfach schön, nicht mehr wie in> BASIC S$=RIGHT$(S$, LEN (S$)-1) schreiben zu müssen, sondern einfach nur> s++ .
In ruby geht sowas z.B. mit s[1..-1].
Das letzte Zeichen abschneiden, also die ersten n-1 nehmen, geht in ruby
mit s[0..-2]. Wie macht man das schön in C, ohne unnötige Umwege?
Niklas G. schrieb:> Wie macht man das schön in C, ohne unnötige Umwege?
Sofern man sich keine eigene String-Repräsentation gebastelt hat, die
zusätzlich zum Inhalt des Strings auch noch dessen aktuelle Länge
speichert, muss man die Stringlänge abzählen.
1
string[strlen(string)-2)]='\0';
So könnte man es machen, aber es wird unappetitlich, sofern der String
bereits leer ist, strlen also 0 zurückgibt.
Diese Bedingung muss man noch abfangen.
Das verändert den String selbst; sofern nur eine selektive Kopie erzeugt
werden soll, ist entsprechend strncpy zu verwenden, mit all' seinen
unappetitlichen Einschränkungen (oder, sofern auf der Plattform
vorhanden, strlcpy, aber das ist leider nicht Bestandteil des
C-Standards).
Zeno schrieb:> Aber egal darüber zu diskutieren bringt eh nichts und in diesem Forum> schon gleich gar nicht.
Richtig: wenn dir C nicht gefällt, verwend es halt einfach nicht, statt
herumzujammern wie dolle schlecht es ist. Ganz einfach.
Zeno schrieb:> Jetzt redest Du aber das vermurkste oder besser gesagt nicht vorhandene> Stringhandling in C schön.
Richtig, C hat kein String-Handling. C hat auch keine
Objektorientierung. Und keine Mittel zur generischen Programmierung oder
zur Metaprogrammierung. C ist vermutlich auch schon älter als du.
Nun kann man entweder rumjammern, oder man findet sich damit ab und
verwendet entweder eine ander Sprache oder lernt damit zu leben. Du hast
dich halt für rumjammern entschieden.
zitter_ned_aso schrieb:> Pail P. schrieb:> printf("Wie heisst Du?:");> scanf("%s",&name);>> Mal was anderes gefragt: Funktioniert bei euch solche Eingabe überhaupt?>> Ohne fflush(stdout) nach printf wird bei mir zuerst der Name eingelesen.> Dann kommt die Frage "wie heißt du?".
Auf welchem Betriebssystem arbeitest Du? Bei unixoiden ist garantiert,
dass bei Eingabe vom Terminal (hier scanf) vorher stdout - wenn auch
dieses mit dem Terminal verbunden ist - automatisch geflusht wird.
$ cat s.c
1
#include<stdio.h>
2
3
intmain()
4
{
5
charbuf[80];
6
printf("Hello ");
7
scanf("%s",buf);
8
return0;
9
}
$ cc s.c -o s && ./s
1
Hello World
Zuerst wurde "Hello" ausgegeben, dann habe ich "World" eingegeben. Das
war auch schon so, als die TTYs noch serielle Schnittstellen zum
Terminal waren. Ein '\n" oder "fflush (stdout)" ist in diesem speziellen
Fall nicht notwendig. Das funktioniert bei jeder Funktion, die von stdin
liest. Auch ein getchar() erzwingt einen vorherigen Flush des Outputs.
vn n. schrieb:> Nun kann man entweder rumjammern, oder man findet sich damit ab und> verwendet entweder eine ander Sprache oder lernt damit zu leben. Du hast> dich halt für rumjammern entschieden.
Ich kann dir versichern: Das geht beides problemlos gleichzeitig!
Rufus Τ. F. schrieb:> Sofern man sich keine eigene String-Repräsentation gebastelt hat, die> zusätzlich zum Inhalt des Strings auch noch dessen aktuelle Länge> speichert, muss man die Stringlänge abzählen.> string[strlen(string) - 2)] = '\0';>> So könnte man es machen, aber es wird unappetitlich, sofern der String> bereits leer ist, strlen also 0 zurückgibt.
Wüsste nicht, wozu es sinnvoll sein sollte, einen unbekannten String um
eine bestimmte Anzahl Zeichen zu kürzen.Für sinnvolle Aufgaben wie das
Enfernen von Leerzeichen und anderen unsichtbaren Zeichen am Ende kann
sich man eine kleine Funktion programmieren.
Eine String-Repräsentation, die zusätzlich zum Inhalt des Strings auch
noch dessen aktuelle Länge speichert, lohnt sich nicht. Man braucht die
Länge nur selten und dann ist das Abzählen wirklich kein Aufwand,
verglichen damit, dass man bei jeder Stringoperation die mitgespeicherte
Länge aktualisieren müsste.
Die Standardfunktionen der C-Lib sind leider oft schlecht konzipiert. So
bei fgets oder strcpy, wo der schon bekannte Anfang des Strings wieder
zurückgegeben wird, statt die neue Information des Stringendes oder der
Zahl der gelesenen Zeichen.
Jobst Q. schrieb:> Eine String-Repräsentation, die zusätzlich zum Inhalt des Strings auch> noch dessen aktuelle Länge speichert, lohnt sich nicht.
Deswegen machen das die meisten Sprachen das auch genau so? Alles was C
nicht kann, braucht man auch nicht?
Jobst Q. schrieb:> Eine String-Repräsentation, die zusätzlich zum Inhalt des Strings auch> noch dessen aktuelle Länge speichert, lohnt sich nicht.
Tatsächlich funktioniert das Prinzip sehr gut und hat immense Vorteile
bei der Speichersicherheit.
> Man braucht die> Länge nur selten und dann ist das Abzählen wirklich kein Aufwand,> verglichen damit, dass man bei jeder Stringoperation die mitgespeicherte> Länge aktualisieren müsste.
Angenommen ich möchte einen String in viele Substrings unterteilen (ist
ja durchaus ein realistischer Anwendungsfall). Entweder man kann seine
lustigen \0 in das Ausgangsarray schreiben, oder man muss es kopieren,
wenn der Ursprungsstring irgendwo noch referenziert ist, oder
Textersetzung grundsätzlich nicht möglich ist (z. B. String in Blöcke
von je n Zeichen unterteilen oder bestimmte Regex-Muster). Zum
Kopieren möchte man üblicherweise die Länge wissen, also erstmal schön
den ganzen String lesen, und dann darf man nochmal drüberfahren. Die
Variante mit Pointer+Länge muss hier Garnichts kopieren und kann
direkt loslegen. Außerdem sind Operationen mit Integern generell
ziemlich günstig, verkürzen ist oft eine simple Addition oder
Subtraktion.
Habe ich schon erwähnt, dass Strings durchaus auch einige Hundert
Megabyte groß werden können?
Jemand schrieb:> Jobst Q. schrieb:>> Eine String-Repräsentation, die zusätzlich zum Inhalt des Strings auch>> noch dessen aktuelle Länge speichert, lohnt sich nicht.>> Tatsächlich funktioniert das Prinzip sehr gut und hat immense Vorteile> bei der Speichersicherheit.
Und hat immense Nachteile bei der Geschwindigkeit, weil die Länge immer
wieder aktualisiert werden muss.
Für die Speichersicherheit brauche ich nur einen Pointer auf die Grenze
des reservierten Speicherbereichs, mit dem der Schreibpointer vor jeder
Schreibaktion verglichen wird. Dieser Grenzpointer bleibt für alle
Aktionen auf diesem Speicherbereich gleich und das Hin-und-herrechnen
zwischen Länge und Pointer entfällt.
Wenn die Länge wirklich mal gebraucht wird, so zum Schreiben in eine
Datei, lässt sie sich schnell berechnen als Differenz des
Schreibzeigers, der die Endnull geschrieben hat, zum Pufferanfang.
>>> Man braucht die>> Länge nur selten und dann ist das Abzählen wirklich kein Aufwand,>> verglichen damit, dass man bei jeder Stringoperation die mitgespeicherte>> Länge aktualisieren müsste.>> Angenommen ich möchte einen String in viele Substrings unterteilen (ist> ja durchaus ein realistischer Anwendungsfall). Entweder man kann seine> lustigen \0 in das Ausgangsarray schreiben, oder man muss es kopieren,> wenn der Ursprungsstring irgendwo noch referenziert ist, oder> Textersetzung grundsätzlich nicht möglich ist (z. B. String in Blöcke> von je n Zeichen unterteilen oder bestimmte Regex-Muster). Zum> Kopieren möchte man üblicherweise die Länge wissen, also erstmal schön> den ganzen String lesen, und dann darf man nochmal drüberfahren. Die> Variante mit Pointer+Länge muss hier Garnichts kopieren und kann> direkt loslegen. Außerdem sind Operationen mit Integern generell> ziemlich günstig, verkürzen ist oft eine simple Addition oder> Subtraktion.> Habe ich schon erwähnt, dass Strings durchaus auch einige Hundert> Megabyte groß werden können?
Theoretisch schon, aber wer solche Riesenstrings verwendet, hat den Sinn
von Strings nicht verstanden. Strings sind Mittel zur Kommunikation
zwischen Mensch und Maschine, die von beiden gut lesbar und schreibbar
sind. Auf Dateiebene sind es Textdateien, die in Zeilen organisiert
sind.
Die Zahl der Zeichen, die ein Mensch in einer Zeile lesen kann, ist
begrenzt, deshalb ist es sinnlos, Stringfunktionen auf viel größere
Längen zu optimieren. Für große Datenmengen gibt es die mem-Funktionen,
die auch denen zu empfehlen sind, die in C-Strings die Möglichkeit
vermissen, Nullzeichen unterzubringen.
einfach schön...wie bereits absolute Anfängerfragen IMMER und IMMER
wieder zu ausufernden Diskussionen und Meinungsverschiedenheiten
führen..
C ist offenbar eine Sprache die Programmierern dient..Beschäftigung zu
haben
Der Knaller ist..in meinem Code ist auch noch ein weiterer gravierender
Anfängerfehler, dem ja bereits einer aufgefallen ist,, es fehlt das
\n...
nur dum das es unter codeblocks und gcc problemlos so funktoniert und er
alles UNTEREINANDER!! schreibt, eben NICHT wie zu erwarten in einer
Zeile
Wenn jetzt einer sagt,..Jaaaa das ist doch auch völlig KLAR!! das liegt
einem Deinem verwendeten Compiler..dann lache ich nur noch über C und
seiner Anhänger und seinen tollen Normierungen...wenn es immer wieder
pures Glück ist, wie sich das Programm verhält, je nachdem womit es
compiliert wird.
Aber vielleicht kann mir das auch jemand ganz nüchtern und sachlich
erklären, und ich sehe meinen Fehler ein...
das Gleiche hier..
1
#include<stdio.h>
2
#include<stdlib.h>
3
4
5
6
voidAbfrage()
7
{
8
charVorname[12],Nachname[12];
9
intAlter;
10
11
printf("Bitte geben Sie ihren Vornamen ein: ");
12
scanf("%s",Vorname);
13
printf("Bitte geben Sie ihren Nachnamen ein: ");
14
scanf("%s",Nachname);
15
printf("Ihr Alter: ");
16
scanf("%d",&Alter);
17
printf("Hallo %s %s, sind sie wirklich erst %d Jahre alt?",Vorname,Nachname,Alter);
Niklas G. schrieb:> Deswegen machen das die meisten Sprachen das auch genau so? Alles was C> nicht kann, braucht man auch nicht?
Naja wer halt nur in der C-Suppe schwimmt, muß sich halt selbige immer
wieder schön reden.
Pail P. schrieb:> C ist offenbar eine Sprache die Programmierern dient..Beschäftigung zu> haben
Ja, weil C-Programmierer großen Spaß daran haben alles neu zu
implementieren, was andere Sprachen schon mitgeliefert haben...
Pail P. schrieb:> nur dum das es unter codeblocks und gcc problemlos so funktoniert und er> alles UNTEREINANDER!! schreibt, eben NICHT wie zu erwarten in einer> Zeile
Weil der Zeilenumbruch durch das manuelle Return kommt. Das ist schon
richtig so.
Pail P. schrieb:> wenn es immer wieder> pures Glück ist, wie sich das Programm verhält, je nachdem womit es> compiliert wird.
Das trifft zwar auf diesen Fall nicht zu, aber ja: C (und C++) haben
eine Menge Konstrukte, bei denen das Verhalten vom Compiler(-Version,
-Optionen), OS, Plattform abhängt. Das nennt sich "undefined behaviour"
und "implementation-defined behaviour". Als Programmierer sollte man
entsprechenden Code vermeiden; in den allermeisten Fällen gibt es vom
Standard definierte Möglichkeiten ans Ziel zu kommen, welche dann auch
überall konsistent funktionieren. Leider ist es gerade in Foren
verbreitet, inkorrekten und damit unportablen Code zu zeigen, den
Anfänger dann als gegeben hinnehmen - weil es "funktioniert doch" (bis
zum nächsten Compiler-Update o.ä.).
Dass es diese Fälle gibt ist aber sehr wichtig - dadurch dass das
Verhalten eines Programms teilweise plattformabhängig sein kann, müssen
einige Dinge nicht von der Laufzeit simuliert werden, und das Verhalten
der Plattform "scheint durch". Dies kann einen großen Performance-Gewinn
bedeuten. Sprachen, die überall gleich funktionieren (z.B. Java) müssen
im Code viele Prüfungen einbauen, um auf unterschiedlichen Plattformen
gleiches Verhalten zu simulieren.
Klassisches Beispiel ist der Null-Pointer-Zugriff: Unter Java ist
garantiert, dass der Zugriff auf eine null-Referenz zu einer Exception
führt. Damit das klappt, muss Java bei jedem Referenz-Zugriff den
Pointer auf 0 prüfen (ineffizient). In C(++) ist ein solcher Zugriff
einfach nur "undefined behaviour", d.h. es kann irgendwas passieren -
vom Programmabsturz zur Sicherheitslücke. Daher muss man als
Programmierer sicher stellen, dass ein solcher Zugriff nie passiert.
Daher muss die Laufzeitumgebung das nicht tun, was die Performance
verbessern kann. In der Praxis fangen Betriebssysteme (Linux, Windows)
mithilfe der MMU Nullpointer-Zugriffe ab; auf Mikrocontrollern sieht das
aber schon anders aus. Daher darf man sich in C(++) nicht auf ein
bestimmtes Verhalten bei Null-Pointer-Zugriffen verlassen; in Java kann
man es (auch wenn es nicht sehr sauber ist).
Wen das (zu Recht) stört, sollte kein C verwenden, sondern z.B. Rust
oder Java oder .Net etc.
Jobst Q. schrieb:> Theoretisch schon, aber wer solche Riesenstrings verwendet, hat den Sinn> von Strings nicht verstanden. Strings sind Mittel zur Kommunikation> zwischen Mensch und Maschine, die von beiden gut lesbar und schreibbar> sind. Auf Dateiebene sind es Textdateien, die in Zeilen organisiert> sind.
Solche großen Strings machen durchaus Sinn. Vielleicht nicht bei µC's
aber es gibt auch noch eine Welt außer µC.
Wenn man beispielsweise in einem einem großen Text diverse Textstellen
ersetzen möchte, dann kann man selbigen in einen String einlesen die
Änderungen mit den passenden Funktionen vornehmen und danach das Ganze
meinetwegen wieder in eine Datei schreiben. Und wenn man sich dabei
nicht um irgend welche Pufferlängen etc. kümmern muß dann ist das schon
sehr komfortabel.
Jetzt sage nicht so etwas kommt nicht vor. Das kommt häufiger vor als Du
glaubst.
Richtig lustig wird es in C wenn der zu ersetzende Stringteil in der
Mitte des Strings steht und der Ersatzstring länger als das Orginal ist.
Da muß ich nicht nur den Puffer vergrößern sondern ich muß auch noch den
Reststring nach hinten verschieben. Kann man alles machen, mit einer
passenden Stringfunktion, wie sie andere Programmiersprachen bieten ist
das aber schon sehr komfortabel und natürlich auch weniger
fehlerträchtig, weil sich solche Funktionen i.d.R. um alles kümmern.
Warum treten denn die Bufferoverflows vorzugweise in Programmen auf, die
mit C geschrieben wurden? Weil es C völlig egal ist ob er in einen
Speicherbereich schreiben darf oder nicht. Der Programmierer hat einfach
dafür zu sorgen das dies nicht passiert. Dummerweise sind auch
C-Programmierer nicht fehlerfrei und so passiert es eben doch.
"Dummerweise sind auch
C-Programmierer nicht fehlerfrei ..."
oha..da hast Du jetzt was losgetreten...nun kommt wieder jeder der meint
er wäre unfehlbar und versucht zu kontern..er wird scheitern..aber er
wird es nicht Einsehen bzw verstehe..bis es um die Diskussion der
Rechtschreibung geht wenn keine Argumente mehr vorhanden sind...
Pail P. schrieb:> "Dummerweise sind auch> C-Programmierer nicht fehlerfrei ..."> oha..da hast Du jetzt was losgetreten...nun kommt wieder jeder der meint> er wäre unfehlbar und versucht zu kontern..er wird scheitern..aber er> wird es nicht Einsehen bzw verstehe..bis es um die Diskussion der> Rechtschreibung geht wenn keine Argumente mehr vorhanden sind...
Naja fehlerfrei ist eigentlich niemand, Programmierer eben auch egal in
welcher Sprache sie programmieren. Deshalb ist es gut wenn sich die
Programmiersprache oder besser der Compiler darum kümmert, das bestimmte
Fehler erst gar nicht gemacht werden können. Und ja das ist manchmal
auch etwas unbequem.
Jobst Q. schrieb:> Und hat immense Nachteile bei der Geschwindigkeit, weil die Länge immer> wieder aktualisiert werden muss.
Erfahrungsgemäß überhaupt nicht zu bestätigen.
Jobst Q. schrieb:> Für die Speichersicherheit brauche ich nur einen Pointer auf die Grenze> des reservierten Speicherbereichs
Wie genau definierts du den Begriff "reservierter Bereich"? Jeder
Zugriff auf Speicher außerhalb des Ursprungsstrings ist eine
Speicherverletzung, damit müsste man für jeden String Grenzen zusätzlich
speichern. (Woher weiß man die überhaupt, wenn der String von Fremdcode
bereitgestellt wird?)
Jobst Q. schrieb:> Aktionen auf diesem Speicherbereich gleich und das Hin-und-herrechnen> zwischen Länge und Pointer entfällt.
Optimierende Compiler existieren.
Zeno schrieb:> Solche großen Strings machen durchaus Sinn. Vielleicht nicht bei µC's> aber es gibt auch noch eine Welt außer µC.> Wenn man beispielsweise in einem einem großen Text diverse Textstellen> ersetzen möchte, dann kann man selbigen in einen String einlesen die> Änderungen mit den passenden Funktionen vornehmen und danach das Ganze> meinetwegen wieder in eine Datei schreiben. Und wenn man sich dabei> nicht um irgend welche Pufferlängen etc. kümmern muß dann ist das schon> sehr komfortabel.> Jetzt sage nicht so etwas kommt nicht vor. Das kommt häufiger vor als Du> glaubst.
Ich habe solche Programme auch schon geschrieben. Es gibt aber keinerlei
Notwendigkeit, eine große Datei als einen String einzulesen. Nichtmal
irgendeinen Vorteil gegenüber einem mem-Speicherblock und entsprechenden
Funktionen.
Ein Auto muss nicht fliegen können. Wenn ich fliegen will, nehme ich ein
Flugzeug.
Jemand schrieb:> Jobst Q. schrieb:>> Und hat immense Nachteile bei der Geschwindigkeit, weil die Länge immer>> wieder aktualisiert werden muss.>> Erfahrungsgemäß überhaupt nicht zu bestätigen.
Wenn man einen String als Objekt inklusive Länge als Eigenschaft
definiert, muss bei jeder Methode, bei der sich die Länge ändern kann,
die Länge neu gezählt oder berechnet werden. Bei C-Strings ist das nicht
nötig, da ein String nur durch eine Variable, den Pointer auf den
Anfang, schon vollständig definiert ist.
Und alles was der Prozessor nicht tun muss, ist schneller als jede
Aktion.
>> Jobst Q. schrieb:>> Für die Speichersicherheit brauche ich nur einen Pointer auf die Grenze>> des reservierten Speicherbereichs>> Wie genau definierts du den Begriff "reservierter Bereich"?
1
charbuf[BSIZE];
2
char*lim=buf+BSIZE;//oder lim = buf+sizeof(buf);
> Jeder Zugriff auf Speicher außerhalb des Ursprungsstrings ist eine> Speicherverletzung, damit müsste man für jeden String Grenzen zusätzlich> speichern. (Woher weiß man die überhaupt, wenn der String von Fremdcode> bereitgestellt wird?)
Es gibt keine Ursprungsstrings, in die man schreibt. Man schreibt in
einen Speicherbereich, den man vorher definiert. Ein solcher Puffer ist
in C nicht an einen String gebunden, ein Puffer kann viele Strings
enthalten. So ist (buf+2) ein anderer String als (buf), nämlich um die
beiden ersten Zeichen kürzer. Oder ein völlig anderer String, wenn
*(buf+1)==0 ist.
>> Jobst Q. schrieb:>> Aktionen auf diesem Speicherbereich gleich und das Hin-und-herrechnen>> zwischen Länge und Pointer entfällt.>> Optimierende Compiler existieren.
Das hat nichts mit dem Compiler zu tun. Es entfällt schon im Quelltext,
ich muss mich nicht darum kümmern, wieviel Platz noch zwischen
Stringende und Pufferende ist.
Jobst Q. schrieb:> Ich habe solche Programme auch schon geschrieben. Es gibt aber keinerlei> Notwendigkeit, eine große Datei als einen String einzulesen. Nichtmal> irgendeinen Vorteil gegenüber einem mem-Speicherblock und entsprechenden> Funktionen.>> Ein Auto muss nicht fliegen können. Wenn ich fliegen will, nehme ich ein> Flugzeug.
Du hast offensichtlich noch nie mit einer Programmiersprache gearbeitet,
die ein ordentliches Stringhandling implementiert hat. Da macht das
Einlesen eines ganzen Textes in einen einzigen String schon Sinn.
Aber es ist müßig mit Dir darüber zu diskutieren, weil Du nicht über den
Tellerrand schauen willst und selbst die Schwächen von C für Dich zu
einen tollen Feature mutieren.