Forum: Mikrocontroller und Digitale Elektronik C -> Headerfile -> Variablen und Funktionen initalisieren


von Jan H. (janiiix3)


Lesenswert?

Halli Hallo,
habe da mal eine Frage..
Gibt es eigentlich irgendwelche Probleme wenn man Variablen ( Strukturen 
etc. ) in der Headerdatei initialisiert?
Und was spricht dagegen Funktionen „inline“  in das Headerfile zu 
implementieren?
z.B
1
extern inline enum eTimerError Timer1CompAInit( const sTimer16Config_t *psTab , void (*pFncCallback)(void) )
2
{
3
  cli(); /**< Vorsichtshalber Interrupts global sperren */
4
5
  TCCR1A  = psTab->uiWGMxx;
6
  TCCR1B  = psTab->uiWGMxx;
7
  TCCR1B  |= psTab->uiCSxx;
8
  OCR1A  = psTab->uiCnt;
9
  
10
  if ( pFncCallback == NULL )
11
  {
12
    return ERROR_TIMER_NO_ADDRESS;
13
  }else
14
  {
15
    pvTimerCallback[CALLBACK_TIMER1_COMPA] = pFncCallback;
16
  }
17
  
18
  _ENABLE_OCIE1A;
19
20
  sei();
21
  
22
  return ERROR_TIMER_OK;
23
}

von Theor (Gast)


Lesenswert?

Probleme mit Definitionen von Variablen (das ist die Voraussetzung für 
deren Initialisierung) und Funktionen, ergeben sich aus der Tatsache, 
dass mehrfache Definitionen nicht möglich sind und in einer 
Fehlermeldung (des Linkers) münden, falls die fragliche Header-Datei 
in mehr als eine C-Datei des selben Projekts eingefügt wird. Gerade das 
ergibt nämlich Mehrfachdefinitionen.

Das bedeutet nämlich, dass mehr als eine Objektdatei entsteht, welche 
die selben Variablen, die selben Funktionen enthalten. Eine Situation in 
welcher  der Linker sich darüber beschweren wird. Er kann nämlich nicht 
entscheiden, welche der gleich benannten Variabeln und Funktionen er 
verwenden soll.

von Theor (Gast)


Lesenswert?

Was das extern-Attribut einer Funktion betrifft, der die Definition auf 
dem Fusse folgt, bin ich schlicht überfragt. Ich vermute, dass eine 
Fehlermeldung aufttritt. Das folgende also mit einem Vorbehalt, dass mir 
hier ein moderner C11-"Trick" entgangen ist.

Der Sinn von "extern" ist nämlich gerade, dem Compiler mitzuteilen, dass 
die Definition an anderer Stelle, extern im Verhältnis zu der gerade 
vorliegenden Quellcodedatei, erfolgt. Plötzlich aber folgt sie doch 
direkt in der Datei.
Sowas habe ich noch nie versucht, weil es widersinnig ist, oder mir 
jedenfalls so scheint.
Entweder benutze ich eine externe Definition oder eine unmittelbare.
Beides zusammen führt auf einen Widerspruch.

von Johnny B. (johnnyb)


Lesenswert?

Naj H. schrieb:
> Gibt es eigentlich irgendwelche Probleme wenn man Variablen ( Strukturen
> etc. ) in der Headerdatei initialisiert?

Compiler und Linker unterscheiden grundsätzlich nicht zwischen Header- 
und Codedateien; man könnte beliebigen Code in die Header- oder 
Codedatei reintun und es funktioniert trotzdem.
Nur ist es halt üblich und hat sich so bewährt, dass man den 
eigentlichen Code in die Codedatei packt und die "Schnittstelle" dazu, 
in Form von Vorwärtsdeklarationen und Definitionen etc., in eine 
separate Headerdatei.
Dies verbessert die Übersichtlichkeit und Wiederverwendbarkeit.

von x^2 (Gast)


Lesenswert?

Du hast da ganz unabhängig von der inline Thematik auch ein Problem in 
deiner Funktion.

Du entsperrst die Interrupts nicht wieder, falls die Funktion über 
diesen Zweig verlassen wird:
1
  if ( pFncCallback == NULL )
2
  {
3
    return ERROR_TIMER_NO_ADDRESS;

von Maxim B. (max182)


Lesenswert?

Theor schrieb:
> Probleme mit Definitionen von Variablen (das ist die Voraussetzung für
> deren Initialisierung) und Funktionen, ergeben sich aus der Tatsache,
> dass mehrfache Definitionen nicht möglich sind und in einer
> Fehlermeldung (des Linkers) münden, falls die fragliche Header-Datei
> in mehr als eine C-Datei des selben Projekts eingefügt wird. Gerade das
> ergibt nämlich Mehrfachdefinitionen.
>
> Das bedeutet nämlich, dass mehr als eine Objektdatei entsteht, welche
> die selben Variablen, die selben Funktionen enthalten.

Das ist doch kein Problem!
man sollte nur Inhalt von *.h-Datei mit folgenden Zeilen umrahmen:
für main.h wäre das so:
1
// main.h
2
#ifndef MAIN_H
3
#define MAIN_H 
4
5
/*** Inhalt ***/
6
7
8
#endif  /* MAIN_H */

Naj H. schrieb:
> Und was spricht dagegen Funktionen „inline“  in das Headerfile zu
> implementieren?

Dagegen spricht nichts.

: Bearbeitet durch User
von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Naj H. schrieb:
> Und was spricht dagegen Funktionen „inline“  in das Headerfile zu
> implementieren?

nichts, UND das ist standard in vielen projekten, die umfangreich 
konfiguriert werden.

mit optionalen code in headers kannst du auch bei viel optionalen code 
gut strukturieren und den überblick waren - eigentlich nur so.

kannst du dir quasi in jedem open source project anschauen!


mt

von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> Das ist doch kein Problem!
> man sollte nur Inhalt von *.h-Datei mit folgenden Zeilen umrahmen:
> für main.h wäre das so:

Das löst nur das Problem, wenn in einer C-Datei dieser Header evtl durch 
andere Header auch/nochmal inkludiert werden.

Es löst aber ncht das Problem, wenn man mehrere C-Dateien hat.

von Maxim B. (max182)


Lesenswert?

Dirk B. schrieb:
> Es löst aber ncht das Problem, wenn man mehrere C-Dateien hat.

Man kann beliebig C-Dateien haben, die beliebig viele H-Dateien 
includieren. Die #def - Zeilen schutzen von mehrfachen includieren. So 
wird in einer H-Datei definierte Variable nur einmal definiert. Die 
Variablen, die nur in C-Datei definiert sind, bleiben nur in dieser 
C-Datei sichtbar, so können mehrere Variablen in verschiedenen C-Dateien 
gleich heißen.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> Man kann beliebig C-Dateien haben, die beliebig viele H-Dateien
> includieren. Die #def - Zeilen schutzen von mehrfachen includieren. So
> wird in einer H-Datei definierte Variable nur einmal definiert. Die
> Variablen, die nur in C-Datei definiert sind, bleiben nur in dieser
> C-Datei sichtbar, so können mehrere Variablen in verschiedenen C-Dateien
> gleich heißen.

Nein.

Die Include-Guards schützen nur vor mehrfachen inkludieren in einer 
C-Datei.
Diese werden getrennt übersetzt und der Compiler legt auch jedesmal neue 
Variablen an.
Die Sichtbarkeit wird nur eingeschränkt, wenn diese als static definiert 
sind.

Wenn sie global sind (und denselben Bezeichner haben), hat der Linker 
ein Problem.

: Bearbeitet durch User
von x^2 (Gast)


Lesenswert?

Dirk B. schrieb:
> Maxim B. schrieb:
>> Das ist doch kein Problem!
>> man sollte nur Inhalt von *.h-Datei mit folgenden Zeilen umrahmen:
>> für main.h wäre das so:
>
> Das löst nur das Problem, wenn in einer C-Datei dieser Header evtl durch
> andere Header auch/nochmal inkludiert werden.
>
> Es löst aber ncht das Problem, wenn man mehrere C-Dateien hat.

Deshalb die Funktion per
1
static inline
definieren, dann geht das.

Da inline nur ein Hint an den Compiler ist, muss man im Zweifel nochmal 
compilerspezifisch nachhelfen, um wirklich ein Inlining zu garantieren.

GCC z.B. via
1
__attribute__((always_inline))

von Maxim B. (max182)


Lesenswert?

Dirk B. schrieb:
> Die Sichtbarkeit wird nur eingeschränkt, wenn diese als static definiert
> sind.

Nicht nur. Es reicht, eine Variable nur in einer C-Datei zu definieren 
und in keiner H-Datei. Somit wird sie nirgendwo sonst sichtbar. Noch 
besser ist diese Variable innerhalb einer Funktion zu definieren.

"static" hat etwas andere Funktion. Zwingt Compiler die Variable in RAM 
und nicht in Register oder Stack zu legen.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

x^2 schrieb:
> Deshalb die Funktion per
>
>   static inline
>
> definieren, dann geht das.
>
> Da inline nur ein Hint an den Compiler ist, muss man im Zweifel nochmal
> compilerspezifisch nachhelfen, um wirklich ein Inlining zu garantieren.
>
> GCC z.B. via
> __attribute__((always_inline))

Sobald die Funktion static ist wird es keine Konflikte mit 
Mehrfachdefinitionen mehr geben, dann ist es eigentlich auch völlig egal 
ob die betreffende Funktion inline ist oder nicht.

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

x^2 schrieb:
> Da inline nur ein Hint an den Compiler ist, muss man im Zweifel nochmal
> compilerspezifisch nachhelfen, um wirklich ein Inlining zu garantieren.
>
> GCC z.B. via
> __attribute__((always_inline))

Das ist nur notwendig, wenn man unbedingt verhindern wird, daß die 
Funktion mit push und pop die Zeit verschwendet.
Wenn man einfach "inline" schreibt, wird die Entscheidung dem Compiler 
überlassen, trotzdem kann inline-Funktion ruhig in H-Datei bleiben.

von Einer K. (Gast)


Lesenswert?

Ein kleinwenig fehlt mir das Verständnis, warum versucht wird, eine 
solche Frage in einem "demokratischem Prozess" zu lösen.

Sollte man nicht besser:
1. Die jeweilig benutzte Sprachreferenz zu Rate ziehen?
2. Die Doku des verwendeten Compilers befragen?
3. Ein gutes Buch kaufen und dieses auswendig lernen?

Und dann erst, wenn dann noch was unklar ist, eine konkrete Frage 
stellen?
So richtig, mit Angabe: Welche Sprache, welcher Compiler?

Bitte nicht falsch verstehen!
Ich habe nichts gegen demokratische Prozesse. Auch nicht in Foren.
Wenn soziale Aspekte im Spiel sind, kann das durchaus der angemessene 
Weg sein.
Aber bei rein technischen Fragen?
Doch eher nicht....

von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> "static" hat etwas andere Funktion. Zwingt Compiler die Variable in RAM
> und nicht in Register oder Stack zu legen.

Das sind technische Einzelheiten (der Stack liegt durchaus auch im RAM), 
die man nicht wissen muss.

static schränkt die Sichtbarkeit von Variablen und Funktionen auf die 
aktuelle C-Datei (übersetzungseinheit) ein.

von Maxim B. (max182)


Lesenswert?

Dirk B. schrieb:
> Das sind technische Einzelheiten (der Stack liegt durchaus auch im RAM),
> die man nicht wissen muss.

Das ist aber wichtig: eine Variable in RAM macht noch verbleibende RAM 
kleiner. Eine Variable in Stack ist "RAM-Gratis".

Dirk B. schrieb:
> static schränkt die Sichtbarkeit von Variablen und Funktionen auf die
> aktuelle C-Datei (übersetzungseinheit) ein.

Das passiert auch ohne extra "static" zu schreiben.

von mh (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> 3. Ein gutes Buch kaufen und dieses auswendig lernen?

"auswendig lernen" mit "verstehen" ersetzen und dann zu Punkt 1 
befördern.

von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> Das passiert auch ohne extra "static" zu schreiben.

Nein.

ES geht hier um Variablen, die in Headerdateien definiert sind.
Das wäre ausserhalb jeder Funktion.
Und die haben erstmal globalen Scope.

von Maxim B. (max182)


Lesenswert?

Dirk B. schrieb:
> ES geht hier um Variablen, die in Headerdateien definiert sind.

Dafür gibt es schon erwähnte Abhilfe mit #ifndef - #define

von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> Dirk B. schrieb:
>> ES geht hier um Variablen, die in Headerdateien definiert sind.
>
> Dafür gibt es schon erwähnte Abhilfe mit #ifndef - #define

Das löst - wie schon geschrieben - ein ganz anderes Problem.

von Bernd K. (prof7bit)


Lesenswert?

Maxim B. schrieb:
> Dirk B. schrieb:
>> ES geht hier um Variablen, die in Headerdateien definiert sind.
>
> Dafür gibt es schon erwähnte Abhilfe mit #ifndef - #define

Nein.

Das #ifdef kann nichts davon wissen ob die in einer anderen 
Übersetzungseinheit schonmal definiert war oder werden wird. 
Include-Guards werden verwendet um Mehrfachincludes innerhalb der 
selben Übersetzungseinheit zu vermeiden.

von Maxim B. (max182)


Lesenswert?

Bernd K. schrieb:
> Das #ifdef kann nichts davon wissen ob die in einer anderen
> Übersetzungseinheit schonmal definiert war oder werden wird.
> Include-Guards werden verwendet um Mehrfachincludes innerhalb der
> selben Übersetzungseinheit zu vermeiden.

#ifndef hilft, H-Datei nicht mehr zu includieren, wenn sie schon einmal 
includiert wurde. Somit werden alle Variablen, die in dieser Datei 
definiert sind, nur einmal definiert.
Reicht das nicht?

von Harry L. (mysth)


Lesenswert?

Maxim B. schrieb:
> Reicht das nicht?

Nein!

von Maxim B. (max182)


Lesenswert?

Harry L. schrieb:
> Maxim B. schrieb:
>> Reicht das nicht?
>
> Nein!

Dann kann ich nur vermuten, was dir noch stört, arbeitsfähige Code zu 
schreiben...

: Bearbeitet durch User
von Harry L. (mysth)


Lesenswert?

Maxim B. schrieb:
> Harry L. schrieb:
>> Maxim B. schrieb:
>>> Reicht das nicht?
>>
>> Nein!
>
> Dann kann ich nur vermuten, was dir noch stört, arbeitsfähige Code zu
> schreiben...

Hör auf zu vermuten, und lern besser C!
Sobald du die .h-Datei in einer weiteren .C-Datei includierst, knallts 
im Linker.

Der Include-Guard schützt nur einzelne Files vor Doppel-Definitionen.

von Maxim B. (max182)


Lesenswert?

Harry L. schrieb:
> Sobald du die .h-Datei in einer weiteren .C-Datei includierst, knallts
> im Linker.

Das mache ich oft. Die Umrahmung schutzt vor negativen Folgen.

von Harry L. (mysth)


Lesenswert?

Maxim B. schrieb:
> Harry L. schrieb:
>> Sobald du die .h-Datei in einer weiteren .C-Datei includierst, knallts
>> im Linker.
>
> Das mache ich oft. Die Umrahmung schutzt vor negativen Folgen.

Nur, so lange in dieser -h-Datei keine Variablen definiert sind.

von Dirk B. (dirkb2)


Lesenswert?

Harry L. schrieb:
> Nur, so lange in dieser -h-Datei keine Variablen definiert sind.

DAS (Variablendefinitionen im Header) macht man ja sowieso nicht.

von Maxim B. (max182)


Lesenswert?

Harry L. schrieb:
> Nur, so lange in dieser -h-Datei keine Variablen definiert sind.

Ich definire Variablen in H-Dateien oft. Natürlich setze ich dabei für 
Variablen keine Werte: das mache ich nur in einer C-Datei.

Dirk B. schrieb:
> DAS (Variablendefinitionen im Header) macht man ja sowieso nicht.

Hm... Wie sonst macht man eine Variable sichtbar für mehrere C-Dateien?

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> Hm... Wie sonst macht man eine Variable sichtbar für mehrere C-Dateien?

Man definiert sie in der C-Datei, die sie benötigt. Da kann man sie auch 
initialisieren.

Man deklariert sie (mit extern) in der entsprechenden Header-Datei.

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Dirk B. schrieb:
> Maxim B. schrieb:
>> Hm... Wie sonst macht man eine Variable sichtbar für mehrere C-Dateien?
>
> Man definiert sie in der C-Datei, die sie benötigt.
>
> Man deklariert sie (mit extern) in der entsprechenden Header-Datei.

Vielleicht habe ich "definiert" und "deklariert" verwechselt.
Wenn man in einer H-Datei einer Variablen gleich Wert gibt - klar wird 
Compiler schimpfen. Muß er auch.

von Dirk B. (dirkb2)


Lesenswert?

Eine Definition (bei C mit impliziter Deklaration) verbraucht 
Speicherplatz. Die Variable (oder Funktion wird angelegt)

Eine Deklaration gibt bekannt, dass dieser Name vorhanden ist. Reine 
Information.

Bei einer Funktionsdeklaration kann man auf das extern verzichten, da 
man dies am Fehlenden Funktionskörper erkennen kann.

Bei einer Variablendeklaration ist das extern nötig.

> Wenn man in einer H-Datei einer Variablen gleich Wert gibt - klar wird
> Compiler schimpfen.

Das wäre sogar eine Initialisierung (wenn es bei der Definition 
geschieht)

: Bearbeitet durch User
von leo (Gast)


Lesenswert?

Maxim B. schrieb:
> Vielleicht habe ich "definiert" und "deklariert" verwechselt.

Sicher hast du ...

und das hat auch nichts mit den Headerdefines (#ifdef XYZ_H) zu tun.

leo

von Maxim B. (max182)


Lesenswert?

Dirk B. schrieb:
> Eine Definition (bei C mit impliziter Deklaration) verbraucht
> Speicherplatz. Die Variable (oder Funktion wird angelegt)
>
> Eine Deklaration gibt bekannt, dass dieser Name vorhanden ist. Reine
> Information.
>
> Bei einer Funktionsdeklaration kann man auf das extern verzichten, da
> man dies am Fehlenden Funktionskörper erkennen kann.
>
> Bei einer Variablendeklaration ist das extern nötig.

Ich mache alles so einfach wie möglich. *.h - extern var ohne Wert. *.c 
- var mit Wert. static var - wenn Wert zwischen Aufrufen erhalten sein 
sollte, var aber von außen unsichtbar bleiben sollte.

von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> static var - wenn Wert zwischen Aufrufen erhalten sein
> sollte, var aber von außen unsichtbar bleiben sollte.

Das sind zwei verschiedenen Aufgaben von static, je nachdem ob die 
Variable innerhalb oder außerhalb einer Funktion definiert wurde.

von x^2 (Gast)


Lesenswert?

Bernd K. schrieb:
> Sobald die Funktion static ist wird es keine Konflikte mit
> Mehrfachdefinitionen mehr geben, dann ist es eigentlich auch völlig egal
> ob die betreffende Funktion inline ist oder nicht.

Korrekt, so hatte ich das auch gemeint.

Maxim B. schrieb:
> Das ist nur notwendig, wenn man unbedingt verhindern wird, daß die
> Funktion mit push und pop die Zeit verschwendet.
> Wenn man einfach "inline" schreibt, wird die Entscheidung dem Compiler
> überlassen, trotzdem kann inline-Funktion ruhig in H-Datei bleiben.

Genau, das hatte ich auch geschrieben.

Wie gesagt, inline wollte der TO. Würde sonst auch wenig Sinn ergeben, 
dass er die Definition der Funktion unbedingt in den Header verschieben 
will.

von Maxim B. (max182)


Lesenswert?

x^2 schrieb:

> Wie gesagt, inline wollte der TO. Würde sonst auch wenig Sinn ergeben,
> dass er die Definition der Funktion unbedingt in den Header verschieben
> will.

Ich habe zur Probe einige Funktionen mal so, mal so gemacht. Schreibt 
man einfach "inline", da kommt es von Aufrufzahl, ob das als inline oder 
als separate Funktion gemacht wird. Wenn mit __prefixen, dann wird immer 
inline gemacht. Manchmal spart das nicht nur Zeit, sondern auch 
Speicherplatz! Für ATMega bedeutet Funktionauruf 4 Bytes für call und 
noch 2-4-6-8... Bytes für Parameter. Oft wird inline-Funktion kürzer, da 
Daten benutzt, die schon in Register sind.

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.