Forum: Compiler & IDEs Header und C-Dateien


von Mike (Gast)


Lesenswert?

Hallo,

eigentlich komme ich mit den im Betreff genannten Dateien mittlerweile
gut zurecht.
In den C-Dateien stehen nur noch die Funktionen, in den H-Dateien nur
deren Deklarationen und die globalen Variablen.
In der C-Datei wird dann jeweils oben "#include "xyz.h"
hingeschrieben. In den H-Dateien kommt noch die Anweisung #ifndef ...
usw. hin. So kann ich auch prima Querzugriffe zwischen den einzelnen
Bibliotheken meines Projektes machen.
Will ich aber eine main.h definieren, in der ich ensprechend bestimmte
H-Dateien per #include ... einbinde, so kann ich in meiner main.c (in
der ganz oben #include "main.h" steht) nicht mehr auf die in denen
enthaltenen Funktionen zugreifen.
Wo liegt mein Fehler?

Gruß, Mike

von Matthias (Gast)


Lesenswert?

Hi

sollte eigentlich bis zu einer gewissen Tiefe problemlos funktionieren.
Compiler kaputt?

Matthias

von Dirk (Gast)


Lesenswert?

Hi,

Prototypen angelegt?

Mfg

Dirk

von Alex (Gast)


Lesenswert?

In den H-Dateien stehen die Prototypen.
Muss ich in der main.h auch die main () als Prototyp definieren?

von Matthias (Gast)


Lesenswert?

Hi

nö. Kann dein Compiler nach dem Präprozessorlauf abbrechen und die
Ergebnisse ausgebe? Dann kannst du sehen ob die .h richtig eingebunden
werden.

Matthias

von Peter D. (peda)


Lesenswert?

"in den H-Dateien nur
deren Deklarationen und die globalen Variablen."

Falsch.

Globale Variablen werden in einem C-File angelegt:

int foo[16];

Im H-File steht nur der Prototyp:

extern int foo[16];

oder

extern int foo[]; // hier geht aber nicht mehr sizeof()


#ifndef verwendet man nur dazu, um das File selber von einer
Doppelausführung auszuschließen, nicht aber andere.


Peter

von Mike (Gast)


Angehängte Dateien:

Lesenswert?

Irgendwie raffe ich das nicht. Habe jetzt mal ein extrem simples
Beispiel gemacht.
4 Dateien
test.c
test.h
main.c
main.h

Habe in der main.h die Zeile #include "test.h" stehen. In der test.c
steht #include "test.h" und in der main.c steht #include "main.h".

Nur kann ich mit dieser Konfiguration nicht von der main.c auf die
test.c zugreifen.

Habe es mal angehangen, die Fehlermeldung liegt als Textdatei bei.

Gruß, Mike

von Rolf Theisinger (Gast)


Lesenswert?

Hi Mike,

du musst in "main.c" die Methoden aus "test.c" mit der
"extern"-Deklaration bekannt machen, nicht in "test.h". Der
Compiler muss wissen, dass es diese Methoden gibt, der Linker wird
anschließend die passende Methode zuordnen.

Also, in "main.c":
extern void test(void);

Nicht vergessen: "test.c" an den Linker übergeben!

Da du anscheinend noch nicht so viel programmiert hast, beantworte dir
mit geeigneter Literatur oder dem Internet folgende Fragen:
1. Welche Aufgabe hat der Präprozessor?
2. Welche Aufgabe hat der Compiler und wie parametriere ich ihn?
3. Welche Aufgabe hat der Linker?
4. Wie benutze ich make, bzw. wie erstelle ich ein Makefile?

Programmieren ist am Anfang ein langsamer Prozess, später wird es
schwierig :-)

Viele Grüße
Rolf

von OldBug (Gast)


Lesenswert?

Na, das ist so nicht ganz richtig!

Er muss die Prototypen nicht in der main.c einbinden, sondern besser in
der test.h. Da alle Prototypen mit "extern" versehen -- fertig.

von Mike (Gast)


Lesenswert?

Irgendwie bin ich jetzt nicht wirklich schlauer.
Wenn ich in der main.c alle Funktionsprototypen nochmal hinschreiben
muss, dann kann ich mir das alles auch schenken.

Lasse ich die main.h weg und schreibe oben in die main.c die Anweisung
#include "test.c" , #include "test2.c" usw. dann kann ich beliebig
auf alle Routinen zugreifen.

Hatte mir nur gedacht, dass es eine Möglichkeit gibt, dem Schema treu
zu bleiben und auch eine main.h nach dem selben Muster wie bei den
anderen C-Dateien zu erstellen.

Wie dem auch sei, erstmal danke für eure Ratschläge.

Gruß, Mike

von OldBug (Gast)


Lesenswert?

Huiuiui...

MOMENT! :)

Ein kleines Beispiel:

----

test.h:

extern void foo(void);

----

test.c:

void
foo(void)
{
    /* irgendwas */
}

----

main.c:

#include "test.h"

int
main(void)
{
    foo();
    return 0;
}

von Peter D. (peda)


Lesenswert?

Wenn im main.c das #include "test.c" funktioniert, dann liegt der
Fehler in Deiner make-Datei.

Du must sowohl test.c als auch main.c compilieren und dann test.o +
main.o linken.

Ich benutze den GCC ohne make auf der Kommandozeile und sage ihm als
Quelldatei *.c.
Damit sucht er sämtliche *.c im aktuellen Verzeichnis zusammen,
compiliert sie und link sie alle.

Das make kann leider nicht *.c automatisch expandieren.
Da must Du jede einzelne Datei zu Fuß mit einbinden.


Peter

von Mike (Gast)


Lesenswert?

Als Makefile dient das im WinAVR-Paket enthaltene Beispiel ohne jegliche
Anpassung (außer Programmierdongle, MCU und Ziedatei).

von OldBug (Gast)


Lesenswert?

Nein, Peter, das hat mit dem Makefile nichts zu tun.
Wenn er #include "foo.c" macht, dann macht der Präprozessor nichts
anderes, als den Inhalt von foo.c an der stelle des Includes
einzufügen. D.h. der Code von foo.c steht in der gleichen Datei.
Damit werden die Prototypen "überflüssig" (nicht wirklich, aber für
die Erklärung reichts). Wenn im Code darunter dann diese Funktionen
(scheinbar aus foo.c) verwendet werden, gibts keine offenen Referenzen.

von Rolf Theisinger (Gast)


Lesenswert?

O.k., mal etwas Licht ins Dunkle schicken:

ich hatte nicht gesehen, dass in "main.h" auch noch "#include
<test.h>" steht. So sind natürlich die Prototypen bekannt. Aber so
weiß der Linker  noch immer nicht, was er zu tun hat. Das erklärt auch
die Ausgabe "undefined reference": die Datei "test.c" ist dem
Linker nicht bekannt!

Durch das "#include <test.c>" ist natürlich keine extern-Deklaration
mehr notwendig, da nun die Dateien "test.c" und "main.c" vom
Präprozessor "zusammengebaut" werden.

Mit meiner ersten Antwort wollte ich nicht verwirren, aber wichtig ist
es, den Weg vom Quellcode zur ausführbaren Datei zu verstehen.

Viele Grüße
Rolf

von Rolf Theisinger (Gast)


Lesenswert?

>> die Datei "test.c" ist dem Linker nicht bekannt!

ich meine natürlich die Datei "test.o", welche aus
"test.c" erzeugt wird ;-)

von Mike (Gast)


Lesenswert?

@ Rolf Theisinger

>Aber so
>weiß der Linker  noch immer nicht, was er zu tun hat. Das erklärt
auch
>die Ausgabe "undefined reference": die Datei "test.c" ist dem
>Linker nicht bekannt!

Und was muss ich nun machen. Im Makefile was ändern?

von OldBug (Gast)


Lesenswert?

Auszug aus dem makefile:

----

# List C source files here. (C dependencies are automatically
generated.)
SRC = $(TARGET).c

# If there is more than one source file, append them above, or modify
and
# uncomment the following:
#SRC += foo.c bar.c

# You can also wrap lines by appending a backslash to the end of the
line:
#SRC += baz.c \
#xyzzy.c

----

# uncomment the following:
#SRC += foo.c bar.c

Wenn Du das in

SRC += test.c

änderst, dann sollte alles Kompiliert und gelinkt werden.

von Peter D. (peda)


Lesenswert?

@OldBug

"Nein, Peter, das hat mit dem Makefile nichts zu tun."

Doch.

Wenn der Linker nämlich test.o mit linken würde, müßte er dann meckern,
daß die Funktion test() zweimal existiert.

Da er aber nicht meckert, ist also das make-File falsch.


Peter

von Rolf Theisinger (Gast)


Lesenswert?

Hi Mike,

dein Programm ist ja schon modular aufgebaut. Du hast mehrere Dateien,
in denen deine Methoden stehen. Um Verwechslungen und mehrfache
#inlcude-Direktiven zu vermeiden, solltest du nach Möglichkeit die
Headerfiles des einen Moduls (hier test) nicht in den Headerfiles des
anderen (hier main) einbauen, sondern in den Code-Files (hier main.c).
In deinem Fall ist das jedoch nicht nötig. Ebenso ist es nicht nötig,
eine Datei "test.h" mit extern-Deklarationen zu erstellen, da du dem
Linker eigentlich anweist, in einem anderen Modul nach der Methode zu
suchen.  Gleichzeitig bindest du "test.h" in "test.c" ein, wo es
auch eine Methode "void test(void)" gibt.

Ganz kurz: der Compiler erzeugt aus den vom Präprozessor zusammen
gestellten Datein die object-Dateien (Endung .o). Diese Module werden
dann vom Linker zusammen gebracht und es wird eine ausführbare Datei
erzeugt.

Dein Programm wird kompiliert, wenn du
1. "#include <test.h> aus "main.h" entfernst
2. die extern-Deklarationen aus "test.h" entfernst
für "void test(void) brauchst du dort keinen Prototypen angeben.
3. den Datentyp von i ändern. i++ und i-- werden von einem C-Compiler
übrigens nicht erkannt (C++)!!!
Ganze Zahlen <=> int!!!
4. extern void test(void) nach main.c
5. "main.h" löschen, extern void main(void) macht keinen Sinn,
sollte auch eher sowas sein wie int main (int argc, char** argv)
6. gcc -c test.c -o test.o übersetzt dir "test.c" ohne Linker
7. gcc -c main.c -o main.o entsprechend für "main.c"
7. gcc -Wall -pedantic -o main main.o test.o erstellt dir eine
ausführbare Datei mit Namen main und benutzt hierfür die Module main.o
und test.o (ja, sie werden gelinkt).

Vielleicht probierst du es erstmal ohne Makefile, liest dich dann in
die Materie ein (siehe meine erste Antwort).

Viele Grüße und viel Erfolg (habe jetzt keine Zeit mehr :-)
Rolf

von Mike (Gast)


Lesenswert?

@ OldBug

Danke für den Hinweis.

@Peter
Wenn du mit falsch meinst, dass im Makefile die von OldBug genannten
Zeilen fehlen, dann hast du natürlich recht. Auch wenn mir sein Posting
trotz allem mehr geholfen hat.

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.