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.