Da du ATMega erwähnt hast, gehe ich davon aus, dass die ein Atmel-Studio
installiert hast.
Hol dir mal aus dem Anhang das beiliegende Projekt und lade es ins
Atmel-Studio
Was geht hier vor.
Zunächst mal gibt es da ein main.c
1 | #include <avr/io.h>
|
2 |
|
3 | #include "Header1.h"
|
4 | #include "Header2.h"
|
5 |
|
6 | int main(void)
|
7 | {
|
8 | struct test t1;
|
9 | struct check t2;
|
10 |
|
11 | while(1)
|
12 | {
|
13 | }
|
14 | }
|
Da in main.c 2 Strukturen benutzt werden, muss es dafür Definitionen
geben. Die Defintion für die struct test steckt in header1.h. Hier wird
also erklärt, was man sich unter einem derartigem Strukturobjekt
vorzustellen hat
1 | struct test {
|
2 | int a;
|
3 | };
|
Da diese Definition in Header1.h drinnen steht, gibt es
konsequenterweise einen entsprechenden #include, damit diese Header
Datei beim Compilieren von main.c mit hereingezogen wird und somit dem
Compiler beim Anlegen der Variable t1 bekannt ist, wieviel Speicherplatz
er dafür reservieren muss. So ein #include macht ja im Grunde nichts
anderes, als die Zeile mit dem #include, durch den Inhalt der
angegebenen Datei zu ersetzen. Aus Sicht dieses #include ist das nichts
anderes als ob der Compiler anstatt dem originalen main.c eine temporäre
Datei mit diesem Inhalt ...
1 | #include <avr/io.h>
|
2 |
|
3 | // #include "Header1.h"
|
4 | struct test {
|
5 | int a;
|
6 | };
|
7 |
|
8 | #include "Header2.h"
|
9 |
|
10 | int main(void)
|
11 | {
|
12 | struct test t1;
|
13 | struct check t2;
|
14 |
|
15 | while(1)
|
16 | {
|
17 | }
|
18 | }
|
... übersetzt hätte. Genau das hat der Präprozessor aus der Zeile mit
dem #include gemacht.
Was ist mit struct check?
Auch dafür gibt es eine Header Datei, in der erklärt wird, was man sich
unter diesem struct vorzustellen hat
1 | #include "Header1.h"
|
2 |
|
3 | struct check {
|
4 | struct test a;
|
5 | int b;
|
6 | };
|
Diesmal besteht so ein Strukurobjekt aus 2 Membern, a und b. Wobei der
Member a ein struct test Objekt sein soll. Getreu dem Motto "Jede Datei
soll in sich vollständig sein", includiert diese Header Datei auch die
Header1.h, in der ja beschrieben ist, wie ein struct test Objekt
auszusehen hat. Soweit so gut.
Aber was bedeutet das für main.c?
Nun ganz einfach. lass uns wieder den Inhalt von Header2.h anstelle der
Zeile mit dem #include einsetzen. Damt ergibt sich
1 | #include <avr/io.h>
|
2 |
|
3 | // #include "Header1.h"
|
4 | struct test {
|
5 | int a;
|
6 | };
|
7 |
|
8 | // #include "Header2.h"
|
9 | #include "Header1.h"
|
10 |
|
11 | struct check {
|
12 | struct test a;
|
13 | int b;
|
14 | };
|
15 |
|
16 | int main(void)
|
17 | {
|
18 | struct test t1;
|
19 | struct check t2;
|
20 |
|
21 | while(1)
|
22 | {
|
23 | }
|
24 | }
|
Durch die Ersetzung ist eine weitere Zeile mit einem #include
entstanden, die natürlich ebenfalls durch die Ersetzung aufgelöst wird
....
1 | #include <avr/io.h>
|
2 |
|
3 | // #include "Header1.h"
|
4 | struct test {
|
5 | int a;
|
6 | };
|
7 |
|
8 | // #include "Header2.h"
|
9 | // #include "Header1.h"
|
10 | struct test {
|
11 | int a;
|
12 | };
|
13 |
|
14 | struct check {
|
15 | struct test a;
|
16 | int b;
|
17 | };
|
18 |
|
19 | int main(void)
|
20 | {
|
21 | struct test t1;
|
22 | struct check t2;
|
23 |
|
24 | while(1)
|
25 | {
|
26 | }
|
27 | }
|
Tja. Jetzt sieht man aber schon das Dilemma. Über den Umweg Header2.h
ist die Datei Header1.h 2 mal im Endergebnis repräsentiert. Als Folge
davon gibt es 2 Definitionen darüber, wie ein struct Test Objekt
auszusehen hat. Die beiden Definitionen stimmen zwar überein, trotzdem
ist das in C verboten. Du darfst von einer Struktur immer nur eine
Definition haben!
Und genau an dieser Stelle setzten die Include Guards an. Ergnänzt man
den Code von Header1.h mit einem Include Guard, so wie hier
1 | #ifndef HEADER1_INCLUDED
|
2 | #define HEADER1_INCLUDED
|
3 |
|
4 | struct test {
|
5 | int a;
|
6 | };
|
7 |
|
8 | #endif
|
dann sieht die Situation anders aus. Wenn sich der Präprozessor das
original main.c vornimmt, dann stösst er wie immer auf den #include. Die
Eresetzung der Zeile mit dem Inhalt ergibt ...
1 | #include <avr/io.h>
|
2 |
|
3 | // #include "Header1.h"
|
4 | #ifndef HEADER1_INCLUDED
|
5 | #define HEADER1_INCLUDED
|
6 |
|
7 | struct test {
|
8 | int a;
|
9 | };
|
10 |
|
11 | #endif
|
12 |
|
13 | #include "Header2.h"
|
14 |
|
15 | int main(void)
|
16 | {
|
17 | struct test t1;
|
18 | struct check t2;
|
19 |
|
20 | while(1)
|
21 | {
|
22 | }
|
23 | }
|
Bearbeitet der Präprpzessor den Text weiter, dann stösst er auf die
Fragestellung
1 | #ifndef HEADER1_INCLUDED
|
Nun, das Makro HEADER1_INCLUDED ist zu diesem Zeitpunkt noch nicht
defininiert, also wird der Teil innheralb des #ifndef im Quelltext
belassen. Dieser Teil beinhaltet aber ein
1 | #define HEADER1_INCLUDED
|
womit das Makro definiert wird. In weiterer Folge gibt es also dieses
Makro.
Stösst der Präprozessor auf den nächsten Include, dann wird wie immer
der Inhalt der Datei Header2 hereingezogen
1 | #include <avr/io.h>
|
2 |
|
3 | // #include "Header1.h"
|
4 | #ifndef HEADER1_INCLUDED
|
5 | #define HEADER1_INCLUDED
|
6 |
|
7 | struct test {
|
8 | int a;
|
9 | };
|
10 |
|
11 | #endif
|
12 |
|
13 | // #include "Header2.h"
|
14 | #include "Header1.h"
|
15 |
|
16 | struct check {
|
17 | struct test a;
|
18 | int b;
|
19 | };
|
20 |
|
21 | int main(void)
|
22 | {
|
23 | struct test t1;
|
24 | struct check t2;
|
25 |
|
26 | while(1)
|
27 | {
|
28 | }
|
29 | }
|
wodurch auch der 2.te #include für Header1.h wirksam wird. Erneut wird
Header1 hereingezogen
1 | #include <avr/io.h>
|
2 |
|
3 | // #include "Header1.h"
|
4 | #ifndef HEADER1_INCLUDED
|
5 | #define HEADER1_INCLUDED
|
6 |
|
7 | struct test {
|
8 | int a;
|
9 | };
|
10 |
|
11 | #endif
|
12 |
|
13 | // #include "Header2.h"
|
14 | // #include "Header1.h"
|
15 | #ifndef HEADER1_INCLUDED
|
16 | .....
|
aber diesmal ist bei der weiteren Auswertung das Makro HEADER1_INCLUDED
bereits definiert. Durch die erste Inklusion wurde es ja definiert.
Da damit die Fragestellung #ifndef mit 'Nein' zu beantworten ist, wird
der zwischen #ifndef und #endif stehende Text nicht mit in das temporäre
Zwischenergebnis mit übernommen. Als Zwischenergebnis entsteht also
1 | #include <avr/io.h>
|
2 |
|
3 | // #include "Header1.h"
|
4 | #ifndef HEADER1_INCLUDED
|
5 | #define HEADER1_INCLUDED
|
6 |
|
7 | struct test {
|
8 | int a;
|
9 | };
|
10 |
|
11 | #endif
|
12 |
|
13 | // #include "Header2.h"
|
14 | // #include "Header1.h"
|
15 | #ifndef HEADER1_INCLUDED
|
16 | #endif
|
17 |
|
18 | struct check {
|
19 | struct test a;
|
20 | int b;
|
21 | };
|
22 |
|
23 | int main(void)
|
24 | {
|
25 | struct test t1;
|
26 | struct check t2;
|
27 |
|
28 | while(1)
|
29 | {
|
30 | }
|
31 | }
|
Und jetzt ist dir struct test nur einmal definiert.
ANzumerken ist noch, dass die #-Zeilen vom Präprozessor natürlich
komplett entfernt werden. Ich hab sie nur der Anschaung wegen drinnen
gelassen. Der eigentliche C-Compiler kriegt das hier
1 | ... an dieser Stelle der komplette Inhalt von avr/io.h
|
2 | ... inklusive aller weiteren Includes
|
3 |
|
4 | struct test {
|
5 | int a;
|
6 | };
|
7 |
|
8 | struct check {
|
9 | struct test a;
|
10 | int b;
|
11 | };
|
12 |
|
13 | int main(void)
|
14 | {
|
15 | struct test t1;
|
16 | struct check t2;
|
17 |
|
18 | while(1)
|
19 | {
|
20 | }
|
21 | }
|
zu sehen und das ist offensichtlich ein korrektes C Programm, in dem
nichts doppelt vorkommt.