String - Größe und Länge ?

Hallo,

ich bin am basteln mit der Stringlänge. Die max. Größe eines String Arrays muß man ja definieren. Soweit klar.
Gedanklich stolper ich jedoch immer an der definierten Größe und Indexnummer.
solche Angaben
char Stringvar[15];
gehen doch immer bei 0 los. Also hätte ich einen Indexbereich von 0 bis 15? Oder sind 0 bis 14 gemeint, die dann in Summe die 15 ergeben.

Anders gefragt. Definiere ich die höchste Indexnummer oder max. Anzahl der Zeichen?

Testsketch:

const int buffersize = 15;
char Stringvar[buffersize];   

void setup() {
  Serial.begin(38400);
 
  Serial.print("String Size:  "); Serial.println(sizeof(Stringvar)); 
  Serial.print("Laenge,Zeichenanzahl "); Serial.println(strlen(Stringvar)); 
  Serial.println();
  string_fuellen();
}

void loop() {
  // empty
}


void string_fuellen()
{  
  for (int i=0; i<buffersize-1; i++)  {   // 14x, Index 0 bis 13
    Stringvar[i] = 'I';
    Stringvar[i+1] = '\0';  // Null Terminierung
    Serial.print("IndexNr "); Serial.print(i); 
    Serial.print("\t String Size: "); Serial.print(sizeof(Stringvar));
    Serial.print("\t Laenge,Zeichenanzahl: "); Serial.print(strlen(Stringvar)); 
    Serial.print("\t Inhalt: "); Serial.println(Stringvar);  
  }  
  
  Serial.println();
  
  // an Indexnr. 6  Null Terminierung
  Stringvar[6] = '\0';  
  Serial.print("Stringvar:  "); Serial.print(Stringvar); Serial.print("  "); Serial.print(sizeof(Stringvar)); 
  Serial.print("  Laenge,Zeichenanzahl:  "); Serial.println(strlen(Stringvar)); 
  
  // an Indexnr. 0  Null Terminierung  
  Stringvar[0] = '\0';  
  Serial.print("Stringvar:  "); Serial.print(Stringvar); Serial.print("\t    "); Serial.print(sizeof(Stringvar)); 
  Serial.print("  Laenge,Zeichenanzahl:  "); Serial.println(strlen(Stringvar)); 
}

char string[15]; // gibt an, dass das Array 15 Elemente hat
string[0-14]; // Das erste Element hat die 0, das letzte 15-1

Hallo,

okay, alles klar, so schnell kann es gehen. Muß ich mir irgendwie merken oder besser notieren.

Anders gefragt. Definiere ich die höchste Indexnummer oder max. Anzahl der Zeichen?

Wenn es darum geht ob man den Terminator mit zählt oder nicht: Das ist Geschmackssache. Kann man so oder so machen

Das ist ein Array für 15 'char':
char Stringvar[15];

Wie bei allen anderen Arrays auch, geht der Array-Laufindex dann von 0 bis 14 (=15 verschiedene).

Der String selbst kann eine maximale Länge von 14 Zeichen haben, plus das abschließende Nullzeichen '\0'. Dann würden die 14 Zeichen des Strings in Stringvar[0] bis Stringvar[13] stehen und das abschließende Nullzeichen in Stringvar[14].

Hallo,

@ Serenifly: genau das Gedankenproblem wollte ich aus der Welt schaffen

@ jurs: diesmal bekommst Du den Zuschlag für die gute eindeutige Erklärung. :slight_smile:

Wenn ich das Array als String nutze muß ich eins Luft lassen für den Null Terminator. Das hatte mir mal Serenifly schön erklärt. Ich kam nur wieder mit der Indexnummer und der Gesamtanzahl durcheinander.

Danke allen. Jetzt ist alles klar.

Hallo,

irgendwie komme ich aus der Null Nummer nicht so leicht raus.

Ich bin auf ein kleines Problem gestoßen, welches mir vielleicht jemand erklären kann.

Eigentlich sollte alles eine Ausgabe der Ziffer 0 erzeugen.

char String_4[] = "0";    // Ziffer 0
char String_5[] = {'0'};  // Ziffer 0
char String_6[] = {48};   // ASCI Code 48, gleich Ziffer 0

String_5 ergibt jedoch immer eine "00".

eine

char String_5[] = {'A'};  // Zeichen A

ergibt dann "A0"

Warum das denn jetzt?

5 und 6 sind nicht terminiert. Das sind Arrays mit einem Element. Bei 4 wird die Terminierung automatisch gemacht, da du ein String-Literal verwendet hast. Bei 5 läuft die String-Verarbeitung dann in Array 6 hinein, da die im RAM direkt hintereinander stehen. Deshalb die zweite '0'. Da bist du auch genau darauf gestoßen, weshalb Arrays in C so fehleranfällig sind.

Korrekt:

char String_5[] = { 'A', '\0' };

Doc_Arduino:

char String_4[] = "0";    // Ziffer 0

char String_5[] = {'0'};  // Ziffer 0
char String_6[] = {48};   // ASCI Code 48, gleich Ziffer 0




String_5 ergibt jedoch immer eine "00".

eine


char String_5[] = {'A'};  // Zeichen A




ergibt dann "A0"

Warum das denn jetzt?

Weil ein char-Array in C manchmal eben auch nur ein "array of char" ist, ohne deshalb ein "string" zu sein.

Dieses char-Array ist ein string:

char String_4[] = "0";    // Ziffer 0

Bestehend aus einem ASCII-Zeichen Ziffer '0' und einem abschließenden Nullzeichen.
Belegter Speicherplatz: 2 Bytes

Dies ist ein char-Array mit einem einzigen char-Element:

char String_5[] = {'0'};  // Ziffer 0

==> kein String, da kein abschließendes Nullzeichen vorhanden

Dito:

char String_6[] = {48};   // ASCI Code 48, gleich Ziffer 0

[/code]
==> kein String, da kein abschließendes Nullzeichen vorhanden

Merke: Ein String ist ein String ist ein String. Dann muß er aber auch als String definiert werden, eingeschlossen in doppelte Anführungszeichen. Oder wenn ein char-Array definiert werden soll, das später nicht als char-Array sondern als String verwendet werden soll, dann mußt Du dafür sorgen, dass als abschließendes Zeichen ein Nullzeichen vorhanden ist.

Wenn nur ein char-Array hast, ohne abschließendes Nullzeichen, und dieses dann mit Stringfunktionen verarbeitest, bekommst Du ganz schnell einen Buffer-Overflow. Beim lesenden Zugriff führt das nur zu Datenmatsch. Beispielsweise wenn Du ein nicht-nullterminiertes Char-Array versuchst wie einen String mit print auszugeben, dann wird solange hinter dem eigentlichen Array weitergelesen, bis das erste Nullzeichen im Speicher gefunden wird. Wenn Du das bei schreibendem Zugriff machst, überschreibst Du RAM-Bereiche, die zu anderen Variablen gehören, mit unvorhersehbaren Auswirkungen auf das laufende Programm.

Hallo,

oh man, warum immer Kleinigkeiten zu größeren Effekten führen.

char String_5[] = {'0'};
und
char String_5[1] = {'0'};
ergibt immer Ausgabe 00

char String_5[2] = {'0'};
ergibt 0

Vielleicht Zufall wenn immer noch die Null Terminierung fehlt. Oder weil Index 1 automatisch oder zufällig Null terminiert wird.

Nur dann ist das Example Str2 auch fehlerhaft und dürfte nicht so beschrieben sein. Oder?
http://www.arduino.cc/en/pmwiki.php?n=Reference/String

dann wäre das alles so richtig?

char String_4[] = "0";        // Ziffer 0
char String_5[] = {'0', '\0'};  // Ziffer 0
char String_6[] = {48, '\0'};   // ASCI Code 48, gleich Ziffer 0

Das ist kein Fehler:

char Str2[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o'};

Aus dem ganz einfachen Grund dass die Array Länge angegeben ist. Man definiert ein Array der Größe 8, aber initialisiert nur 7 Position. Der Rest ist automatisch Null. Steht so auch dort:

Declare an array of chars (with one extra char) and the compiler will add the required null character, as in Str2

Davon würde ich aber absehen. Besser man gibt alles an und lässt den Compiler die Array Größe bestimmen. Es gibt zwar Anwendung dafür (es kann sein dass man später mal mehr Speicher braucht, also macht man das Array größer), aber allgemein ist es etwas undeutlich.

Hier kann ich übrigens Visual Studio und Visual C++ empfehlen. Array definieren. Breakpoint setzen. Code laufen lassen und den Speicher im Debugger ansehen. Da sieht man sofort wie groß ein Array ist und was drin steht.

Doc_Arduino:
char String_5[2] = {'0'};
ergibt 0

Das ist ja auch mehr oder weniger "zufällig" ein korrekter String.

Globale Variablen werden per Definition ausgenullt, wenn sie nicht initialisiert werden. Mit 'char String_5[2]' definierst Du ein char-Array mit der Größe 2 und initialisierst das erste Zeichen. Das zweite (nicht initialisierte) ist damit automatisch ein Nullzeichen. Und es befindet sich am Ende. Also ist es nicht nur ein char-Array, sondern auch ein nullterminierter String.

Doc_Arduino:
dann wäre das alles so richtig?

char String_4[] = "0";        // Ziffer 0

char String_5[] = {'0', '\0'};  // Ziffer 0
char String_6[] = {48, '\0'};   // ASCI Code 48, gleich Ziffer 0

Diese drei Deklarationen sind swohl initialisierte char-Arrays (aus je zwei Zeichen) als auch alle drei Strings, die mit den Stingfunktionen der AVR LIBC verarbeitet werden können.

Du mußt Dich glaube ich mal von dem Gedanken freimachen, dass ein char-Array immer auch ein String ist und als solcher Verwendung finden muß. Manchmal sind char-Arrays in einem Programm auch einfach nur char-Arrays, die nur mit Arrayfunktionen oder Schleifen vererbeitet werden. Nur wenn Du Stringfunktionen verwenden möchtest, dann müßte das char-Array tatsächlich auch ein nullterminierter String sein und spätestens das letzte Zeichen im Array müßte ein Nullzeichen sein.

Wenn Du keine Stringfunktionen verwenden möchtest, kann ein Array aber aus ganz beliebigen Zeichen bestehen, ein Nullzeichen braucht nicht darunter zu sein.

Hallo,

aha, jetzt ist alles klar. Oder sollte ich besser schreiben zur Zeit ist alles klar. :slight_smile:

Danke Euch beiden für die prima Erklärungen. Ihr macht das immer sehr schön.
Ich mache mir dazu eigene Notizen damit ich mir das besser merken kann.

Kannst Du mir bitte gleich ein Code Bsp. geben für Visual Studio? Damit ich darin nicht bei Null anfangen muß mit Code tippen und Syntaxfehler usw.

Um das deutlich zu machen:
Der Datentyp für ein Byte in C ist "unsigned char". Das Schlüsselwort "byte" in Arduino ist lediglich ein typedef darauf. Aber das ist kein Standard C/C++.

Wenn man also vorzeichen-behaftete 8-Bit Werte von -127 bis 128 braucht, hat man auch den Datentyp char. Und ein Array daraus ist auch ein char[]. Aber kein C String.

Kannst Du mir bitte gleich ein Code Bsp. geben für Visual Studio? Damit ich darin nicht bei Null anfangen muß mit Code tippen und Syntaxfehler usw.

Das ist Standard C++. Ein Array ist da auch nichts anderes wie auf dem Arduino. Wenn du ein Projekt anlegst, kannst du das so einstellen dass die main() Methode automatisch erzeugt wird.

Zwei Tips vielleicht:
1.) am Ende von main() ein getchar() zu machen. Dann geht das Konsolenfenster erst zu wenn man eine Taste drückt.
2.) für Ausgaben google nach "C++ cout"

Hallo,

okay. Das in VS werde ich versuchen. Für heute ist erstmal Schluss '\0' :slight_smile:

Ist kein Muss. Aber um solche Grundlagen zu lernen kann es einfacher als der Arduino sein.

In dem Bild habe ich den String deklariert, dann einen Breakpoint gesetzt. Wenn man dann das Programm laufen lässt kann mit der Maus über jede Variable gehen um zu sehen was da drin steht.

P.S.:
Die ganze #include Sachen am Anfang sind nicht unbedingt nötig. cstring.h oder stdlib.h brauchst du aber wenn du bestimmte Funktionen möchtest. Wie auf dem Arduino auch, nur dass die von der IDE automatisch inkludiert werden.

Debug.jpg