Forum: Mikrocontroller und Digitale Elektronik Declaration von stucts


von Andreas (Gast)


Lesenswert?

Hallo Leute ich habe mir gerade einen Treiber für den CAN Controller 
MCP2515 geschrieben.

Ich habe nur noch ein kleines Problem bevor ich endlich das Testen 
anfangen kann.

und zwar brauche ich eine stuct um die verschiedenen Parameter für die 
CAN Botschaft einzustellen.

Es gibt ein Header File mit den extern Declarationen für die Funktionen 
und ein C File mit den Funktionen.

Frage :

wo und wie muss ich dieses Struct definieren sodass ich es in meiner 
Main und in der C Treiberdatei benutzen  kann ?

Das Headerfile ist natürlich in der Main eingebunden.

Die definition sieht momentan so aus :


typedef struct
{
    unsigned int  id;
    unsigned char   rtr;
    unsigned char   length;
    unsigned char   data[8];
} CANMessage;

muss ich die im c file machen und im header als extern declarieren, wenn 
ja wie sieht das aus ? oder muss ich die im Header definieren , wenn ja 
wie sieht das aus ?

vielen Dank im Voraus











von Karl H. (kbuchegg)


Lesenswert?

Andreas wrote:
>
> wo und wie muss ich dieses Struct definieren sodass ich es in meiner
> Main und in der C Treiberdatei benutzen  kann ?
>

Mal sehen ob du das selber durch etwas Nachdenken rauskriegst.

Wie arbeitet ein C Compiler?

Ein wesentliches Prinzip eines C Compilers ist es, daß
er einzelne *.c Files für sich getrennt übersetzt. D.h.
wenn du 2 Dateien hast, A.c und B.c, dann übersetzt der
Compiler A.c. Wenn er sich B.c vornimmt, dann hat er alles
was er durch die Compilierung von A.c gelernt hat (Datentypen,
Funktionen, etc.) vergessen. Er fängt praktisch wieder bei
0 an. Das ist auch gut so, denn dieses Verhalten erlaubt
es, dass bei 200 *.c Files, immer nur die neu compiliert
werden, die sich auch tatsächlich verändert haben. Alle
anderen Objectfiles (= das Ergebnis der Compilierung)
bleiben so wie sie sind. Erst durch den Link-Schritt
werden diese Einzelteile zum Gesamtprogramm zusammengebunden.

Definierst du also einen eigenen Datentyp, zb eine struct
dann muss diese struct natürlich in beiden *.c, also in
A.c und in B.c vorkommen. Allgemeiner gesagt: Eine eigene
Datenstruktur muss in allen Compiliation-Units (im weitesten
Sinne also: in allen *.c Files) in der sie verwendet wird
auch deklariert werden.

A.c
===
1
 struct MyStruct {
2
   int n;
3
 };
4
5
void foo( struct MyStruct* pM )
6
{
7
  pM->n = 5;
8
}
B.c
===
1
  struct MyStruct {
2
    int n;
3
  };
4
5
int main()
6
{
7
  struct MyStruct tmp;
8
9
  foo( &tmp );
10
}
Sowohl in A.c als auch in B.c muss in diesem Fall natürlich
die struct MyStruct definiert werden, denn in beiden
Fällen muss der Compiler wissen
* dass es eine struct MyStruct überhaupt gibt. Du könntest
  dich ja auch vertippt haben und anstatt struct MyStruct
  eine struct Mystruct angeben, die natürlich im Gesamtsystem
  nicht existiert.
  Es gilt die Regel: Wenn der Compiler sich eine Compilation
  Unit vornimmt, dann kennt er gerade mal die C-Schlüsselwörter.
  Alles andere muss dem Compiler mitgeteilt werden, bevor es
  das erste Mal verwendet werden kann.
* wie diese struct aufgebaut ist.
  In A.c muss er das wissen, weil er selbstverständlich wissen
  muss, dass diese struct ein Member namens n hat und (bei
  mehreren Member) an welcher Stelle in der struct dieses
  Member steht. Und in B.c muss er es wissen, damit die korrekte
  Anzahl an Bytes für tmp reserviert werden können.

Also: So wie das da oben geschrieben ist, ist das 100% ok.

Aber!
Aber es ist unbefriedigend.
Warum?
Weil es fehleranfällig ist.

Logischerweise musst du als Programmierer darauf achten, dass
alle struct Definitionen auch übereinstimmen. Ich sag
mal so. Wenn in A.c die struct so definiert ist:
1
struct MyStruct {
2
  int First;
3
  int n;
4
  int Third;
5
};
und in B.c aber so
1
struct MyStruct {
2
  int n;
3
  int Third;
4
  int First;
5
  int Nocheiner;
6
};
dann wird da Kuddelmuddel rauskommen.

Was kannst du dagegen tun?

Du kannst die struct Definition aus den beiden *.c Files
herausoperieren und in eine eigene Datei geben. Dadurch
erreichst du, dass die Definition nur an einer einzigen
Stelle existiert. Diese Definition wird dann mit einem
#include sowohl in A.c als auch in B.c hineingezogen.
Dadurch ist aber automatisch gewährleistet, dass sowohl
A.c als auch B.c jeweils identische structs zu Gesicht
bekommen. Traditionell nennt man solche Files dann
Header Files und gibt ihnen die Endung *.h. Dem Compiler
ist es aber grundsätzlich völlig egal wie du so ein File
nennst und welche Endung du ihm gibst. Für ihn (genauer
für den Preprozessor) gilt nur eines: Die Zeile in der
#include "Filename"
steht, wird durch den Inhalt der Datei Filename ersetzt,
bevor das ganze Machwerk zum eigentlichen Compiler geht.

Dadurch erhälts du als Gesamtsystem:

MyStruct.h
==========
1
struct MyStruct {
2
  int First;
3
  int n;
4
  int Second;
5
};
A.c
===
1
#include "MyStruct.h"
2
3
void foo( struct MyStruct* pM )
4
{
5
  pM->n = 5;
6
}
B.c
===
1
#include "MyStruct.h"
2
3
int main()
4
{
5
  struct MyStruct tmp;
6
7
  foo( &tmp );
8
}
Wenn der Compiler A.c übersetzt, dann sieht er, über den
Umweg des include, eine Definiton für struct MyStruct.
Compiliert er B.c, dann sieht er ebenfalls als erstes
eine struct MyStruct. Und das beste daran: Es ist garantiert,
dass es sich in beiden Fällen um dieselbe struct MyStruct
handelt, denn sie wird ja immer vom gleichen File, dem
Header File MyStruct.h hereingezogen.

Ich hoffe das klärt das Ganze etwas auf.
Es ist wirklich sehr einfach, wenn man erst mal begriffen
hat:
 jedes .c File wird für sich selbst, ohne ansehen der
  anderen *.c Files übersetzt
 Wenn der Compiler sich ein .c vornimmt, dann weiss er ausser
  den grundlegenden C-Dingen (Schlüsselwörter, Datentypen)
  nichts.
* Jeder Datentyp, der kein eingebauter Datentyp ist, muss
  zumindest deklarariert werden, bevor er verwendet werden kann
  und sei die Verwendung nur ein Pointer Argument in einer
  Argumentliste. Wird auf Internals einer struct zugegriffen,
  dann reicht eine Deklaration nicht aus, dann muss es schon
  eine vollständige Definition sein, aus der der Compiler den
  exakten Aufbau der struct ersehen kann.

von crazy horse (Gast)


Lesenswert?

hat ja keiner was gegen kopieren - aber tu nicht so, als wärs dein 
eigener Mist :-)

typedef struct
{
    unsigned int  id;
    unsigned char rtr;
    unsigned char length;
    unsigned char data[8];
} CANMessage;

CANMessage message1;
CANMessage message2;


Jetzt kannst du der Variable message1(2) Daten zuweisen oder von dort 
lesen.
message1.id=0x540;   //zum Beispiel


Kreatives Chaos ist aber echt eine super Seite, die mir viel Zeit 
erspart hat. Das schützt aber nicht davor, verstehen zu müssen, was man 
kopiert.

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.