Forum: Compiler & IDEs Komische Werte im char array


von Andi (Gast)


Lesenswert?

Hallo Leute!

Ich möchte aus einem gegebenen char-Array einen bestimmten Teil 
ausschneiden. Dazu suche ich mit einer for-Schleife nach einem 
Trennzeichen (in diesem Fall '.'). Der Inhalt vom Anfang des char-Arrays 
bis zu dem character vor dem Trennzeichen soll über eine weitere 
for-Schleife in einem neuen char-Array gespeichert werden.
Später soll dann mithilfe von atoi() der Inhalt des Arrays in eine 
integer-Zahl umgewandelt werden. Das funktioniert auch soweit.

Allerdings sehe ich beim Debuggen einen komischen Wert im char-Array der 
immer wieder auftaucht. Hier erstmal mein Code:
1
#define SERIAL_BUFFER_SIZE 10
2
3
int main(void) {
4
  const char data[SERIAL_BUFFER_SIZE] = {'1','2','3','4','5','6','.','7','8','\0'};
5
  uint8_t indexDot, length;
6
7
  for(uint8_t i = 0; i < SERIAL_BUFFER_SIZE; i++) {
8
    if (data[i] == '.') {
9
      indexDot = i;
10
    } else if (data[i] == '\0') {
11
      length = i + 1;
12
    }
13
  }
14
15
  char foo1[indexDot];
16
  memset(foo1,0,indexDot); //nullen
17
  for(uint8_t i = 0; i < indexDot; i++) {
18
    foo1[i] = data[i];
19
  }
20
21
  char foo2[SERIAL_BUFFER_SIZE-indexDot-2];
22
  memset(foo2,0,SERIAL_BUFFER_SIZE-indexDot-2); //nullen
23
  for(uint8_t i = 0; i < SERIAL_BUFFER_SIZE-indexDot-1; i++) {
24
        f002[i] = data[indexDot+1+i];
25
  }
26
27
  //Conversion to integer
28
  uint32_t integer_foo1 = atoi(foo1);
29
  uint32_t integer_foo2 = atoi(foo2);
30
}

Die erste for-Schleife findet die Länge des Arrays heraus (hier 
eigentlich unnötig, da sowieso fest) und die Stelle des Trennzeichens 
'.'.

Nachdem aber die zweite for-Schleife durchlaufen ist, wird im letzten 
Schleifendurchlauf immer noch ein '@' an die letzte Stelle hinzugefügt. 
D.h. ich erhalte dann "123456@". Bei der letzten for-Schleife 
funktioniert wieder alles bestens.

Weiß jemand woher dieses Zeichen kommt?

Eine weiter Frage: Ist das Nullen der Arrays mit memset() überhaupt 
nötig? Wie sieht das ganze bei µC aus?

Schon mal vielen Dank! :)

von Peter II (Gast)


Lesenswert?

Andi schrieb:
> Weiß jemand woher dieses Zeichen kommt?

ja weil du keine ende vom String hast. Es fehlt ein 0 Byte.

Aber das ganze ist viel zu kompliziert!

das ganze umkopieren ist überhaupt nicht notwenig, atoi hört von selber 
auf zu lesen wenn kein Digit mehr kommt.

von Andi (Gast)


Lesenswert?

Peter II schrieb:
> Andi schrieb:
>> Weiß jemand woher dieses Zeichen kommt?
>
> ja weil du keine ende vom String hast. Es fehlt ein 0 Byte.
>
> Aber das ganze ist viel zu kompliziert!
>
> das ganze umkopieren ist überhaupt nicht notwenig, atoi hört von selber
> auf zu lesen wenn kein Digit mehr kommt.

Warum ist dann beim Array foo2 nie so ein Zeichen am Ende?

Danke für den Hinweis mit atoi! Da kann ich mir die Hälfte sparen ;)

von Bitflüsterer (Gast)


Lesenswert?

1
#include "string.h"
2
3
#define SERIAL_BUFFER_SIZE 10
4
5
int main(void) {
6
  char data[SERIAL_BUFFER_SIZE] = {'1','2','3','4','5','6','.','7','8','\0'};
7
  uint8_t indexDot;
8
9
  dot = strnchr (data, SERIAL_BUFFER_SIZE, '.');
10
  data [dot] = '\0';
11
12
  uint32_t integer_foo1 = atol(data);
13
  uint32_t integer_foo2 = atol(&data[dot + 1]);

Fehlerbehandlung fehlt natürlich noch.

von Bitflüsterer (Gast)


Lesenswert?

Wobei atol natürlich auch ein wenig blöd ist, wenn Du den vollen 
Wertebereich von uint32_t nutzen willst. Aber gut. Das Ganze ist sowieso 
ein Blödsinn - äh, ein Übungscode zum wegschmeissen, da schadet das nun 
auch nichts.

von A. H. (ah8)


Lesenswert?

An dieser Stelle sollte Dein Compiler laut meckern:
1
  char foo1[indexDot];

Wenn Du ein Feld definieren willst muss die Größe zur Compilezeit 
bekannt sein. indexDot ist aber eine Variable. Definiere das Feld auch 
zu SERIAL_BUFFER_SIZE und probiere es noch mal.

Die schon erwähnte Bemerkung mit dem fehlenden '\0' Zeichen stimmt 
natürlich auch. Du müsstest mindestens indexDot + 1 als Feldgröße 
anlegen.

: Bearbeitet durch User
von Andi (Gast)


Lesenswert?

A. H. schrieb:
> An dieser Stelle sollte Dein Compiler laut meckern:
>   char foo1[indexDot];
>
> Wenn Du ein Feld definieren willst muss die Größe zur Compilezeit
> bekannt sein. indexDot ist aber eine Variable. Definiere das Feld auch
> zu SERIAL_BUFFER_SIZE und probiere es noch mal.
>
> Die schon erwähnte Bemerkung mit dem fehlenden '\0' Zeichen stimmt
> natürlich auch. Du müsstest mindestens indexDot + 1 als Feldgröße
> anlegen.

Der Compiler sagt dazu nichts...seltsam. Ich dachte die Terminierung mit 
dem '\0'-Zeichen wird nur für die Interpretation als String benötigt?

von Peter II (Gast)


Lesenswert?

A. H. schrieb:
> An dieser Stelle sollte Dein Compiler laut meckern:
>   char foo1[indexDot];
>
> Wenn Du ein Feld definieren willst muss die Größe zur Compilezeit
> bekannt sein.

nein muss es nicht. Im Aktuellen C und C++ ist das zulässig.

von Peter II (Gast)


Lesenswert?

Andi schrieb:
> Ich dachte die Terminierung mit
> dem '\0'-Zeichen wird nur für die Interpretation als String benötigt?

sie immer notwendig wenn man in C mit Strings arbeitet. Woher soll denn 
sonst atoi wissen wo schluss ist.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Bitflüsterer schrieb:
> #include "string.h"
>
> #define SERIAL_BUFFER_SIZE 10
>
> int main(void) {
>   char data[SERIAL_BUFFER_SIZE] =
> {'1','2','3','4','5','6','.','7','8','\0'};
>   uint8_t indexDot;
>
>   dot = strnchr (data, SERIAL_BUFFER_SIZE, '.');
>   data [dot] = '\0';
>
>   uint32_t integer_foo1 = atol(data);
>   uint32_t integer_foo2 = atol(&data[dot + 1]);
>
> Fehlerbehandlung fehlt natürlich noch.

data [dot] = '\0';

ist nicht notwendig, kann sogar stören. Wenn man mit const arbeitet.

von A. H. (ah8)


Lesenswert?

Peter II schrieb:
> A. H. schrieb:
>> An dieser Stelle sollte Dein Compiler laut meckern:
>>   char foo1[indexDot];
>>
>> Wenn Du ein Feld definieren willst muss die Größe zur Compilezeit
>> bekannt sein.
>
> nein muss es nicht. Im Aktuellen C und C++ ist das zulässig.

OK, man lernt nie aus, meine Standards sagen alle noch etwas anderes. 
(Sind aber nicht mehr die neuesten :-) Obwohl ich mir nicht vorstellen 
kann, wie der Compiler das macht. Zumindest dürfte es ja im gleiche 
Stack-Frame keine weiteren Variablen dahinter mehr geben?!

Oder macht er daraus implizit Pointer, initialisert sie zur Laufzeit mit 
dem Stackpointer und schiebt den einfach entsprechend weiter?

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

A. H. schrieb:
> OK, man lernt nie aus, meine Standards sagen alle noch etwas anderes.
> (Sind aber nicht mehr die neuesten :-)

VLA (Variable Length Array) gibt es seit C99.

Also auch schon seit 15 Jahren.

von A. H. (ah8)


Lesenswert?

Dirk B. schrieb:
> A. H. schrieb:
>> OK, man lernt nie aus, meine Standards sagen alle noch etwas anderes.
>> (Sind aber nicht mehr die neuesten :-)
>
> VLA (Variable Length Array) gibt es seit C99.
>
> Also auch schon seit 15 Jahren.

Der letzte Standard (C++), den ich hier habe ist von 2003. Angesichts 
der Zeit, die viele Compiler brauchen, um neue Features zuverlässig zu 
implementieren, erachte ich das für die meisten Fälle auch noch als 
ausreichend. (Da wird für die Deklaration eines Arrays auch noch ein 
konstanter Ausdruck verlangt. Wenn es auf die Portabilität des Codes 
ankommt würde ich in diesem Fall auch versuchen, mich darauf zu 
beschränken.)

von Kaj (Gast)


Lesenswert?

Peter II schrieb:
> nein muss es nicht. Im Aktuellen C und C++ ist das zulässig.
1
#include <stdint.h>
2
3
int main(void)
4
{
5
  uint8_t i;
6
  uint8_t array[i];
7
  for(i = 0; i < 10; i++)
8
  {
9
    array[i] = 0;
10
  }
11
  return 0;
12
}
mit gcc 4.8.1, -Wall -Werror -pedantic -pedantic-errors -std=c++11 
compiliert gibt:
1
error: ISO C++ forbids variable length array 'array' [-Wvla]
Scheint also doch nicht ganz so zulässig zu sein.

Andi schrieb:
> Der Compiler sagt dazu nichts...seltsam.
Der compiler sagt da nur was zu wenn du mit -pedantic compilierst. Ohne 
-pedantic hält er schön die klappe.

Grüße

von Tom K. (ez81)


Lesenswert?

Kaj schrieb:
> Scheint also doch nicht ganz so zulässig zu sein.

In C99 schon, in C++ bisher nur als gcc-Erweiterung:

"Variable-length automatic arrays are allowed in ISO C99, and as an 
extension GCC accepts them in C90 mode and in C++."

von Peter II (Gast)


Lesenswert?

Kaj schrieb:
> mit gcc 4.8.1, -Wall -Werror -pedantic -pedantic-errors -std=c++11
> compiliert gibt:error: ISO C++ forbids variable length array 'array'
> [-Wvla]
> Scheint also doch nicht ganz so zulässig zu sein.

bei C geht es

-Wall -Werror -pedantic -pedantic-errors -std=c99

test.c:6:11: error: variable 'array' set but not used 
[-Werror=unused-but-set-variable]
   uint8_t array[i];

von Kaj (Gast)


Lesenswert?

Peter II schrieb:
> bei C geht es
Ja, habs grad festgestellt, liegt an der stelle am c++ compiler :-/

Tom K. schrieb:
> in C++ bisher nur als gcc-Erweiterung
Danke für den hinweis

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Tom K. schrieb:
> Kaj schrieb:
>> Scheint also doch nicht ganz so zulässig zu sein.
>
> In C99 schon, in C++ bisher nur als gcc-Erweiterung:
>
> "Variable-length automatic arrays are allowed in ISO C99, and as an
> extension GCC accepts them in C90 mode and in C++."

Und um es noch komplizierter zu machen: In C11 ist das Feature nur noch 
optional.

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.