Forum: Compiler & IDEs AVR-GCC Array(Pointer) einer Funktion übergeben


von Matthias L. (Gast)


Lesenswert?

Hallo Leute,

ich kapier es irgendwie nicht. Es geht mal wieder um das Thema Array als 
Pointer einer Funktion mitzugeben.

AVR GCC V4.19

folgendes habe ich vor:
1
volatile
2
struct
3
{
4
  uint8_t     au8RxMsg[128];
5
  uint8_t     u8RxLen;
6
  ...
7
}
8
scDiag;
9
10
//***************************************************************
11
void    rs232_recv       (  uint8_t   *i_pau8Data,
12
                            uint8_t   *i_u8Len   )
13
{
14
  ...
15
  *i_u8Len = ...
16
}
17
18
//***************************************************************
19
void main ( void )
20
{
21
  //...
22
  scDiag.= sizeof(scDiag.au8RxMsg);
23
  re232_recv( scDiag.u8RxLen, &scDiag.au8RxMsg);
24
  if ( scDiag.u8RxLen != 0 )
25
  { // scDiag.u8RxLen - Bytes empfangen
26
    // mach was
27
  }
28
  //...
29
}

Das tut was es soll. Aber es kommt
1
../plc_diag.c:128: warning: passing argument 1 of 'rs232_recv' from incompatible pointer type
2
../plc_diag.c:128: warning: passing argument 2 of 'rs232_recv' discards qualifiers from pointer target type

Laut dieser Beschreibungen hier
Beitrag "Re: [C] Char Array an Funktion übergeben"
1
static char calculate_checksum(char data[]) 
2
{
3
  ...
4
}

Nun habe ich gegoogelt und gelesen und ua das gefunden:
1
void    rs232_recv       (  uint8_t    i_pau8Data[],
2
                            uint8_t   *i_u8Len   )

Das soll aber wohl dasselbe sein. Aber es bleibt bei der Warnung.

wie bekomme ich die Warnungen weg? Was im Detail mache ich falsch?

PS: Ja, ich kann auch die Länge direkt mitgeben und die (neue) Länge als 
Return-wert zurückgeben. Aber so muss es doch auch gehen...

von Peter II (Gast)


Lesenswert?

Matthias Lipinsky schrieb:
> wie bekomme ich die Warnungen weg?

volatile uint8_t* != uint8_t

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:

> void    rs232_recv       (  uint8_t   *i_pau8Data,
>                             uint8_t   *i_u8Len   )


>   re232_recv( scDiag.u8RxLen, &scDiag.au8RxMsg);


Entweder deine Bezeichnungen sind missverständlich, oder das ist genau 
verkehrt rum.

i_pau8Data ist doch wohl der Pointer zu den Daten. Und die sind ja wohl 
im Array au8RxMsg und nicht in u8RxLen. Die Funktion will zuerst den 
Pointer zu den Daten und als zweites den Pointer zum Längenbyte.
1
  re232_recv( scDiag.au8RxMsg, &scDiag.u8RxLen );
wäre für mich daher logischer.
Und dann passt es auch mit dem Adressoperator.

von Matthias L. (Gast)


Lesenswert?

>Entweder deine Bezeichnungen sind missverständlich, oder das ist genau
>verkehrt rum.

Richtig. Das ist hier aus Versehen vertauscht.
1
  rs232_recv( scDiag.au8RxMsg, &scDiag.u8RxLen );

... liefert aber dieselben beiden Warnungen.. Warum?

Hat das was mit dem Volatile der Struktur zutun?

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:
>>Entweder deine Bezeichnungen sind missverständlich, oder das ist genau
>>verkehrt rum.
>
> Richtig. Das ist hier aus Versehen vertauscht.
>
>
1
>   rs232_recv( scDiag.au8RxMsg, &scDiag.u8RxLen );
2
>
>
> ... liefert aber dieselben beiden Warnungen.. Warum?

Dieselben?
Also die hier
1
../plc_diag.c:128: warning: passing argument 1 of 'rs232_recv' from incompatible pointer type
2
../plc_diag.c:128: warning: passing argument 2 of 'rs232_recv' discards qualifiers from pointer target type

zumindest die erste müsste jetzt anders lauten. Das müsste ebenfalls 
eine discards qualifier warnung sein.

> Hat das was mit dem Volatile der Struktur zutun?

ja, hat es.
Ein

  volatile uint8_t *

ist nun mal was anderes als ein

  uint8_t *

im ersten ist noch immer die Information drinnen, dass das Ziel volatile 
ist. Im zweiten nicht.

d.h. wenn die Pointer an die Funktion übergeben werden, wird innerhalb 
der Funktion dieses volatile ignoriert. Schlecht ausgedrückt. Es geht 
beim Aufruf der Funktion 'verloren'. Innerhalb der Funktion darf der 
Compiler wieder hemmungslos optimieren. Das wolltest du aber unter 
Umständen nicht. Sonst hättest du das Ding ja erst gar nicht volatile 
gemacht.

von Matthias L. (Gast)


Lesenswert?

>Dieselben?
Hab ich nicht geprüft. War nur ein CTRL+C/V Fehler hier in den Post.

>ja, hat es.
>Ein
>  volatile uint8_t *
>ist nun mal was anderes als ein
>  uint8_t *
>im ersten ist noch immer die Information drinnen, dass das Ziel volatile
>ist. Im zweiten nicht.


Hm ok. Aber volatile soll doch verwendet werden, bei Variablen, die an 
verschiedenen Stellen (Ints) genutzt werden.

Ist der Gedanke, diese Variablen alle in einer Strkutur 
zusammenzupacken, und diese volatile zu kennzeichnen, falsch?

Nehm ich das volatile weg, sind die Warnungen weg.

von Peter II (Gast)


Lesenswert?

Matthias Lipinsky schrieb:
> Nehm ich das volatile weg, sind die Warnungen weg.

oder schreib das volatile in der Funktion hin

> Hm ok. Aber volatile soll doch verwendet werden, bei Variablen, die an
> verschiedenen Stellen (Ints) genutzt werden.
kommt darauf an ob im Hauptprogramm und in den INTs darauf zugegriffen 
wird oder nur ein verschieden INTs

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:

> Hm ok. Aber volatile soll doch verwendet werden, bei Variablen, die an
> verschiedenen Stellen (Ints) genutzt werden.

Na ja.
volatile soll in erster Linie benutzt werden um dem Compiler klar zu 
machen, dass er seine Optimize-Finger von den Variablen lassen soll.

Bei Wert-Übergaben aus einer ISR heraus durch globale Variablen ist das 
ein Muss. Aber innerhalb einer ISR wäre das zb harmlos, wenn die 
Variable nicht volatile wäre.

> Ist der Gedanke, diese Variablen alle in einer Strkutur
> zusammenzupacken, und diese volatile zu kennzeichnen, falsch?

Überhaupt nicht.

Wenn es keinen grossen Unterschied in der recv Funktion macht, dann 
kannst du ja ein
1
void    rs232_recv       (  volatile uint8_t   *i_pau8Data,
2
                            volatile uint8_t   *i_u8Len   )
3
{
4
...
draus machen.
Wenn dir das das Optimierungspattern in der Funktion über den Jordan 
haut, UND es ok ist, dass innerhalb der Funktion der Compiler 
hemmungslos optimieren darf, dann kannst du auch beim Aufruf das 
volatile wegcasten
1
void main()
2
{
3
  re232_recv( (uint8_t*)scDiag.au8RxMsg, (uint8_t*)&scDiag.u8RxLen );

oder den Cast durch den Einsatz einer Inline Hilfsfunktion verstecken
1
void    rs232_recv      ( volatile uint8_t   *i_pau8Data,
2
                          volatile uint8_t   *i_u8Len   )
3
{
4
  rs232_recv_non_vol( (uint8_t*)i_pau8Data, (uint8_t*)i_u8Len );
5
}
6
7
void    rs232_recv_non_vol( uint8_t   *i_pau8Data,
8
                            uint8_t   *i_u8Len   )
9
{
10
...

Aber eines steht auf jeden Fall fest. So wie im Original, fällt beim 
Aufruf ein volatile unter den Tisch und der Compiler weist dich darauf 
hin. Es liegt jetzt an dir zu entscheiden, ob das ok ist (und du daher 
den Compiler mit einem "Ruhe-Jetzt"-Cast ruhigstellst) oder ob du in der 
Funktion dieses volatile respektieren musst.
Oder aber vielleicht muss ja gar nicht die komplette Struktur volatile 
sein? Wer weiss? Gibt es denn Potential, dass der COmpiler in diesen 
beiden Members mit der Optimierung daneben hauen kann?

von Matthias L. (Gast)


Lesenswert?

>Oder aber vielleicht muss ja gar nicht die komplette Struktur volatile
>sein? Wer weiss? Gibt es denn Potential, dass der COmpiler in diesen
>beiden Members mit der Optimierung daneben hauen kann?

Hm..

Der Kontext ist etwa so:
Eine Funktion (hier im Beispiel i2c_write) bekommt Daten/Infos was ist 
zutun mit den EIngangsvariablen. Das wird lib-intern in einer Struktur 
abgelegt und per ISR(s) etc in der Lib im Hintergrund bearbeitet. Wenn 
das fertig ist, dann wird das über ein Zeiger auf eine "Fertig" Variable 
gemeldet.

SO hier etwa:
1
...
2
case cDIAG_0x11_I2cWrite:
3
{
4
  //-- ENTRY --
5
  if ( scDiag.u8DiagLast != scDiag.u8Diag )
6
  {
7
    scDiag.u8DiagLast = scDiag.u8Diag;
8
9
    scDiag.u8Addr    = scDiag.au8RxMsg[ 2];
10
    scDiag.u8WResult = scDiag.au8RxMsg[ 3];  // write length
11
    i2c_read ( /*  *i_pu8Return  */ &scDiag.u8Return,
12
               /*   i_u8RAddr    */  ...,
13
               /*  *i_pu8RLen    */  ...,
14
               /*  *i_pau8RData  */ &... ( wo sollen die Daten hin )  );
15
  }
16
17
  //-- ACTION --
18
  //-- TRANSITION --
19
  if ( scDiag.u8Return != cI2C_BUSY )
20
  {
21
    // i2c fertig gelesen, mach irgendwie weiter
22
  }  
23
  break;
24
}
Ich weiß eben nicht genau, ob der Compiler sowas per Optimierung 
zerwürgt...

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:

> Ich weiß eben nicht genau, ob der Compiler sowas per Optimierung
> zerwürgt...

Unter uns Klosterschwestern.
In einem Array hat der Compiler normalerweise sowieso schlechte Karten, 
sich einzelne Array Elemente in einem Register vorzuhalten. Soviele CPU 
interne Register hat der nicht.

Die neuralgischen Punkte sind sowas
1
uint8_t i;
2
3
void main()
4
{
5
  ...
6
7
  while( 1 )
8
  {
9
    if( i > 5 )
10
    {
11
      PORTD |= 8;
12
    }
13
14
    // X ....
15
  }

d.h. kleine Hauptschleife, wenig Variablen
Sobald an der Position X da komplexerer Code reinkommt und der COmpiler 
vor lauter Variablen in Register laden und umladen nichts mehr frei hat, 
dann geht da sowieso nichts mehr mit Schleifenübergreifender 
Optimierung. Um
1
void main()
2
{
3
  ...
4
  for( i = 0; i < 40; i++ )
5
    mach was mit msg[i];
6
  ...

soweit zu optimieren, dass ein fehlendes volatile (obwohl es eigentlich 
korrekt wäre!) sich auswirkt, da hat der COmpiler auf einem AVR 0 
Chancen. Soviele CPU Register gibt es gar nicht, dass er das machen 
könnte und für jedes einzelne Array-Element ein Register reserviert.

Aber:
Ich denke mal nicht, dass deine recv Funktion stark beeinträchtigt wird, 
wenn du das volatile ergänzt. Daher würde ich es reintun.

von Matthias L. (Gast)


Lesenswert?

>Ich denke mal nicht, dass deine recv Funktion stark beeinträchtigt wird,
>wenn du das volatile ergänzt. Daher würde ich es reintun.

Ich hab es jetzt komplett weggelassen und geht noch.
Wenn ich länger drüber nachdenke, glaube ich, kanns fast überall 
weglassen. Wenn ich mit einer (zB) i2c_write Funktion einen Zeiger in 
die interne Struktur schaffe um damit zu arbeiten, und aussen warte bis 
diese fertig ist, dann ändert sich der Zeiger währenddem ja nicht. Und 
somit sollte das volatile unnötig sein. Ich werde es wohl nur bei den 
internen Statusvariablen ergänzen..

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:

> die interne Struktur schaffe um damit zu arbeiten, und aussen warte bis
> diese fertig ist

normalerweise sind diese Flag Variablen der Knackpunkt.
Die sollten volatile sein. Das ist wie eine Semaphore, wo 1 Semaphore 
benutzt wird um eine ganze Datenstruktur in der Multitasking 
Programmierung abzusichern.

von Matthias L. (Gast)


Lesenswert?

>normalerweise sind diese Flag Variablen der Knackpunkt

Somit würde es ja reichen, wenn ich hier:
Beitrag "Re: AVR-GCC Array(Pointer) einer Funktion übergeben"

diese Variable:
1
    i2c_read ( /*  *i_pu8Return  */ &scDiag.u8Return,
2
...
3
  if ( scDiag.u8Return != cI2C_BUSY )

in der Abfrage als volatile kennzeichne, damit sie wirklich immer von 
der SPeicherzelle gelesen wird...

von Stefan E. (sternst)


Lesenswert?

Matthias Lipinsky schrieb:
> diese Variable:
1
    i2c_read ( /*  *i_pu8Return  */ &scDiag.u8Return,
2
...
3
  if ( scDiag.u8Return != cI2C_BUSY )
>
> in der Abfrage als volatile kennzeichne, damit sie wirklich immer von
> der SPeicherzelle gelesen wird...

Matthias Lipinsky schrieb:
> Hm ok. Aber volatile soll doch verwendet werden, bei Variablen, die an
> verschiedenen Stellen (Ints) genutzt werden.

Ich habe den Eindruck, dass du ein grundsätzliches Missverständnis 
bezüglich des "die an verschiedenen Stellen (Ints) genutzt werden" hast. 
Wenn du in einer aufgerufenen Funktion eine Variable änderst und dann in 
der übergeordneten Funktion diese testest, dann gibt es nicht den 
geringsten Grund für ein volatile.

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.