Forum: Compiler & IDEs Verständnisfrage zum #include beim "Auslagern" in .c und .h


von J. T. (chaoskind)



Lesenswert?

MoinMoin

ich bin grad dabei ein wenig Ordnung in mein Programm zu bringen, in 
welchem ich mir eine TWI-Routine programmiert hab, um ein Sensorshield 
abzufragen.

Dazu hab ich nun alles was sich auslagern lies (TWI_ISR, 
Shield-Routinen)
in eine blaTWi.h und eine blaSHIELD.h und die entsprechenden .c 
ausgelagert.

In den .h stehen die ganzen #defines für Register usw und die 
Funktionsprototypen. In den .c dann die Funktionen selbst.

Wenn ich nun nun im main.c die .h und die .c #include-de dann läuft 
alles wie am Schnürchen. Nur seh ich das in "professionellen" "Libs" nie 
so.
( Das hab ich grad auf der Suche nach Lösungen gelernt, eine Libary ist 
eigentlich etwas fertig kompiliertes, was ist dann so ein Sammelsorium 
von lesbarem Quelltext.c und .h eigentlich genau?(Nur ne Nebenfrage, 
eigl unwichtig) )
Dort wird immer nur die .h eingebunden. Wenn ich aber das #include bla.c 
in der bla.h mache, dann beschwert er sich in der bla.c er würd die 
ganzen Sachen die in der bla.h stehen, nicht kennen. Daraufhin hab ich 
noch probiert, die bla.h in der bla.c nochmal einzubinden. Hat aber auch 
zu nix geführt, was aber eigentlich auch so sein sollte, da ich in der 
.h #indef #def benutzt hab.

P.P.S.
Falls ihr euch das antun wollt, im Anhang die Dateien, um die es geht.
Ich versuchs grad erstmal nur mit ...SHIELD_DRIVER als die MyTWI lohnt 
eigentlich nicht anzuschauen.

Das kann doch eigentlich nicht so kompliziert sein? Aber irgendwie 
stellte es sich die paar Male die ich es bisher versucht hab, als nahezu 
unmöglich raus :D

Also ich bitte dringendst um Hilfe.



P.S.
Noch ne kleine Nebenfrage. Auf der Suche nach Hilfe bin unter anderem in 
nem Online C-Buch auf das hier gestoßen. (Nur ausm Kopf, den Link hab 
ich schon wieder nicht mehr).

a.h

#define foo

void bar(void)
{
   mach irgendwas;
}

Das wäre doch aber kein Funktionsprototyp mehr? Und müsste eigentlich in 
die .c? Ein Funktionsprototyp wäre doch sowas hier:

void bar(void);
?

MfG Chaos

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

J. T. schrieb:
> Ich versuchs grad erstmal nur mit ...SHIELD_DRIVER als die MyTWI lohnt
> eigentlich nicht anzuschauen.

Geht besonders an den, der sichs grad anschaut. Sorry es fiel mir erst 
Sekundenbruchteile nachdem ich auf Absenden geklickt hatte ein.

von Timmo H. (masterfx)


Lesenswert?

C-Files includet man nicht!
In das header file kommen Funktionsprototypen und defines, datentypen 
etc.
In der C-Datei sind dann die Funktionen selbst, welche dann natürlich 
auch ihr eigenes H-File includen muss um ggf. auch ihre eigenen 
Funktionen (über die Funktionsprototypen zu kennen, sofern sie nicht in 
der Reihenfolge des Aufrufs im C file vorkommen).

"Private" Funktionen, also jene die nur von dem "Modul" aufrufbar sein 
sollen kannst du in der C-Datei als Prototyp angeben und ggf. noch 
static deklarieren.

Gibt dazu genug im Netz: 
https://de.wikibooks.org/wiki/C-Programmierung:_Eigene_Header
https://www.cs.cf.ac.uk/Dave/C/node35.html
http://www.csse.monash.edu.au/courseware/cse2304/hndtC.html

von Sebastian V. (sebi_s)


Lesenswert?

J. T. schrieb:
> Wenn ich nun nun im main.c die .h und die .c #include-de dann läuft
> alles wie am Schnürchen. Nur seh ich das in "professionellen" "Libs" nie
> so.

Bitte keine *.c Dateien includen. Die verschiedenen *.c Dateien werden 
alle separat compiliert und später zusammen gelinked. Wie das im 
einzelnen geht hängt von deiner Entwicklungsumgebung ab. Man kann aus 
den *.c auch eine shared/static Library machen was du wohl mit "fertig 
kompiliert" meinst. Für eigene Libraries macht das aber nur Sinn wenn 
die Library einigermaßen fertig ist und sich nicht ständig ändert. Ich 
würde dir daher empfehlen erstmal bei deiner Sammlung *.c/*.h Dateien zu 
bleiben.

J. T. schrieb:
> Das wäre doch aber kein Funktionsprototyp mehr? Und müsste eigentlich in
> die .c? Ein Funktionsprototyp wäre doch sowas hier:

Ist richtig. Wenn es unbedingt in den Header soll kann man das mit 
inline machen.

von J. T. (chaoskind)


Angehängte Dateien:

Lesenswert?

Timmo H. schrieb:
> https://de.wikibooks.org/wiki/C-Programmierung:_Eigene_Header

Danke für den Link, ich versuchs grad danach. Übrigens im AVRStudio 6.2 
auf nem ATMEGA2560

In der .h wie dort beschrieben als erstes #ifndef def,
dann kommen die #defines, dann die Deklarationen, dann die 
Funktionsprototypen. In der .c auch wie dort beschrieben die .h 
includieren, dann die Variablen definieren, dann Funktionen. Siehe 
Anhang, die ist zu lang für hier.
1
#ifndef _MyTWI_H_
2
#define _MyTWI_H_ 1
3
4
#define TWI_Error_Flag 7
5
#define TWI_Read_Flag 1
6
#define TWI_Send_Flag 0
7
#define TWI_StreamingMode_Flag 2
8
9
10
#define TWI_WatchdogMax 100 //Kann nach Bedarf verändert werden.
11
#define TWI_ReadBufferSize 40
12
#define TWI_SendBufferSize 10
13
14
#include <util/twi.h>
15
16
extern volatile uint8_t TWI_Flags;
17
extern volatile uint8_t TWI_ReadBuffer[TWI_ReadBufferSize];
18
extern volatile uint8_t TWI_SendBuffer[TWI_SendBufferSize];
19
extern volatile uint8_t TWI_Watchdog;
20
21
22
extern volatile uint16_t Tick_100telMillis;
23
extern volatile uint16_t TWI_DataSize;
24
extern volatile uint16_t TWI_ReadCnt;
25
extern volatile uint16_t TWI_SendCnt;
26
27
void Timer0_init(void);
28
void TWI_ClearReadBuffer(void);
29
void TWI_ClearSendBuffer(void);
30
void TWI_init(void);
31
void TWI_Read(void);
32
void TWI_Send(void);
33
void TWI_WatchdogCheck(void);
34
35
#endif  /* _MyTWI_H_ */

Aber bei den Deklarationen gehts schon los, im Falle das ich in der 
X_NUCLEO_IKS01A1.c (Standardmäßig die main.c) das #inlcude "MyTWI.h" 
auskommemtiere. Mit extern gibt es einen Error:  63  ld returned 1 exit 
status  collect2.exe  0  0  X_NUCLEO_IKS01A1

und 62 undefinierte Referenzen auf die oben deklarierten Variablen. 
Lasse ich extern weg, bleibt der eine Fehler gleich, aber die 
undefinierten Referenzen schrumpfen auf 12 zusammen, und beziehen sich 
auf die Funktionen TWI_ClearRead/WriteBuffer. Komischerweise sollen alle 
Fehler in der X_NUCLEO_IKS01A1.c  line1  collumn1 sein.... Damit kann 
ich irgendwie garnichts anfangen.

Im Anhang nochmal der aktuelle Stand.

von J. T. (chaoskind)


Lesenswert?

Sebastian V. schrieb:
> Bitte keine *.c Dateien includen. Die verschiedenen *.c Dateien werden
> alle separat compiliert und später zusammen gelinked.

Danke für die Erläuterung, aber das hatte ich dann schon rausgefunden, 
das man dass nicht macht. Aber da ich es ohne nicht hinbekomme, frage 
ich hier nach.

: Bearbeitet durch User
von sebastian (Gast)


Lesenswert?


von Fabian O. (xfr)


Lesenswert?

In Atmel Studio musst Du die C-Dateien in das Projekt hinzufügen 
(Solution Explorer -> Dein Projekt -> Add Existing Files o.ä.). Dann 
werden sie mitcompiliert und gelinkt, ohne dass Du sie includen musst.

In das H-File gehören übrigens ausschließlich Dinge, die der Benutzer 
der Library benötigt. Interne Dinge, die Du nur zum Implementieren der 
Library brauchst, wie z.B. Registerdefinitionen, sind am Anfang der 
C-Datei besser aufgehoben.

Falls Du bestimmte Sachen (Puffergrößen, verwendete Pins, ...) 
konfigurierbar machen möchtest, würde ich dafür ein eigenes 
Config-Header-File verwenden, das in jeder Anwendung individuell sein 
kann. Damit hast Du immer die Möglichkeit, z.B. eine neue Funktion in 
den Hauptheader hinzuzufügen, ohne dass Du das von Hand in sämtlichen 
Anwendungen mit angepassten Konfigurationen nachziehen musst.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Fabian O. schrieb:
> In das H-File gehören übrigens ausschließlich Dinge, die der Benutzer
> der Library benötigt.

Schreib' nicht "Library". Hier ist weit und breit keine Library im 
Spiel.

von Le X. (lex_91)


Lesenswert?

Rufus Τ. F. schrieb:
> Fabian O. schrieb:
>> In das H-File gehören übrigens ausschließlich Dinge, die der Benutzer
>> der Library benötigt.
>
> Schreib' nicht "Library". Hier ist weit und breit keine Library im
> Spiel.

Das kam von dir schon öfter, ist und bleibt aber falsch:
https://de.wikipedia.org/wiki/Programmbibliothek

von J. T. (chaoskind)


Lesenswert?

Fabian O. schrieb:
> In Atmel Studio musst Du die C-Dateien in das Projekt hinzufügen
> (Solution Explorer -> Dein Projekt -> Add Existing Files o.ä.). Dann
> werden sie mitcompiliert und gelinkt, ohne dass Du sie includen musst.

Ich bin langsam am verfzweifeln. Genau darauf bin ich bei meinen 
Nachforschungen dann auch irgendwann gestoßen. Ich hab irgendwann auch 
keinen anderen Grund mehr gesehen. Die Dateien sind nun nämlich so 
aufgebaut, wie is in den Links beschrieben wird.

Wenn ich dann in der main.c .h einbinde, findet er halt den Kram aus .c 
nicht, und wenn die ich die .c per #include einbinde, bin ich inzwischen 
soweit, dass er sich beschwert, die ISR wäre doppelt definiert.

Wenn ich dann aber oben zitiertes versuche, dann klick ich entweder auf 
die Pünktchen, um ein Fenster zu bekommen, wo dann meine 
Ordnerstrukturen angezeigt werden und ich mir aussuchen könnte, welche 
Dateien ich angeben möchte, jedoch stürzt das Studio konsequent ab, wenn 
ich dort raufklicke. Wenn ich den Pfad per Hand in die Zeile eintrage, 
merkt er sich ums Verrecken nicht, was ich da eingetragen habe....

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

le x. schrieb:
> ist und bleibt aber falsch:

Wenn wir hier seit Jahrzehnten etablierte Fachbegriffe durch allgemeine 
Wikipedia-Interpretationen ersetzen, dann ja.

Hier geht es aber um C, bzw. um C-Compiler & Linker. Und da ist klar 
definiert, was eine Library ist.

von J. T. (chaoskind)


Lesenswert?

Auf die Libarygeschichte bin ich im Eröffnungspost schon eingegangen... 
Eine Libary ist etwas fertig kompiliertes....

Aber das ist hier nicht Thema der Diskussion. Bitte beim Thema bleiben.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

J. T. schrieb:
> Auf die Libarygeschichte bin ich im Eröffnungspost schon eingegangen

Du ja. Andere in diesem Thread habens nicht begriffen.

Übrigens: Lib_r_ary.

> Bitte beim Thema bleiben.

Alles relevante ist Dir bereits mitgeteilt worden:

1. Keine .c-Dateien mit #include einbinden

2. Alle .c-Dateien, aus denen Dein Projekt besteht, in die 
Projektverwaltung Deines Entwicklungssystemes eintragen


Und wenn Dein Entwicklungssystem (anscheinend AVR-Studio 6.2) damit 
Probleme hat, wirst Du entweder in dessen Dokumentation nachsehen 
müssen, wie man das macht, oder Deine Installation des 
Entwicklungssystems ist kaputt. Du bist ganz sicher nicht der erste, der 
ein Projekt, das aus mehreren Quelltextdateien besteht, mit AVR-Studio 
bearbeitet.

J. T. schrieb:
> Wenn ich dann in der main.c .h einbinde, findet er halt den Kram aus .c
> nicht, und wenn die ich die .c per #include einbinde, bin ich inzwischen
> soweit, dass er sich beschwert, die ISR wäre doppelt definiert.

Genaue Fehlermeldungen?

von J. T. (chaoskind)


Lesenswert?

Rufus Τ. F. schrieb:
> Genaue Fehlermeldungen?

Error  4  redefinition of 'ISR'  C:\Users\Ichich\documents\atmel 
studio\6.2\GccApplication3\GccApplication3\MyTWI.c  60  1 
GccApplication3

Ist die einzig verbleibende Fehlermeldung. Sonst noch n paar Warnings

Warning  7  control reaches end of non-void function [-Wreturn-type] 
C:\Users\Ichich\documents\atmel 
studio\6.2\GccApplication3\GccApplication3\MyTWI.c  263  1 
GccApplication3

Hiervon 2(jeweils bei den ISRs):

Warning  1  return type defaults to 'int' [enabled by default] 
C:\Users\Ichich\documents\atmel 
studio\6.2\GccApplication3\GccApplication3\MyTWI.c  43  1 
GccApplication3

Und hier von auch 2(bei den ISRs):

Warning  2  type of '__vector_21' defaults to 'int' [enabled by default] 
C:\Users\Ichich\documents\atmel 
studio\6.2\GccApplication3\GccApplication3\MyTWI.c  43  1 
GccApplication3

von J. T. (chaoskind)


Lesenswert?

Rufus Τ. F. schrieb:
> Und wenn Dein Entwicklungssystem (anscheinend AVR-Studio 6.2) damit
> Probleme hat, wirst Du entweder in dessen Dokumentation nachsehen
> müssen, wie man das macht, oder Deine Installation des
> Entwicklungssystems ist kaputt.

Laut Doku geb ich das einfach an der genannten Stelle an, jedoch stürzt 
es bei mir ab....

Mal sehen, Neuinstallation?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die Fehlermeldungen und Warnungen lassen darauf schließen, daß der 
Compiler Deine ISR-Syntax nicht kennt.

Ich verwende weder avr-gcc noch AVR-Studio, deswegen kann ich Dir nicht 
exakt sagen, wo der Fehler jetzt liegt - aber wenn ich mir ansehe, 
welche zum Compiler gehörenden Dateien Du einbindest -- keine! -- dann 
würde ich vermuten, daß mindestens
1
#include <avr/interrupt.h>

in Deinem Code fehlt.

von J. T. (chaoskind)


Lesenswert?

Rufus Τ. F. schrieb:
> Ich verwende weder avr-gcc noch AVR-Studio, deswegen kann ich Dir nicht
> exakt sagen, wo der Fehler jetzt liegt - aber wenn ich mir ansehe,
> welche zum Compiler gehörenden Dateien Du einbindest -- keine! -- dann
> würde ich vermuten, daß mindestens
> #include <avr/interrupt.h>

Oh ja, das ist ein guter Tipp, die <avr/interrupt.h> ist nur in der 
main.c drinne.
Ich installier grad Studio7, mal sehen wenn das fertig ist, werd ichs 
nochmal versuchen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die Datei gehört mindestens in jedem *.c-File eingebunden, in dem Du 
Interrupthandler schreibst, also "ISR(vektor)" verwendest.

<avr/io.h> einzubinden dürfte auch nicht störend sein.

von J. T. (chaoskind)


Lesenswert?

Ich danke dir Rufus. Nun kompiliert er fehler- und warnungsfrei.

Ich hasse das so sehr, wenn etwas an so einer Kleinigkeit liegt, die man 
ewig übersieht. Andrerseits ist es auch sehr erleichternd, dass es nun 
läuft =)

MfG Chaos

von Timmo H. (masterfx)


Lesenswert?

Und durch langes suchen und kapieren statt kopieren lernt man sogar aus 
seinen Fehlern und macht sie nicht nochmal.

von J. T. (chaoskind)


Angehängte Dateien:

Lesenswert?

Ahhhhh doch nicht... Zu früh gefreut. Ich hatte noch das #include .c in 
der main.... =(

Ich hab nun das Studio7 installiert, und dort kann ich einstellen, in 
welchem Ordner die Files liegen.

Folgende 3 Fehlermeldung schmeißt er mir:

Severity  Code  Description  Project  File  Line
Error    undefined reference to `TWI_init'  GccApplication3 
C:\Users\Ichich\Documents\Atmel 
Studio\7.0\GccApplication3\GccApplication3\Debug/.././GccApplication3.c 
18

Error    undefined reference to `TWI_Flags'  GccApplication3 
C:\Users\Ichich\Documents\Atmel 
Studio\7.0\GccApplication3\GccApplication3\Debug/.././GccApplication3.c 
20

Error    ld returned 1 exit status  GccApplication3  collect2.exe  0

Heißt doch im Klartext, er findet die .c immer noch nicht? Schließlich 
liegen dort die Definition von TWI_Flags und die Funktion TWI_init liegt 
auch dort....

Im Anhang nochmal die aktuellen Dateien, um die es geht.

von Karl H. (kbuchegg)


Lesenswert?

J. T. schrieb:

> Heißt doch im Klartext, er findet die .c immer noch nicht?

Das heisst es nicht.
Wenn dem so wäre, dann würdest du bereits vom Compiler eine 
Fehlermeldung kriegen, dass die Datei MyTWI.c nicht gefunden werden 
konnte.
Das heisst, wenn du MyTWI.c überhaupt mit ins Projekt eingefügt hast.

Und daran wird es wahrscheinlich auch liegen: du hast MyTWI.c nicht zum 
Projekt hinzugefügt.


Die Fehlermeldung stammt vom Linker und zeigt an, dass die übersetzte 
IMplementierung der Funktionen nicht gefunden werden konnte. Was kein 
Wunder ist, wenn MyTWI.c nicht mit im Projekt ist.

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

J. T. schrieb:
> und dort kann ich einstellen, in welchem Ordner die Files liegen.

Darum geht es nicht. Du musst nicht einstellen, wo irgendwelche Dateien 
sind, Du musst jede *.c-Datei einzeln zur Projektverwaltung hinzufügen, 
so daß sie im "Project Explorer" (oder wie auch immer das im Atmel 
Studio genannt wird) auch auftaucht.

von J. T. (chaoskind)


Lesenswert?

Timmo H. schrieb:
> Und durch langes suchen und kapieren statt kopieren lernt man sogar aus
> seinen Fehlern und macht sie nicht nochmal.

Ich hab ziemlich lang gesucht und bin fast verzweifelt. Auch ist nahezu 
keine Zeile kopiert. Ich hatte mal was, in nem anderen Thread, mit der 
UART drin. Da hab ich mir die Baudratenberechnung rauskopiert, die ist 
praktisch.

Ansonsten hab ich jede Zeile selbst geschrieben. Die Definitionen der 
Register abzutippen war ne stumpfe Fleißarbeit.


Und langsam kapiere ich ja auch. Wenn du mein Eingangsposting gelesen 
hast, solltest du auch mitbekommen haben, dass ich mir sogar bewusst 
war, dass man eigentlich keine .c indludedet, ich mir aber nicht anders 
zu helfen wusste, und genau das dann ja der Anstoß meiner Nachfrage war.

Was ich damit eigentlich sagen will ist: Was willst du mir damit sagen?
Wieso kann hier nichtmal ein einziger Thread laufen, ohne das irgendwer 
seine völlig belanglose Meinung zu irgendwas, das mit dem eigentlichen 
Thema in keinster Weise zu tun hat, kund tut?

Wie kommst du darauf, das mein Code zusammenkopiert ist?

Die Statemachine für die TWI hab ich auch erst aus dem Bauch raus 
runtergeschrieben, nach dem ATMEGA Datenblatt, für die 
Statusregisterwerte.
Dann bin ich auf die Appnote dazu gestoßen, und bis auf ein zwei 
Fehlerfälle hatte ich es genau so umgesetzt, wie dort beschrieben. Nach 
der Lektüre hab ich noch die Fehler angepasst, und sie lief. Dann wollte 
ich sie auslagern, hab dazu dieses und jenes gelesen (evtl nicht 
verstanden) und das über einen Zeitraum von mehreren Tagen. Wobei die 
meiste Zeit grübeln und nicht mehr lesen war. Und kurz vor der 
Verzweiflung, fragte ich dann hier nach. Meistens konnte mir hier sehr 
gut geholfen werden. Auch wenn es des öfteren solche Kleinigkeiten 
waren, wie von Rufus angemerkt. Man hätte selber draufkommen können. 
Aber du kennst das sicher auch, wenn du eine Zeile gefühlte 100000 mal 
gelesen hast, bist du irgendwann betriebsblind.

von J. T. (chaoskind)


Lesenswert?

Rufus Τ. F. schrieb:
> Darum geht es nicht. Du musst nicht einstellen, wo irgendwelche Dateien
> sind, Du musst jede *.c-Datei einzeln zur Projektverwaltung hinzufügen,
> so daß sie im "Project Explorer" (oder wie auch immer das im Atmel
> Studio genannt wird) auch auftaucht.

Sowas wie in CodeBlocks "Add File to Project"? Sowas hab ich auch schon 
gesucht... Ich werd nochmal googeln statt selber zu gucken... Meld mich 
gleich nochmal.

Danke nochmal für deine Unterstützung

von J. T. (chaoskind)


Lesenswert?

JAWOLLJA!!!

So klappt es. Irgendwo gibt es rechts das Fensterchen, in dem man beim 
Debuggen die Register angucken kann. Dort versteckt sich in einem Reiter 
der "Solution Explorer", dort kann man dann mit nem Rechtsklick die 
Files hinzufügen.

Also nun endgültig, vielen Dank für die Hilfe =)

MfG Chaos

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Na dann, weiterhin viel Erfolg!

von J. T. (chaoskind)


Lesenswert?

Eine Frage evtl doch noch nachgeschoben. Sie ist aber nur noch 
theoretischer Natur.

Ist der Unterschied zwischen der Version wo man quasiverbotenerweise das 
.c in main.c includedet, und dem zum Projekt hinzufügen der, das im 
ersten Fall die sämtliche .c in ein File gehen, dass dann compiliert 
wird? Und im letzteren Fall werden die .c einzeln compiliert und erst 
vom Linker zusammengeführt?

Warum soll man dann nicht die .c includen? Ist das nicht eigentlich 
egal, wo die Files zusammengeführt werden? Da sollte am Ende doch 
trotzdem das selbe bei rauskommen? Ich las irgendwo was vom 
"ASIF"-Prinzip, das zwar eher was mit der Optimierung zu tun hat, aber 
sollte hier nicht was ähnliches gelten?

von Karl H. (kbuchegg)


Lesenswert?

J. T. schrieb:
> Eine Frage evtl doch noch nachgeschoben. Sie ist aber nur noch
> theoretischer Natur.
>
> Ist der Unterschied zwischen der Version wo man quasiverbotenerweise das
> .c in main.c includedet, und dem zum Projekt hinzufügen der, das im
> ersten Fall die sämtliche .c in ein File gehen, dass dann compiliert
> wird? Und im letzteren Fall werden die .c einzeln compiliert und erst
> vom Linker zusammengeführt?

Genau.

> Warum soll man dann nicht die .c includen?

Weil du es richtig machen sollst. Auch bei kleinen Projekten.
C ist so hingetrimmt, dass man Dinge einzeln kompilieren kann und nicht 
alles in einem Rutsch compilieren muss. Eines der letzten Projekte, in 
denen ich beteiligt war, bestand aus ein paar tausend Files. Alles 
durchzucompilieren dauert etwas über eine Stunde. Da ist man doch froh, 
wenn nach einer Änderung in einem C-File lediglich dieses eine C-File 
neu compiliert werden muss, was in ein paar Sekunden abgeht.

von J. T. (chaoskind)


Lesenswert?

Karl H. schrieb:
> Weil du es richtig machen sollst. Auch bei kleinen Projekten.
> C ist so hingetrimmt, dass man Dinge einzeln kompilieren kann und nicht
> alles in einem Rutsch compilieren muss. Eines der letzten Projekte, in
> denen ich beteiligt war, bestand aus ein paar tausend Files. Alles
> durchzucompilieren dauert etwas über eine Stunde. Da ist man doch froh,
> wenn nach einer Änderung in einem C-File lediglich dieses eine C-File
> neu compiliert werden muss, was in ein paar Sekunden abgeht.

Ja das leuchtet mir ein. Danke dir für deine wie immer treffenden Worte!

Ich denke, nun sind tatsächlich alle Klarheiten beseitigt :D

von Fabian O. (xfr)


Lesenswert?

Ein anderer Grund, warum man mehrere C-Dateien getrennt kompiliert, sind 
Daten und Funktionen, die als "static" deklariert sind. Diese sind nur 
innerhalb der jeweiligen Übersetzungseinheit gültig.

Wenn man jede C-Datei einzeln kompiliert, ist jede C-Datei (samt den 
Header-Files, die sie inkludiert) eine eigene Übersetzungseinheit. Man 
kann also problemlos jeweils ein "static int x" in zwei C-Dateien haben, 
ohne dass es einen Konflikt gibt.

Wenn man dagegen den Code aus allen C-Dateien in eine C-Datei inkludiert 
und compiliert, gibt es nur eine Übersetzungseinheit und dementsprechend 
Fehler, wenn in mehreren Dateien static-Elemente mit gleichem Namen 
definiert sind.

von xXx (Gast)


Lesenswert?

Bei kleineren Projekten, gerade fuer Mikrocontroller, kann es durchaus 
sinnvoll sein, alles zu einem grossen Sourcefile zusammen zu setzen: Der 
Compiler kann so wesentlich mehr und besser optimieren.

von Sebastian V. (sebi_s)


Lesenswert?

xXx schrieb:
> Bei kleineren Projekten, gerade fuer Mikrocontroller, kann es
> durchaus
> sinnvoll sein, alles zu einem grossen Sourcefile zusammen zu setzen: Der
> Compiler kann so wesentlich mehr und besser optimieren.

Seitdem es LTO (Link Time Optimization) gibt ist das auch nicht mehr der 
Fall.

von Benjamin K. (exs)


Lesenswert?

Die Frage ob man c-Dateien direkt inkludiert oder nicht scheint mir 
schnell in einen Glaubenskrieg auszuarten.
Ich habe das auch mal so gelernt, dass ein #include ".c" schlechter Stil 
ist.
Mittlerweile ist mir im Hinblick auf CleanCode, Architektur-Design und 
Testen nicht mehr so klar warum.

Vielmehr bin ich der Meinung, das man wie immer die Optionen abwägen 
muss.

Gehen wir die einzelnen Themen mal durch:

Testen:

Hintergrund:
Beim Testen per Unit-Test muss dem Linker bekannt sein, welche 
Funktionen (Symbole) es gibt, so dass diese durch "Fake/Mock"-Funktionen 
für den Unit-Test
ersetzen werden können. Im Fall von "static"-Funktionen ist dies nicht 
möglich, da diese Funktionen außerhalb der Compiliereinheit (die 
c-Datei) nicht bekannt
sind. Meines Wissens gibt es hier nur zwei Möglichkeiten um solche 
Funktionen zu testen:

Möglichkeit #1 - Anlegen eines Defines und eines entsprechenden 
Compiler-Schalters (Beispiel: gcc -D__TEST__ ...)
1
#ifdef __TEST__
2
#define PRIVATE
3
#else
4
#define PRIVATE static
5
#endif

Möglichkeit #2 - Direktes inkludieren der C-Datei
1
#include "unit.c"

Was sind die Vorteile der #1 Version?
- Ich muss keine c-Dateien inkludieren.
Was sind die Nachteile der #1 Version?
- Ich muss das Schlüsselwort "static"-Situationsbedingt definieren. Ich 
darf das Schlüsselwort bei Funktionen nicht direkt verwenden,
sondern muss stattdessen "PRIVATE" verwenden (bzw. was auch immer 
stattdessen definiert wurde).
- Ich muss den zu testenden Code auf Tests vorbereiten. Streng genommen 
ist das zu testende Programm ein anderes als das welches Produktiv 
eingesetzt wird.

Was sind die Vorteile der #2 Version?
- Ich spare mir die Definition und den Compilerschalter.
- Ich muss den Code nicht verändern / auf das Testen vorbereiten
Was sind die Nachteile der #2 Version?
- Ich muss eine neue Konfiguration für das Compilieren zum Testen 
erstellen (zum Beispiel in Eclipse CDT)
  und die Datei "unit.c" explizit vom compilieren in dieser 
Konfiguration ausschließen, da sonst doppelte Definitionen auftreten


Architektur-Design:

Hintergrund:
Die Komplexität von Software lässt sich leichter beherrschen, wenn das 
Gesamtsystem in Teilsysteme - Module - gegliedert wird. Jedes Modul 
besteht aus einer Schnittstelle
(header-Datei) welche vom Anwender des Moduls eingebunden wird, um die 
Funktionalität des Moduls zu nutzen. Grundsätzlich sollte in dieser 
"öffentlichen"
(=von außerhalb des Moduls genutzten) Schnittstelle nur Definitionen und 
andere Header-Dateien bereitgestellt werden, welche unmittelbar zur 
Nutzung der Schnittstelle
benötigt werden. Der Nutzer sollte nicht gezwungen werden Elemente zu 
inkludieren, welche nicht zur Nutzung der Schnittstelle des Moduls 
benötigt werden. Es sollten
weiterhin keine Definitionen auftauchen die nur innerhalb des Moduls 
genutzt werden (Prinzip der Kapselung). Daraus folgt, dass falls ein 
Modul
sehr komplex ist, dieses durchaus aus mehreren c-/h-Dateien bestehen 
kann. Was dann zu dem Problem führt wie man Nutzer des Moduls davon 
abhält nicht öffentliche
Header-Dateien einzubinden.


Möglichkeit #1 - Anlegen von private.h-Dateien um gemeinsame 
Definitionen und Prototypen für Funktionen des Moduls bereitzustellen
Beispiel Unterverzeichnis Modul:
module
+ - modulePublicInterface.h
1
      #include <stdint.h>
2
      #define SUPERIMPORTANT_PUBLIC_DEFINE ...
3
      extern ... function ...
+ - modulePublicImplementation.c
1
      #include "modulePublicInterface.h"
2
      #include "modulePrivateInterface.h"
+ - modulePrivateInterface.h
1
      #define COMMON_TO_ALL_CODE_WITHIN_MODULE ...
2
      extern ... function A(...)
+ - modulePrivateImplementation.c (<-Modul-interne Funktionen dürfen 
nicht "static" sein! Da andere Compiliereinheit)
1
      #include "modulePrivateInterface.h"
2
      static ... function ... //(<- Nur in "modulePrivateImplementation.c" verwendbar)
3
      ... function A(...) //(<- Prototyp in "modulePrivateInterface.h" definiert)
Möglichkeit #2 - Direktes inkludieren von privaten C-Dateien
Beispiel Unterverzeichnis Modul:
module
+ - modulePublicInterface.h
1
      #include <stdint.h>
2
      #define SUPERIMPORTANT_PUBLIC_DEFINE
3
      extern ... function ...
+ - modulePublicImplementation.c
1
      #include "modulePublicInterface.h"
2
      #define COMMON_TO_ALL_CODE_WITHIN_MODULE ...
3
      #include "modulePrivateImplementation.c"
+ - modulePrivateImplementation.c
1
      #define ...
2
      static ... function ...
Im ersten Fall gebietet es nur die Vernunft der Anwender die Datei 
"modulePrivateInterace.h" nicht direkt zu verwenden. Im zweiten Fall 
stellt sich diese Option dem Anwender erst nicht. Er hat nur die Datei 
"modulePublicInterface.h" zur Verfügung.

Definiert man Prototypen für statische Funktionen müssen diese gepflegt 
werden! Insbesondere die oft stiefmütterlich behandelte Dokumentation 
muss immer nachgezogen werden. Daher wird im Allgemeinen angestrebt die 
Dokumentation zentral an einer Stelle durchzuführen. Im Fall der 
öffentlichen Header-Datei werden die meisten sicher die Dokumentation 
der Funktionen dort vornehmen, zum Beispiel mit Doxygen. Dies kommt 
sogleich dem Anwender der Schnittstelle zu gute.

Die interne Dokumentation der Funktionen für die nachfolgenden 
Bearbeiter kann dann entweder bei der Deklaration der Protoypen der 
statischen Funktionen oder bei den Definitionen der statischen 
Funktionen selbst stehen. Spart man sich die Prototypen, stellt sich 
auch diese Frage nicht.

Um die Übersicht zu wahren, welcher Teil des Codes "privat" und welcher 
Teil  "öffentlich" ist bietet es sich an die privaten Funktionen 
statisch zu definieren und in eine eigene C-Datei auszulagern. Diese 
kann dann direkt in der Hauptdatei des Moduls inkludiert wird (siehe 
Möglichkeit #2)

Ich kann auch sagen, dass MISRA-C:2012 keine Regel hat die ein 
inkludieren von C-Dateien untersagt. Ebenfalls ist mit keine 
Fachliteratur bekannt die dies explizit mit Beispielen, warum man das 
nicht tun sollte, belegt.

Aus meiner Sicht ist es ganz klar eine Sache der Erfahrung und des 
Abwägens der Vor- und Nachteile. Es kann wie die Verwendung des 
goto-Statements sinnvoll sein oder furchtbar missbraucht werden. Das 
Einbinden von C-Dateien ist zumindest "ungewohnt".

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.