Forum: PC-Programmierung c-char Terminierung


von Tim (Gast)


Lesenswert?

Guten Morgen,
ich habe einige Fragen zu C.

- Wenn ich eine Größe von #define MAX 1024 zeichen definiere, sehe ich 
oft bei der Deklaration so etwas wie "char buffer[MAX+1]". Wozu steht 
die "1"? MAX übergebe ich später an eine Funktion mit der maximalen 
Anzahl an Strings, die erlaubt ist. Bedeutet die +1, dass das "\0" 
berücksichtig wird?

-  im weiteren Verlauf meiner Funktion lasse ich mir die Anzahl der 
Strings zurückgeben: bytes_read = foo(.....);
Nun wird oft geschrieben: "buffer[bytes_read]='\0'". Was bedeutet das? 
Das nach meinem String, das ich über eine Konsole eingebe, das 
Terminierungszeichen drangehängt wird?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Tim schrieb:
> MAX übergebe ich später an eine Funktion mit der maximalen Anzahl an
> Strings, die erlaubt ist.

Strings oder Zeichen?

> Nun wird oft geschrieben: "buffer[bytes_read]='\0'". Was bedeutet das?
> Das nach meinem String, das ich über eine Konsole eingebe, das
> Terminierungszeichen drangehängt wird?

Exakt. So etwas sollte natürlich nur gemacht werden, solange 
"bytes_read" kleiner ist als die Puffergröße.

von ui (Gast)


Lesenswert?

Tim schrieb:
> - Wenn ich eine Größe von #define MAX 1024 zeichen definiere, sehe ich
> oft bei der Deklaration so etwas wie "char buffer[MAX+1]". Wozu steht
> die "1"? MAX übergebe ich später an eine Funktion mit der maximalen
> Anzahl an Strings, die erlaubt ist. Bedeutet die +1, dass das "\0"
> berücksichtig wird?

Ja und nein. Das bedeutet, dass die \0 berücksichtigt werden soll. Obs 
gemacht werden soll oder nicht hängt von dir selber ab.

Tim schrieb:
> -  im weiteren Verlauf meiner Funktion lasse ich mir die Anzahl der
> Strings zurückgeben: bytes_read = foo(.....);
> Nun wird oft geschrieben: "buffer[bytes_read]='\0'". Was bedeutet das?
> Das nach meinem String, das ich über eine Konsole eingebe, das
> Terminierungszeichen drangehängt wird?

Ja. Dabei aber wieder aufpassen das du nicht über die Grenzen 
hinausschreibt

BTW: chars müssen nicht terminiert werden, weil es ja nur ein Zeichen 
ist. Die Terminierung von character arrays hingegen ist mit \o 
festgelegt (ob das eine Konvention oder im Standard drinsteht 
beantwortet dir mit Sicherheit jemand anderes hier, hab meinen Ritchi 
grad nicht hier)

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

ui schrieb:
> ist mit \o festgelegt

Nicht \o, sondern \0.

von J. F. (Firma: Père Lachaise) (rect)


Lesenswert?

Jubel! \o/

von ui (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> ui schrieb:
>> ist mit \o festgelegt
>
> Nicht \o, sondern \0.

F*ck vertippt :) Warum müssen die auch s0 nahe beinander liegen?

von Amateur (Gast)


Lesenswert?

Das ist, wie bereits gesagt ein persönliches Prinzip.

Bei Zeichen (char []) ist es eigentlich egal. Aber bei Zeichenketten 
bzw. Strings gilt: Die sind immer ein Zeichen länger, dadurch dass "c" 
immer ein terminierendes '\0' hinten anhängt.

Ein simples Beispiel:

"Ein String mit 25 Zeichen"

benötigt intern 26 Zeichen. Dadurch, dass am Ende das String-Endezeichen 
angehängt wird. Letzteres geschieht automatisch. Die str???-Funktionen 
erwarten dies alle. Üblicherweise wird das Ende-Zeichen auch nicht 
explizit angegeben.

Also:
#define STR_MAX 25
Variable char [ STR_MAX + 1 ];

In diese Variable passt also eine Zeichenkette mit maximal 25 Zeichen.

von Sapperlot (Gast)


Lesenswert?

ui schrieb:
>> Nicht \o, sondern \0.
>
> F*ck vertippt :) Warum müssen die auch s0 nahe beinander liegen?

Ja, klar ...
http://i.imgur.com/0DvXxkX.jpg

von ui (Gast)


Lesenswert?

Sapperlot schrieb:
> ui schrieb:
>>> Nicht \o, sondern \0.
>>
>> F*ck vertippt :) Warum müssen die auch s0 nahe beinander liegen?
>
> Ja, klar ...
> http://i.imgur.com/0DvXxkX.jpg

Kännte ich mich wenigszens nicht verzzipen.

von ui (Gast)


Lesenswert?

Amateur schrieb:
> Das ist, wie bereits gesagt ein persönliches Prinzip.
>
> Bei Zeichen (char []) ist es eigentlich egal. Aber bei Zeichenketten
> bzw. Strings gilt: Die sind immer ein Zeichen länger, dadurch dass "c"
> immer ein terminierendes '\0' hinten anhängt.
>
> Ein simples Beispiel:
>
> "Ein String mit 25 Zeichen"
>
> benötigt intern 26 Zeichen. Dadurch, dass am Ende das String-Endezeichen
> angehängt wird. Letzteres geschieht automatisch. Die str???-Funktionen
> erwarten dies alle. Üblicherweise wird das Ende-Zeichen auch nicht
> explizit angegeben.
>
> Also:
> #define STR_MAX 25
> Variable char [ STR_MAX + 1 ];
>
> In diese Variable passt also eine Zeichenkette mit maximal 25 Zeichen.

Automatisch passiert gar nix. Du meinst implizit, deinen "automatismus 
kann man auch ganz leicht kaputt machen:
1
char a[5] = "fooo";
2
a[5] = 'f';

und schon ist der "automatismus" im Ar*ch.
Wer in C programmiert (eigentlich bei jeder Sprache, nur in C ganz 
besonders) sollte insbesondere über alles mit strings dreimal nachdenken 
bevor er irgendwas damit macht, siehe auch 
https://www.golem.de/news/rust-c-ist-eine-feindselige-sprache-1707-129196.html

von ui (Gast)


Lesenswert?

ui schrieb:
> char a[5] = "fooo";
> a[5] = 'f';

Gleich noch beim Beispiel off-by-one... So muss natürlich richtig 
heißen.
char a[5] = "fooo"
a[4] = 'f';

von Sebastian S. (amateur)


Lesenswert?

@ui
Mir kommt Dein Argument ein wenig wie das Zählen von Erbsen vor.

Der Ausbruch aus den String-konventionen ist natürlich ganz leicht, wenn 
man einen fremden (hier falschen) Datentyp benutzt.

Wenn mich nämlich nicht alles täuscht ist:
char a[5] = "fooo" eine zulässige Anweisung im Datenformat string
und
a[4] = 'f'; eine zulässige Anweisung im Datenformat char

Auf gleiche Weise kann man natürlich auch wunderbar Zahlen, im 32-Bit 
Format, durch das Speichern eines Bytes "relativieren".

von Andreas E. (hismastersvoice)


Lesenswert?

Ein wesentlicher Vorteil bei +1 ist, ich kann das Define bei späteren 
Operationen so benutzen, wie es ist und muss nicht 1 abziehen, um Platz 
für das '\0' zu lassen:
1
if (strlen(stringa) <= MAX)
2
{
3
  strcpy(stringb, stringa);
4
}

Man kann sich also zwischen +1 bei der Deklaration oder -1 bei den 
Stringoperationen entscheiden.

von Tim (Gast)


Lesenswert?

Ah ok. Und was  bewirkt eigentlich "memset(buffer, 0, MAX_SIZE); ?

Wenn ich anschließend mit printf irgend ein Element ausgeben möchte, 
erhalte ich nicht die Zahl "0", o obwohl so initialisiert. Wie überprüfe 
ich, dass das char* wirklich mit "0" initialisiert worden ist?
"

von Sebastian L. (der_mechatroniker)


Lesenswert?

Der Speicher sollte hoffentlich nicht mit '0' initialisiert worden sein 
(Ziffer 0, also Bytewert 0x30), sondern mit '\0' (0x00).

Wenn das printf einfach gar nix ausgibt, hast du es evtl. richtig 
gemacht.

von Tim (Gast)


Lesenswert?

Bedeutet Initialisierung nicht, dass ich es auch mit "0" initialisiere 
und dann ausgeben kann?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Tim schrieb:
> Bedeutet Initialisierung nicht, dass ich es auch mit "0" initialisiere
> und dann ausgeben kann?

Die Stringterminierung muss vorhanden sein, sonst wird Müll ausgeben.

Die Ziffer 0 als darstellbares Zeichen ist keine Stringterminierung.

von ui (Gast)


Lesenswert?

Sebastian S. schrieb:
> @ui
> Mir kommt Dein Argument ein wenig wie das Zählen von Erbsen vor.
>
> Der Ausbruch aus den String-konventionen ist natürlich ganz leicht, wenn
> man einen fremden (hier falschen) Datentyp benutzt.
>
> Wenn mich nämlich nicht alles täuscht ist:
> char a[5] = "fooo" eine zulässige Anweisung im Datenformat string
> und
> a[4] = 'f'; eine zulässige Anweisung im Datenformat char

Ja das sind durchaus Kleinigkeiten. Aber um Verständnis zu schaffen und 
Fehler vorzubeugen finde ich diese Kleinigkeiten (oder Erbsen zählen) 
enorm wichtig.

Übrigens: Wenn mich nicht alles täuscht (d.h. ich müsste nachschauen) 
kennt C keine strings (wie aus java,python,c++ etc. bekannt). C kennt 
nur character arrays, und damit hinkt dein Vergleich ganz gewaltig. Das 
gleiche für andere Datentypen ist total normal (also das man das ganze 
array nutzt), nur für char arrays hat man sich diesen Sonderfall 
ausgedacht. Und diesen zu kennen ist enorm wichtig, die meisten 
buffer-overflows basieren genau auf diesem Problem.

von Sebastian S. (amateur)


Lesenswert?

>Ah ok. Und was  bewirkt eigentlich "memset(buffer, 0, MAX_SIZE); ?
Niemand hindert Dich daran MAX_SIZE für inclusive das 
Terminierungszeichen zu definieren.
Die meisten aber würden MAX_SIZE + 1 schreiben oder beten, dass die zu 
speichernden Strings, nie die maximale Länge erreichen.

Das hat aber den Nachteil - wie bereits gesagt - dass Du z.B. in 
Schleifen, mit for (...;; <=MAX_SIZE - 1) Arbeiten tust.

> Das ist, wie bereits gesagt ein persönliches Prinzip.

Übrigens: Summarisch gesehen wird meist nur bei der 
Variablenkonstruktion oder der Speicherreservierung MAX_SIZE + 1 
verwendet. Später fast immer MAX_SIZE ohne den Plusterm.
Dem Compiler ist es egal, da es ja, auf jeden Fall, um eine Konstante 
geht.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Sebastian S. schrieb:
> Wenn mich nämlich nicht alles täuscht ist:
> char a[5] = "fooo" eine zulässige Anweisung im Datenformat string

Das ist keine Anweisung, das ist eine Definition einer Variablen (vom 
Typ char-Array) mit einer Initialisierung mit einer Stringkonstanten.

> und
> a[4] = 'f'; eine zulässige Anweisung im Datenformat char

Das ist eine Zuweisung an ein Element eines Arrays.

Komplett andere Baustelle.

Eine Zuweisung von Stringkonstanten an Arrays ist nicht möglich, die 
können nur zur Initialisierung verwendet werden (oder müssen mit 
Funktionen à la strcpy/memcpy explizit kopiert werden).

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.