Forum: PC-Programmierung C String einlesen, wieso geht das?!


von Pail P. (Gast)


Lesenswert?

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...

1
 #include <stdio.h>
2
#include <stdlib.h>
3
4
5
6
void Abfrage()
7
{
8
    char name[1];
9
 printf("Wie heisst Du?:");
10
 scanf("%s",&name);
11
 printf("Hallo %s",name);
12
 getchar();
13
}
14
15
int main()
16
{
17
    Abfrage();
18
    return 0;
19
}

von DPA (Gast)


Lesenswert?

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.

von Jim M. (turboj)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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

von Jobst Q. (joquis)


Lesenswert?

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);

von leo (Gast)


Lesenswert?

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
void Abfrage()
5
{
6
  char name[1];
7
  printf("Wie heisst Du?:");
8
  scanf("%s",&name);
9
  printf("Hallo %s",name);
10
  getchar();
11
}
12
13
int main()
14
{
15
  Abfrage();
16
  return 0;
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=]
4
   scanf("%s",&name);
5
          ~^  ~~~~~
6
Wie heisst Du?:xxxxxxxxxx
7
*** stack smashing detected ***: <unknown> terminated
8
Aborted (core dumped)
9
$

I.e. das Programm schreibt auf die Adresse von name.
Der Buffer-Overflow wurde ja schon angesprochen.

leo

Beitrag #5790823 wurde von einem Moderator gelöscht.
von Rolf M. (rmagnus)


Lesenswert?

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.

von Pail P. (Gast)


Lesenswert?

"Was wäre das von dir erwartete Verhalten gewesen?"
Ein Compilerfehler..und das es nicht kompiliert wird mit der 
Fehlermeldung "String too long"

von Pail P. (Gast)


Lesenswert?

also bei Strings
scanf("%4s",name);

und bei Integer
scanf("%d",&alter);
!??!

Warum muss es dann mal mit & und mal ohne & sein?

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Pail P. (Gast)


Lesenswert?

"
Und woher weiß der Compiler die Länge des Strings?"

Wie in anderen Sprachen auch, anhand seiner Definition...

von Thomas B. (sinotech)


Lesenswert?

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
char name[ 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 = new char[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.

von Dirk B. (dirkb2)


Lesenswert?

Niklas G. schrieb:
> werden sie automatisch zu Pointern auf das 0. Element

Das Element mit drm Index 0 ist das 1. Element im Array

von Dirk B. (dirkb2)


Lesenswert?

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.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Dirk B. schrieb:
> Das Element mit drm Index 0 ist das 1. Element im Array

Das ist irgendwie unklar.

von Zeno (Gast)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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
void Abfrage (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
void Abfrage()
5
{
6
  std::string name;              // 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
int main () {
14
    Abfrage ();
15
    return 0;
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).

von Dirk B. (dirkb2)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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).

: Bearbeitet durch User
von Thomas M. (Firma: https://img.favpng.com/23/21/3) (thomasmopunkt)


Lesenswert?

"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...

von zitter_ned_aso (Gast)


Lesenswert?

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?".

von Dirk B. (dirkb2)


Lesenswert?

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?

von Rolf M. (rmagnus)


Lesenswert?

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?

von Dirk B. (dirkb2)


Lesenswert?

Rolf M. schrieb:
>> Benutzt du Eclipse?
>
> Was hat das damit zu tun?

Eclipse hat(te?) ein ähnliches Verhalten bei der Konsole.

von zitter_ned_aso (Gast)


Lesenswert?

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....
1
    char str[30];
2
3
    scanf("%s", strncpy(str, "", puts("wie heisst du?")));
4
    printf("name: %s\n", str);

Mist, geht auch nicht ))))
1
mohammed
2
wie heisst du?
3
name: mohammed

von Jobst Q. (joquis)


Lesenswert?

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?

von Jobst Q. (joquis)


Lesenswert?

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.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

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?

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von zitter_ned_aso (Gast)


Lesenswert?

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:
1
Wie heisst Du?
2
Manfred
3
Dein Name ist Manfred.
Stattdessen wird auf die Eingabe gewartet:
1
Manfred
2
Wie heisst Du?
3
Dein Name ist Manfred.
Das ist doch doof

von Zeno (Gast)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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

von zitter_ned_aso (Gast)


Lesenswert?

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.

von zitter_ned_aso (Gast)


Lesenswert?

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
char str[10];
2
strncpy(str, "", 100);

C library function - puts()
.....
Return Value
If successful, non-negative value is returned.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

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.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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?

von Rolf M. (rmagnus)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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?

von Rolf M. (rmagnus)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

: Bearbeitet durch User
von Jemand (Gast)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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).

von Walter S. (avatar)


Lesenswert?

Niklas G. schrieb:
> Wie macht man das schön in C, ohne unnötige Umwege?

meineGenialeFunktion(s,-1)

von Vn N. (wefwef_s)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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
int main ()
4
{
5
    char buf[80];
6
    printf ("Hello ");
7
    scanf ("%s", buf);
8
    return 0;
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.

: Bearbeitet durch Moderator
von Jemand (Gast)


Lesenswert?

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!

von Jobst Q. (joquis)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

Jemand schrieb:
> Ich kann dir versichern: Das geht beides problemlos gleichzeitig!

So ist es!

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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?

von Jemand (Gast)


Lesenswert?

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?

von Jobst Q. (joquis)


Lesenswert?

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.

von Pail P. (Gast)


Lesenswert?

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
void Abfrage()
7
{
8
 char Vorname[12], Nachname[12];
9
 int Alter;
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);
18
  getchar();
19
}
20
21
int main()
22
{
23
 Abfrage();
24
}

von Zeno (Gast)


Lesenswert?

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.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Pail P. (Gast)


Lesenswert?

danke für die ausführliche Ausführung trotz meiner absichtlich provokant 
gestellten Frage.
Selten in diesem Forum!

von Zeno (Gast)


Lesenswert?

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.

von Pail P. (Gast)


Lesenswert?

"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...

von Zeno (Gast)


Lesenswert?

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.

von Jemand (Gast)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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
char buf[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.

von Zeno (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.