Forum: Mikrocontroller und Digitale Elektronik C Übergabe mit Zeiger


von M. M. (blackcow)


Lesenswert?

Hallo Forum!

Ich bin mal wieder am Verzweifeln mit C. Ich habe ein Problem mit der 
Übergabe by reference. Die Funktion g_printc gibt ein Zeichen auf meinem 
LCD aus. Das auszugebende Zeichen wird per Zeiger von test() übergeben 
(*ichar). Nur leider funktioniert das nicht. Ich habe schon versucht mit 
static test() und volatile... Funktioniert alles nicht und ich weiß 
nicht warum. Könnte da bitte mal jemand drüberschauen? Warscheinlich 
sehe ich den Baum vor lauter Wäldern nicht und es ist ein saudummer 
Fehler.

1
///define font arrays
2
  //font0 4x2
3
  const uint8_t font0[10] PROGMEM = {153, 240, 219, 249, 231, 189, 223, 241, 255, 251};
4
5
[...]
6
7
void g_printc(uint8_t x, uint8_t y, uint8_t font, char *ichar){
8
  switch(font){
9
    case 0: ;
10
      char test=*ichar;
11
      uint8_t *charflash=font0+test;
12
      g_crect(x-1, y-1, x+3, y+5);
13
      for(uint8_t iy=0; iy<4; ++iy){
14
        if(*charflash&(1<<iy))
15
          g_point(x, y+iy);
16
      }
17
      for(uint8_t iy=0; iy<4; ++iy){
18
        if(*charflash&(1<<iy+4))
19
          g_point(x+1, y+iy);
20
      }
21
      g_crect(10, 5, 12, 20);
22
      for(int i=0; i<8; ++i){
23
        if(*charflash&(1<<i))
24
          g_point(10, 6+i);
25
      }
26
      break;
27
    case 1: ;
28
      break;
29
    case 2: ;
30
      break;
31
  }
32
  return;
33
}
34
35
[...]
36
37
void test(){
38
  g_frect(0,0,120,30);
39
  char test=1;
40
  g_printc(5,5,0,&test);
41
  lcd_refresh();
42
  return;
43
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die Übergabe von "test" ist soweit korrekt.

Deine Funktion erwartet einen Pointer (char *ichar), Du übergibst ihr 
beim Aufruf die Adresse von "test" (g_printc(5, 5, 0, &test))

Ist zwar vollkommen unklar, was das soll (Du könntest Deinen einzelnen 
char auch direkt als Wert übergeben), aber korrekt ist es trotzdem.

Woran machst Du fest, daß "das nicht funktioniert"?

Könnte es sein, daß Deine Zugriffe auf Deine Zeichentabelle nicht 
funktionieren, weil Du PROGMEM und normale Speicherzugriffe mischt?

von Dirk B. (dirkb2)


Lesenswert?

M. M. schrieb:
> Das auszugebende Zeichen wird per Zeiger von test() übergeben
> (*ichar).
Warum über Zeiger?

> Nur leider funktioniert das nicht.
Doch, das funktioniert.

Das Problem ist nicht der Zeiger von ichar.

von M. M. (blackcow)


Lesenswert?

Rufus Τ. F. schrieb:

> Ist zwar vollkommen unklar, was das soll (Du könntest Deinen einzelnen
> char auch direkt als Wert übergeben), aber korrekt ist es trotzdem.

Ich mach eine zweite Funktion printf, die bekommt einen Zeiger auf ein 
String-Array. Die druckt dann die einzelnen Zeichen eben mit dem printc. 
Ich meine das dies effizienter ist als die Variablen zu kopieren.

> Woran machst Du fest, daß "das nicht funktioniert"?

Wenn ich in die Funktion printc nicht den Zeiger verwende, sondern eine 
Variable erstelle, in diesem Fall mit char testvariable=1;, dann wird 
das richtige Zeichen ausgegeben.

> Könnte es sein, daß Deine Zugriffe auf Deine Zeichentabelle nicht
> funktionieren, weil Du PROGMEM und normale Speicherzugriffe mischt?

Wie meinst du das? Ist die Vorgehensweise korrekt:
const uint8_t a PROGMEM = 1;
uint8_t test;
test=a;

von Dirk B. (dirkb2)


Lesenswert?

M. M. schrieb:
> Die druckt dann die einzelnen Zeichen eben mit dem printc.
> Ich meine das dies effizienter ist als die Variablen zu kopieren.
Da täuscht du dich.

Ein Zeiger braucht i.A. mehr Speicher als ein Byte.
Und somit müssen mehr Daten kopiert werden als bei der direkten 
Übergabe.

Das Problem ist dann auch nicht ichar sondern charflash.

von M. M. (blackcow)


Lesenswert?

M. M. schrieb:
> void g_printc(uint8_t x, uint8_t y, uint8_t font, char *ichar){
>   switch(font){
>     case 0: ;
        char test2=1;
        char *test3=&test2;
>       char test=*test3;
>       uint8_t *charflash=font0+test;
>       g_crect(x-1, y-1, x+3, y+5);
>       for(uint8_t iy=0; iy<4; ++iy){
>         if(*charflash&(1<<iy))
>           g_point(x, y+iy);
>       }
>       [...]

Hier funktioniert es wie gewünscht, damit ihr wisst was ich meine. Liegt 
es vielleicht an dem switch? Ist es ein Bug von AVR Studio?

Dirk B. schrieb:
> Das Problem ist dann auch nicht ichar sondern charflash.

Das wäre dann hiermit wiederlegt, oder sehe ich das falsch?

von Dirk B. (dirkb2)


Lesenswert?

So leicht kannst du den Compiler auch nicht verwirren, der macht ein
char test=1;
daraus.

Geht das denn, wenn du ichar nicht als Zeiger definierst?


Du kannst dich mit den Funktionen durchaus an der Standardbibliothek 
orientieren: printf und putc

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Hat zwar nichts mit dem Problem zu tun, aber:
> case 0: ;
> case 1: ;
> case 2: ;
gibt es einen Grund für die (sinnfreien) Semikola? Die tun zwar nicht 
weh, aber extra Punkte bringen sie auch nicht.

M. M. schrieb:
> Ist es ein Bug von AVR Studio?
Wenn dann wäre es ein Bug im Compiler (und nicht in der IDE), also dem 
GCC, und das ist (wenn auch nicht ganz ausgeschlossen) eher 
unwahrscheinlich.
Such mal hier im Forum nach Bug und GCC. Fast jeder behauptet erstmal, 
das muss ein Bug im Compiler sein,... und praktisch jedes Mal stellt 
sich raus, das es doch kein Bug im GCC ist.

Mit welchen Compiler flags compilierst du denn? Kann es vielleicht sein, 
das der Compiler da was wegoptimiert?

von Dirk B. (dirkb2)


Lesenswert?

M. M. schrieb:
> Das wäre dann hiermit wiederlegt, oder sehe ich das falsch?

Da der Wert von test in der Funktion auf 1 festgelegt ist, kann der 
Compiler ganz anders Optimieren.

Er kann *charflash komplett durch 240 ersetzen.

von M. M. (blackcow)


Lesenswert?

Ok. Nochmal ganz einfach. Der gleiche Effekt jetzt mit direkter 
Übergabe.

Funktioniert:
1
void g_printc(uint8_t x, uint8_t y, uint8_t font, char ichar){
2
        ichar=1
3
  g_crect(x-1, y-1, x+3, y+5);
4
  for(uint8_t iy=0; iy<4; ++iy){
5
    if(*charflash&(1<<iy))
6
      g_point(x, y+iy);
7
  }
8
  for(uint8_t iy=0; iy<4; ++iy){
9
    if(*charflash&(1<<iy+4))
10
      g_point(x+1, y+iy);
11
  }
12
        return;
13
}

Funktioniert nicht:
1
void g_printc(uint8_t x, uint8_t y, uint8_t font, char ichar){
2
  g_crect(x-1, y-1, x+3, y+5);
3
  for(uint8_t iy=0; iy<4; ++iy){
4
    if(*charflash&(1<<iy))
5
      g_point(x, y+iy);
6
  }
7
  for(uint8_t iy=0; iy<4; ++iy){
8
    if(*charflash&(1<<iy+4))
9
      g_point(x+1, y+iy);
10
  }
11
        return;
12
}
13
void test(){
14
     char test=1;
15
     g_printc(1,1,0,test);
16
}

von Dirk B. (dirkb2)


Lesenswert?

Dirk B. schrieb:
> Er kann *charflash komplett durch 240 ersetzen.

Dirk B. schrieb:
> Das Problem ist dann auch nicht ichar sondern charflash.

von M. M. (blackcow)


Lesenswert?

Kaj G. schrieb:
> Hat zwar nichts mit dem Problem zu tun, aber:
>> case 0: ;
>> case 1: ;
>> case 2: ;
> gibt es einen Grund für die (sinnfreien) Semikola? Die tun zwar nicht
> weh, aber extra Punkte bringen sie auch nicht.

Das dachte ich auch zuerst, aber die sind tatsächlich notwendig. Der 
C-Standard sagt nach einem case: darf keine Variable initialisiert 
werden.

> M. M. schrieb:
>> Ist es ein Bug von AVR Studio?
> Wenn dann wäre es ein Bug im Compiler (und nicht in der IDE), also dem
> GCC, und das ist (wenn auch nicht ganz ausgeschlossen) eher
> unwahrscheinlich.
> Such mal hier im Forum nach Bug und GCC. Fast jeder behauptet erstmal,
> das muss ein Bug im Compiler sein,... und praktisch jedes Mal stellt
> sich raus, das es doch kein Bug im GCC ist.

Ja klar im Compiler. Ich weiß garnicht das AVR-Studio gcc nutzt. Ehrlich 
gesagt kann ich es mir auch nicht vorstellen, aber an was liegt es dann?

> Mit welchen Compiler flags compilierst du denn? Kann es vielleicht sein,
> das der Compiler da was wegoptimiert?

Mit den Standardeinstellungen.

von M. M. (blackcow)


Lesenswert?

Dirk B. schrieb:
> Dirk B. schrieb:
>> Er kann *charflash komplett durch 240 ersetzen.
>
> Dirk B. schrieb:
>> Das Problem ist dann auch nicht ichar sondern charflash.

Dann dürfte es doch trotzdem keinen Unterschied geben, die Optimierung 
ist ja in dem Fall in Ordnung.

von Dirk B. (dirkb2)


Lesenswert?

DAs habe ich noch vergessen zu zitieren:

Rufus Τ. F. schrieb:
> Könnte es sein, daß Deine Zugriffe auf Deine Zeichentabelle nicht
> funktionieren, weil Du PROGMEM und normale Speicherzugriffe mischt?

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

M. M. schrieb:
> Funktioniert:void g_printc(uint8_t x, uint8_t y, uint8_t font, char
...
> Funktioniert nicht:
Ist ganz schön was du da schreibst, aber in beiden Beispielen wird ichar 
nicht verwendet... aber vielleicht bin ich auch einfach nur blind.

von M. M. (blackcow)


Lesenswert?

Kaj G. schrieb:
> M. M. schrieb:
>> Funktioniert:void g_printc(uint8_t x, uint8_t y, uint8_t font, char
> ...
>> Funktioniert nicht:
> Ist ganz schön was du da schreibst, aber in beiden Beispielen wird ichar
> nicht verwendet... aber vielleicht bin ich auch einfach nur blind.

Ahh du hast recht! Also nochmal richtig:

Funktioniert:
1
void g_printc(uint8_t x, uint8_t y, uint8_t font, char ichar){
2
  ichar=1;
3
  uint8_t *charflash=font0+ichar;
4
  g_crect(x-1, y-1, x+3, y+5);
5
  for(uint8_t iy=0; iy<4; ++iy){
6
    if(*charflash&(1<<iy))
7
      g_point(x, y+iy);
8
  }
9
  for(uint8_t iy=0; iy<4; ++iy){
10
    if(*charflash&(1<<iy+4))
11
      g_point(x+1, y+iy);
12
  }
13
        return;
14
}
15
void test(){
16
     char test=1;
17
     g_printc(1,1,0,test);
18
}

Funktioniert nicht:
1
void g_printc(uint8_t x, uint8_t y, uint8_t font, char ichar){
2
  uint8_t *charflash=font0+ichar;
3
  g_crect(x-1, y-1, x+3, y+5);
4
  for(uint8_t iy=0; iy<4; ++iy){
5
    if(*charflash&(1<<iy))
6
      g_point(x, y+iy);
7
  }
8
  for(uint8_t iy=0; iy<4; ++iy){
9
    if(*charflash&(1<<iy+4))
10
      g_point(x+1, y+iy);
11
  }
12
        return;
13
}
14
void test(){
15
     char test=1;
16
     g_printc(1,1,0,test);
17
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

M. M. schrieb:
>> gibt es einen Grund für die (sinnfreien) Semikola? Die tun zwar nicht
>> weh, aber extra Punkte bringen sie auch nicht.
>
> Das dachte ich auch zuerst, aber die sind tatsächlich notwendig. Der
> C-Standard sagt nach einem case: darf keine Variable initialisiert
> werden.

Aua. Da hilft auch kein Semikolon. Wenn hier neue Variablen nötig sind, 
beginnt man einen neuen Block - d.h. einen von geschweiften Klammern 
umschlossenen Abschnitt.

Natürlich nur, wenn es tatsächlich nötig ist, hier neue Variablen zu 
deklarieren, sonst sind sie überflüssig.

von M. M. (blackcow)


Lesenswert?

Rufus Τ. F. schrieb:
> M. M. schrieb:
>>> gibt es einen Grund für die (sinnfreien) Semikola? Die tun zwar nicht
>>> weh, aber extra Punkte bringen sie auch nicht.
>>
>> Das dachte ich auch zuerst, aber die sind tatsächlich notwendig. Der
>> C-Standard sagt nach einem case: darf keine Variable initialisiert
>> werden.
>
> Aua. Da hilft auch kein Semikolon. Wenn hier neue Variablen nötig sind,
> beginnt man einen neuen Block - d.h. einen von geschweiften Klammern
> umschlossenen Abschnitt.
>
> Natürlich nur, wenn es tatsächlich nötig ist, hier neue Variablen zu
> deklarieren, sonst sind sie überflüssig.

http://stackoverflow.com/questions/92396/why-cant-variables-be-declared-in-a-switch-statement

von Karl H. (kbuchegg)


Lesenswert?

M. M. schrieb:

> Ahh du hast recht! Also nochmal richtig:
>
> Funktioniert:
>
1
> void g_printc(uint8_t x, uint8_t y, uint8_t font, char ichar){
2
>   ichar=1;
3
>   uint8_t *charflash=font0+ichar;
4
>   g_crect(x-1, y-1, x+3, y+5);
5
>   for(uint8_t iy=0; iy<4; ++iy){
6
>     if(*charflash&(1<<iy))
7
>

Nö. Das funktioniert ganz sicher nicht.
Nicht mit
1
  const uint8_t font0[10] PROGMEM = {153, 240, 219, 249, 231, 189, 223, 241, 255, 251};

In deinem Beispiel wenöglichst du dem Compiler alle Zwischenvariablen 
sowiet wegzuoptimieren, bis er beim eigentlichen Zugriff auf charflash 
noch immer die Information da hat, dass es sich da um einen Zugriff ins 
Flash handelt.

In deinem 'funktioniert nicht' Beispiel kann der Compiler diesen weg 
aber nicht gehen, weil ihm der Wert für ichar nicht bekannt ist.

Wenn du auf Dinge zugreifen willst, die due mittels PROGMEM ins Flash 
legst, dann müssen die Zugriffe anders sein. Du brauchst dazu die 
pgm_read Funktionen

https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Flash_mit_PROGMEM_und_pgm_read

Da liegt dein Problem.

von Karl H. (kbuchegg)


Lesenswert?

M. M. schrieb:

> 
http://stackoverflow.com/questions/92396/why-cant-variables-be-declared-in-a-switch-statement


Alles schön und gut. Der übliche Weg besteht trotzdem darin in einem 
case einen Block einzufügen, wenn man in einem case eine Variable 
benötigt

1
  switch( ... )
2
  {
3
4
    case 'A':
5
      {
6
        char j = 5;
7
        ....
8
      }
9
      break;
10
11
    case 'B':
12
      {
13
        char j = 8;
14
        ....
15
      }
16
      break;
17
18
  }

das vermeidet dann Probleme mit dem Scope, zeigt an (und garantiert), 
dass diese Variable nur in diesem case verwendet werden kann und der 
Block ist auch ein Hinweis an zukünftige Programmier-Generationen, dass 
hier in diesem case irgendetwas 'Aussergwöhnliches' passiert.

Das Thema lautet "defensives programmieren".

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.