Forum: Compiler & IDEs Problem mit struct und void pointer


von Niklaus S. (niklaus_s)


Lesenswert?

Hallo,

Ich steh da irgendwie auf dem Schlauch.

Im folgenden Code, habe da mal die relevanten Teile zusammengeschnitten, 
habe ich das Problem dass die 2 letzten Anweisungen einfach nicht 
klappen wollen, irgendwie funktioniert es nicht das struct das ich im 
void * data hinterlegt habe anzusprechen.

Wäre schön wenn mir da jemand auf die Sprünge helfen könnte.

Nik

devices.h
1
typedef struct Sensors_t
2
{
3
  uint8_t id;
4
  uint16_t lastUpdate;
5
  void * data;
6
}Sensors;
7
8
typedef struct Leistung_t
9
{
10
  int vcc;
11
  long int ID;
12
  int Watt;
13
  long int kWh;
14
  long int kWhd;
15
} Leistung ; 
16
17
typedef struct Klima_t
18
{
19
  int vcc;
20
  long int ID;
21
  int Grad;
22
  int Rel;
23
} Klima ;
main.c
1
...
2
static uint8_t leistung_pt = 0; // Zeiger auf das letzte element im array ‘leistung’
3
4
Sensoren sensors[20]; // Array von Allen Sensoren
5
Leistung leistung[4];     // Array von Leisungssensoren
6
Klima   klima[6];           // Array von Klimasensoren
7
8
int8_s = get_sensor_index((uint8_t) id, sensors); // Hole den index auf sensors[?] anhand der id
9
 
10
sensors[s].data = &leistung[leistung_pt]; // Weise dem void * data die Addr von leistung[x] zu
11
12
Leistung * data = (Leistung*) sensors[s].data; // Initialisiere den ‘Arbeistpointer’ 
13
...
14
data->Watt=watt; //
15
printf("%u\r\n", data->Watt);

: Verschoben durch User
von Max (Gast)


Lesenswert?

void-pointer musst du casten! (Leistung*)

von Max (Gast)


Lesenswert?

sensors[s].data = (Leistung*)&leistung[leistung_pt];

von Karl H. (kbuchegg)


Lesenswert?

Verrätst du uns auch noch, was man sich unter
'funktioniert irgendwie nicht'
vorstellen soll.

Denn eines muss dir klar sein: Nur weil du den Compiler zwingst, die 
Schnauze zu halten, indem du den Pointer niedercastest, wird nicht 
magisch aus einer Klimastruktur, deren Adresse du in data abgelegt hast, 
eine Leistung Datenstruktur.

Ich würde jedem empfehlen um void Pointer einen Bogen zu machen. 
Meistens ist das keine gute Lösung.

In deiner Sensors Datestruktur brauchst du sowieso noch einen Member, 
der dir sagt, ob du es jetzt mit einem Leistungssensor oder einem 
Klimasensor zu tun hast. So etwas wie ein Typflag
1
typedef struct Leistung_t
2
{
3
  int vcc;
4
  long int ID;
5
  int Watt;
6
  long int kWh;
7
  long int kWhd;
8
} Leistung ; 
9
10
typedef struct Klima_t
11
{
12
  int vcc;
13
  long int ID;
14
  int Grad;
15
  int Rel;
16
} Klima ; 
17
18
typedef struct Sensors_t
19
{
20
  uint8_t  id;
21
  uint16_t lastUpdate;
22
23
  uint8_t  sensorTyp;
24
  union
25
  {
26
    Leistung* pLeist;
27
    Klima*    pKlima;
28
  } data;
29
}Sensors;

die beiden Pointer liegen durch die union übereinander. Entweder ist der 
Pointer data.pLeist gültig, oder der Pointer data.pKlima ist gültig. Der 
Member sensorTyp (den du natürlich beim Aufbau der Daten korrekt 
besetzen musst), verrät dir dann, welcher der richtige ist, so dass du 
zb alle Sensoren im Array in einer einzigen Schleife abhandeln kannst.
1
#define SENSOR_UNKNOWN  0
2
#define SENSOR_KLIMA    1
3
#define SENSOR_LEISTUNG 2
4
5
...
6
  nrSensors = 0;
7
8
...
9
10
  sensors[nrSensors].senssorTyp = SENSOR_KLIMA;
11
  sensors[nrSensors].data.pKlima = klima[0];
12
  nrSensors++;
13
14
  sensors[nrSensors].sensorTyp = SENSOR_LEISTUNG;
15
  sensors[nrSensors].data.pLeistung = leistung[0];
16
  nrSensors++;
17
18
19
   ...
20
  for( i = 0; i < nrSensors; i++ )
21
  {
22
    if( sensors[i].sensorTyp == SENSOR_KLIMA )
23
    {
24
       //ist ein Klima Sensor
25
      mach_was_mit( sensors[i].data.pKlima );
26
    }
27
    else if( sensors[i].sensorTyp == SENSOR_LEISTUNG )
28
    {
29
       // ist ein Leistungssensor
30
       mach_was_anderes_mit( sensors[i].data.pLeistung );
31
    }
32
    else
33
    {
34
      // unbekannter Sensortyp, mit dem kann man nichts anfagen
35
      // Weißt auf einen programmfehler hin, wenn das Programm jemals
36
      // hier her kommt.
37
    }
38
  }
39
40
...
41
42
void mach_was_mit( Klima* pKlima )
43
{
44
  ...
45
}
46
47
void mac_was_anderes_mit( Leistung* pLeistung )
48
{
49
  ...
50
}

: Bearbeitet durch User
von Niklaus S. (niklaus_s)


Lesenswert?

Hallo miteinander,

Herzlichen Dank für die schnellen Antworten.

@Max
Leider bin ich mit dir nicht ganz einverstanden.
du sagst:
1
 sensors[s].data = (Leistung*)&leistung[leistung_pt];
Da leistung[leistung_pt] bereits ein Pointer auf Leistung ist, ist das 
doppelt gemoppelt, richtig wäre dann eher:
1
sensors[s].data = (void *)&leistung[leistung_pt];
da ich ja den Pointer in den .data reinschreiben will und der ein (void 
*) ist.
Das casting muss ich aber eigentlich nur beim Zugreifen manchen, und 
nicht unbedingt beim Pointer zuweisen.

@Heinz
Das mit der 'union' ist zwar das gleiche was ich gemacht habe, aber von 
der Typenprüfung etwas sicherer. Werde das mal ausprobieren.

Das Problem äussert sich darin, dass der Atmega328 neu startet wenn ich 
die zwei letzten Zeilen einfüge. Entweder schreibe ich ausversehen in 
ein Register oder springe sonst an eine 'falsche' Addresse. - oder - ich 
habe ein Problem mit dem Stack, da ich einige Interrupts in Betrieb 
habe, und da vielleicht was überschrieben wird.

Gruss
 Nik

von Mark B. (markbrandis)


Lesenswert?

Karl Heinz schrieb:
> Ich würde jedem empfehlen um void Pointer einen Bogen zu machen.
> Meistens ist das keine gute Lösung.

Hm, was wenn man ein Bridge Pattern in C implementieren will? Oder wenn 
man flexibel auf Daten zugreifen will, die von verschiedenem Typ sein 
können, und erst zur Laufzeit feststeht (z.B. durch Benutzereingabe) was 
man denn haben will?

Oder wenn man eine Lib schreibt und der Benutzer sich nicht auf Details 
der  Implementierung verlassen soll, weswegen ihm diese nicht bekannt 
sein sollen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Wer weiß was sowas ist, kann dann auch schon mit void Pointern umgehen. 
Der fragt aber dann auch nicht hier im Forum nach.

Das ein Thomas Bubendorfer die Eiger Nordwand ohne Seil durchklettern 
kann ist kein Argument dafür, dass jeder Hamburger Flachlandtiroler mit 
nichts anderem als Sandalen in die Berge gehen kann.

Hier geht es um den Aufbau einer einfachen Datenstruktur, die man gut 
ohne void Pointer implementieren kann. So wie 95% aller anderen 
Datenstrukturen oder Pattern.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Okay. Wer Interesse hat, hier noch ein schönes Beispiel:

http://www.qnx.com/developers/articles/article_302_2.html

von Karl H. (kbuchegg)


Lesenswert?

Niklaus S. schrieb:

> Das Problem äussert sich darin, dass der Atmega328 neu startet wenn ich
> die zwei letzten Zeilen einfüge. Entweder schreibe ich ausversehen in
> ein Register oder springe sonst an eine 'falsche' Addresse. - oder - ich
> habe ein Problem mit dem Stack, da ich einige Interrupts in Betrieb
> habe, und da vielleicht was überschrieben wird.

Schön.
Dann  brauchen wir das konmplette Programm und nicht nur ein paar 
Ausschnitte.
Denn das Problem sitzt dann nicht dort, wo du es vermutest. Du siehst 
nur die Symptome. Die sind aber nicht das eigentliche Problem.

von Oliver (Gast)


Lesenswert?

void * mögen zwar ihre Tücken haben, aber bis darauf, daß das oben 
gezeigte Programm unvollständig ist und Tippfehler enthält, und daher 
gar nicht kompilieren würde, ist das, was da mit den Pointern gemacht 
wird, prinzipiell richtig.

Wenn der Prozessor abstürzt, liegt es wohl daran, daß das 
Originalprogramm ganz anders aussieht, oder eben an anderen Teilen des 
Programms.

Oliver

von Niklaus S. (niklaus_s)


Lesenswert?

Hallo Karl Heinz,

Das ist mir jetzt auch klar. Ich wollte nur noch eine andere Meinung 
dazu hören ob ich da was übersehen habe bei der Implementation der 
Datenstruktur, weil ja nach dem Einfügen der letzten Zeilen der uC 
restartete.

Ich kann auch gerne das ganze Program senden, aber da es halt auf einen 
Atemea328 mit RFM12 zugeschnitten ist wird das nicht viel bringen. Ich 
werde wohl selbst nochmals weitersuchen müssen.

Gruss
 Nik

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.