Forum: Compiler & IDEs Zuweisung von Strings an ein Array


von Alois (Gast)


Lesenswert?

Einen wunderschönen Sonntag wünsche ich allen!


Ich wende mich an euch, weil ich irgendwie ein kleines 
Verständnisproblem habe an dem ich jetzt doch schon einige Zeit sitze 
und irgendwie nicht weiterkomme.
Es geht um die Wandlung diverser uint8_t / uint16_t in das ASCII-Format 
mit anschließender Ausgabe auf ein Display und Versendung der Daten über 
USART.

Hier mal der Code:
1
volatile uint8_t phi_eps[4] = {0,0,0,0};
2
volatile uint16_t t_eps[4] = {0,0,0,0};
3
volatile uint16_t t_eps_old[4] = {0,0,0,0};
4
5
char n_lok_str[7];
6
char phi_eps_str[4][4];
7
char t_eps_str[6][4];
8
char t_eps_old_str[6][4];
9
10
void foo(uint8_t k)    {
11
  
12
    itoa(phi_eps[k], &phi_eps_str[k*4],10);
13
    lcd_setcursor(5,k+1);
14
    lcd_string(&phi_eps_str[k*4]);
15
16
    if ( senden_OK )    {
17
        char sekunden_str[6];
18
        char millsekunden_str[6];
19
20
        utoa(t_eps[k], t_eps_str[k*6], 10);
21
        utoa(t_eps_old[k], t_eps_old_str[k*6], 10);
22
        
23
        utoa(t_eps_sek[k], sekunden_str, 10);
24
        utoa(t_eps_millisek[k], millsekunden_str,10);
25
      
26
        uart_puts(n_lok_str);
27
        uart_putc(32);
28
        uart_puts(sekunden_str);
29
        uart_putc(46);          // Punkt
30
        uart_puts(millsekunden_str);
31
        uart_putc(32);
32
33
        for (i=0; i<4; i++) {
34
            uart_puts(&phi_eps_str[i*4]);
35
            uart_putc(32);
36
            uart_puts(&t_eps_str[i*6]);
37
            uart_putc(32);
38
            uart_puts(&t_eps_old_str[k*6]);
39
            uart_putc(32);
40
        }
41
42
  }
43
}
44
45
int main(void)  {
46
    foo(1);
47
    .
48
    .
49
    foo(4)
50
}

Auf meinem Terminal kommt da nur noch Murx raus. Ist es ein Problem, 
dass ich mehrere Strings in die Arrays schreibe, oder besser gesagt so 
wie ich sie da reinschreibe?
Vielleicht sitze ich einfach schon zu lange davor und muss mal ein 
kurzes Päuschen machen. Für den einen oder anderen Tip wäre ich jedoch 
sehr dankbar.

Ciao,
Alois

von ich rate mal (Gast)


Lesenswert?

itoa(phi_eps[k], &phi_eps_str[k*4],10);
                 ^
utoa(t_eps[k],  t_eps_str[k*6], 10);
               ^

?

von Helfer (Gast)


Lesenswert?

Exemplarisch:

> char phi_eps_str[4][4];
>   foo(4)
>   itoa(phi_eps[k], &phi_eps_str[k*4],10);

=>

    itoa(phi_eps[k], &phi_eps_str[16],10);
                                  ^^

Widerspruch zu

phi_eps_str[4][4]
            ^
1) Warum verwendest du nicht
itoa(phi_eps[k], phi_eps_str[k], 10);
oder
itoa(phi_eps[k], &phi_eps_str[k][0], 10);

2) Der Platz in den Strings ist mit 4 Zeichen (3 Nutzzeichen plus 
abschliessendes Nullbyte) zu knapp. itoa liefert trotz uint8_t 
phi_eps[k] Argument u.U. ein Vorzeichen, d.h. ein -123 sprengt schon 
dein Array. utoa wäre eventuell sinnvoller.

von Stefan E. (sternst)


Lesenswert?

Was war denn dein Gedanke hinter den ganzen Multiplikationen bei den 
Indizes?

von Helfer (Gast)


Lesenswert?

Korrektur eines weiteren systematischen Fehlers in deinem Programm: 
(Arrayindices starten in C bei 0)

1) Warum verwendest du nicht
itoa(phi_eps[k-1], phi_eps_str[k-1], 10);
oder
itoa(phi_eps[k-1], &phi_eps_str[k-1][0], 10);

von Alois (Gast)


Lesenswert?

@ich rate mal

Oh man, das ist ja schon mal ein dämlicher Fehler...

@Helfer

Ich dachte, wenn ich keine signed Werte verwende, dann geht das in 
Ordnung für einen uint8_t Wert 4 Byte im String zu verwenden. Ok wieder 
was gelernt.

Ja das mit dem Arrayindicesstart bei 0 habe ich durcheinandergebracht. 
Verdammtes Matlab.
Aber &phi_eps_str[12] wäre doch in meinem Fall dasselbe wie
&phi_eps_str[0][4] oder?

Die Multiplikationen dienen eigentlich nur dafür, dass ich die 
vorliegenden uint8_t sowie uint16_t Werte in einer Schleife in die 
Stringarrays verbannen kann. Geht das irgendwie geschickter?

Dankeschön schon mal an Alle.

von Helfer (Gast)


Lesenswert?

> Aber &phi_eps_str[12] wäre doch in meinem Fall dasselbe wie
> &phi_eps_str[0][4] oder?

Nee.

In &phi_eps_str[0][4] darfst du den Index 4 nicht benutzen. Maximal 3, 
weil du nur 4 Elemente dimensioniert hast.

Im Speicher können (*) die Elemente von phi_eps_str so liegen

2D addressiert:
0-0 0-1 0-2 0-3 1-0 1-1 1-2 1-3 2-0 2-1 2-2 2-3 3-0 3-1 3-2 3-3

Linear adressiert
0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15

char * t = phi_eps_str;
&t[12] bzw. t+12 ist dann das Element 3-0 also &phi_eps_str[3][0]

(*) können, denn ich weiss nicht, ob es zwingend vorgeschrieben ist, 
dass der Compiler die so anordnet. Es wäre auch denkbar, dass der 
Compiler die nicht linear hintereinander anordnet (wie man es oft macht, 
wenn man selbst mit malloc ein 2D Array aufbaut) oder z.B. Füllmaterial 
benutzt, um auf Wortgrenzen zu kommen.

Ich merke aber gerade, dass ich oben mit itoa(phi_eps[k-1], 
phi_eps_str[k-1], 10); einen Fehler gemacht habe. Ich würde dem ganzen 
Gehuddel aus dem Weg gehen und sauber so auf die einzelnen Zielstrings 
zugreifen.

utoa(phi_eps[k-1], &phi_eps_str[k-1][0], 10);

Die richtige Berechnung der Zugriffsposition macht der Compiler. 
Vermutlich schon zur Kompilierzeit, so dass es keine Performanceeinbußen 
und Zwang zur "Handoptimierung" gibt.

von Helfer (Gast)


Lesenswert?

> Die Multiplikationen dienen eigentlich nur dafür, dass ich die
> vorliegenden uint8_t sowie uint16_t Werte in einer Schleife in die
> Stringarrays verbannen kann. Geht das irgendwie geschickter?

Ja, indem du mit dem Speicher nicht so verschwenderisch bist. Für die 
Ausgabe von 12 Werten (4 x 8-Bit und 8 x 16-Bit) verschleuderst :-) du 
64 Bytes an statischem Pufferspeicher.

Ich würde dafür einen lokalen Pufferspeicher in foo() benutzen, der 
jeweils einen Ausgabestring enthält. Statt zuerst alle Zahlen in Strings 
zu wandeln und dann alle Strings auszugeben würde ich die Wandlung 
just-in-time machen, also eine Zahl wandeln, ausgeben, nächste Zahl 
wandeln etc.

von Helfer (Gast)


Lesenswert?

> volatile uint8_t phi_eps[4] = {0,0,0,0};
> volatile uint16_t t_eps[4] = {0,0,0,0};
> volatile uint16_t t_eps_old[4] = {0,0,0,0};

Wenn diese Array z.B. in einer Timer-ISR gefüllt werden dann daran 
denken, dass du atomare Zugriffe brauchst, damit du die Daten konsistent 
ausliest. Näheres im Artikel Interrupt

von Alois (Gast)


Lesenswert?

Halloechen nochmal,

so ich habe mich jetzt noch mal in Ruhe rangesetzt.
Oben war ein Fehler von mir:

Ich rufe natürlich nicht:
1
 
2
foo(1);
3
.
4
.
5
foo(4):
sondern:
1
foo(0);
2
.
3
.
4
foo(3):
auf.

Nochmal zu dem 2D-Adressierten, ich dachte immer das waere genau anders 
herum, also:

0-0 1-0 2-0 .... 3-4 4-4
 0   1   2        14  15

Und wegen dem atomaren Zugriff:
Das Sperren und wieder Erlauben der Interrupts erledigt doch gcc oder?

Dankeschön,
Alois

von Helfer (Gast)


Lesenswert?

> Nochmal zu dem 2D-Adressierten, ich dachte immer das waere genau anders
> herum

Genau dieses Denken, überlasse ich dem Compiler :-)

> Das Sperren und wieder Erlauben der Interrupts erledigt doch gcc oder?

Nein, macht er nicht. Das ist deine Aufgabe ihm das zu sagen.

von Alois (Gast)


Lesenswert?

Aber wenn ich das laut Datenblatt richtig verstehe, dann hat doch der 
ATMega sowieso keine Interruptpriorisierung, d.h. bei jedem gerade 
abgearbeitetem Interrupt, kann auch ein anderer auftreten. Erst nach 
Beendigung des aktuellen Interrupts kann zu dem neuen gesprungen werden 
oder?

Habe auch nochmal im Tutorial nachgeschaut, da steht es auch so drin. 
Was ist denn jetzt richtig?
Ich bin verwirrt.

von Helfer (Gast)


Lesenswert?

Darum geht es nicht, das ist alles richtig.

Es geht darum, dass deine nicht-interrupt-Funktion foo durch einen 
Interrupt unterbrochen wird und die Interruptfunktion ein oder mehrere 
Elemente der Arrays ändert. Kommt die foo dann wieder zum Zug, hat der 
Code vor dem Interrupt ein paar alte Inhalte gelesen und der Code nach 
dem Interrupt liest den Rest als neue Inhalte. Welcher Inhalt ist jetzt 
der "richtige" Inhalt deiner Variablen?

Bei den 16-Bit Inhalten (die du ja auch hast) kann dieses alt/neu 
Problem sogar innerhalb eines Elements auftreten - eine Hälfte 8-Bit hat 
den alten Wert, eine Hälfte den neuen Wert. Hat z.B. die ISR eine 16-Bit 
Variable von 0x00FF auf 0x0100 um eins erhöht, könnte dein foo einen von 
vier Inhalten sehen: 0x00FF (gelesen vor dem Interrupt), 0x0100 (gelesen 
nach dem Interrupt), 0x01FF (Leseoperation durch Interrupt unterbrochen) 
und 0x0000 (Leseoperation durch Interrupt unterbrochen)... ein 
Ziemlicher Unterschied oder?

von Alois (Gast)


Lesenswert?

Ja, da hast du Recht. So rum habe ich das noch gar nicht betrachtet.

Ich muss mal sehen, ob ich das noch einbaue. Rein theoretisch dürfte 
nämlich nichts passieren. Die Zeitvariablen sekunde und millsekunde 
fliegen noch raus.
Die Funktionen foo(0)...foo(3) werden nur nach einem Flag, welches in 
dem jeweiligen Interrupt gesetzt wird, abgearbeitet. Dann kommt der 
Interrupt eine, für den Mikrocontroller lange, Zeit nicht mehr.

Aber trotzdem noch mal vielen Dank für alles, war sehr informativ.

Grüße
Alois

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.