Forum: Mikrocontroller und Digitale Elektronik Linker meldet "Multiple Definitions" - Warum ?


von Herbert (Gast)


Lesenswert?

Hallo,

ich habe eine C-Datei: init.c
Dort stehen Funktionen drin, die ich in main() brauche.
Diese Funktionen sind in der init.h deklariert.
In der init.h stehen aber auch Variablen die initialisiert sind und in 
den Funktionen benötigt werden.

In der Haupt-C-Datei binde ich die init.h, wie folgt ein #include 
"init.h".

In Main() rufe ich die Funktionen auf.

Der Linker meldet mir aber nach dem Compilieren "Multiple definition" !
Also jede Variable soll mehrfach definert worden sein, aber wo ?

Klarquelltext

init.h
------

int g[2]={0,1};

init.c
------

#include "init.h"

void g_f(void)
{
a=g[1];
b=g[2];
}

main.c
------
#include "init.h"

main()
{
g_f();
}

Da meckert der Linker, dass g[] mehrfach definiert ist und zuerst in der 
main() Datei.

Wie kann man das umgehen ?

Welches Schlüsselwort muss ich benutzen, um g[] in main() ebenfalls 
verändern zu können. Diese Variable soll also global für alle C-Dateien 
der Projektes sein !

Ich habe bisher immer alles in ein File programmiert.
Nun soll das anders werden.

Vielleicht kann mir jemand helfen.

von Johannes M. (johnny-m)


Lesenswert?

In eine .h-Datei gehören nur Deklarationen und keine Definitionen ! 
Die Definition von g_f() gehört in genau eine .c-Datei, in die 
.h-Datei kommt nur der Funktionsprototyp (also besagte Deklaration ).

Damit es klar wird, was ich meine:
1
void g_f(void)
2
{
3
a=g[1];
4
b=g[2];
5
}
ist eine Definition während
1
void g_f(void);
die entsprechende Deklaration ist. Wenn Du in der Headerdatei die 
Definition unterbringst und die Datei mehr als einmal einbindest, dann 
ist die Funktion mit jedem entsprechenden #include definiert, was 
unzulässig ist. Generell muss eine Funktion/Variable in einem Projekt 
genau einmal definiert sein, während sie beliebig oft deklariert 
werden kann. Bei Variablen benutzt man das Schlüsselwort extern um 
darauf hinzuweisen, dass es sich um eine Deklaration handelt (also 
dass dem Compiler lediglich gesagt werden soll, dass irgendwo eine 
Variable mit den spezifizierten Eigenschaften existiert).

Bei Funktionen ist das extern überflüssig, da aus der Syntax 
ersichtlich ist, ob es sich um eine Definition oder eine Deklaration 
(Prototyp) handelt.

von Herbert (Gast)


Lesenswert?

Ja das meine ich ja auch.

Also nochmal:

init.h
------

int g[2]={0,1};         //Variable - das sollte gehen oder ?
void g_f(void);         //Prototyp

init.c
------

#include "init.h"

void g_f(void)         //Funktionsdefinition
{
a=g[1];
b=g[2];
}

main.c
------
#include "init.h"

main()
{
g_f();                //Aufruf
}

von Stefan B. (stefan) Benutzerseite


Lesenswert?

int g[2]={0,1};         //Variable - das sollte gehen oder ?

ist eine Definition

Die Deklaration sähe so aus

extern int g[];       //Variable - das sollte gehen oder ?

Wenn du die Definition in die init.h steckst (wie derzeit) und die 
init.h in int.c und main.c einbindest, dann legst du das Feld g doppelt 
an.

du willst aber das Feld g nur einmal anlegen z.B. in init.c, also ist 
dort die Definition einzufügen. Und in main.c willst du den Namen und 
den Typ bekanntmachen, also ist dort die Deklaration einzufügen z.B. 
über init.h

Es stört in init.c nicht, wenn dort die Definition und die Deklaration 
sichtbar sind, d.h. über das #include von init.h das extern... 
auftaucht.

von Johannes M. (johnny-m)


Lesenswert?

Herbert wrote:
> Ja das meine ich ja auch.
>
> Also nochmal:
>
> init.h
> ------
>
> int g[2]={0,1};         //Variable - das sollte gehen oder ?
Nö, eben nicht. Da muss ein extern davor und der Initialisierungswert 
weg. Und dann mit Initialisierung ohne extern (also die Definition ) 
in die entsprechende .c-Datei.

von Herbert (Gast)


Lesenswert?

Hallo,

danke für eure Antworten.

Wie mache ich es, dass man in init.c eine Variable definiert und in main 
verändert ?

Brauche ich dafür nur die init.h einbinden ?

Also:

init.h
------

extern int g[];

init.c
------
#include "init.h"
int g[2]={0,1}

main.c
------
#include "init.h"
main()
{
g[2]=0;
}

von Johannes M. (johnny-m)


Lesenswert?

Jo, genau so ist es. Ich vermute, dass Dir der Unterschied zwischen 
"Definition" und "Deklaration" noch nicht ganz klar ist.

Definition : Eine Variable wird angelegt, d.h. es wird angemessener 
Speicherplatz für sie reserviert. Das muss für jede Variable / Funktion 
genau einmal gemacht werden.

Deklaration : Es wird dem Compiler mitgeteilt, dass es irgendwo 
außerhalb seines momentanen Blickfeldes eine Definition der betreffenden 
Variable gibt. Es ist aber nur eine formelle Mitteilung an den Compiler 
"tu so, als wäre die Variable da und arbeite mit ihr". Das kann man 
beliebig oft machen.

Der Compiler bearbeitet jede Quelldatei (.c-Datei) völlig separat, also 
unabhängig von anderen Quelldateien. Das bedeutet aber auch, dass er 
Variablen und Funktionen, in anderen Source-Files definiert wurden, 
nicht sehen kann. Er darf die Variable dann aber nicht neu anlegen, denn 
es soll ja die Variable aus der anderen Datei verwendet werden. Deshalb 
braucht er dann einfach nur einen Hinweis, dass die Variable existiert 
(wenn sie das natürlich nicht tut, dann meckert der Linker eben aus dem 
Grunde...).

Erst der Linker fügt die einzelnen Objekt-Files, die der Compiler 
erzeugt, zu einer Gesamtheit zusammen. Und deshalb ist er es auch, der 
(berechtigt) meckert, wenn für eine Variable mehrfach Speicherplatz 
angefordert wurde.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Allerdings solltest Du beim Arrayzugriff auch die zulässigen Grenzen 
berücksichtigen.

Wird das Array so definiert:

  int g[2]={0,1}

dann geht folgender Schreibzugriff in die Hose:

  g[2]=0;


Das Array hat nur zwei und nicht drei Elemente, und in C ist das erste 
Element [0] und nicht [1].

von Karl H. (kbuchegg)


Lesenswert?

Johannes M. wrote:
> Jo, genau so ist es. Ich vermute, dass Dir der Unterschied zwischen
> "Definition" und "Deklaration" noch nicht ganz klar ist.

Ich habe auf der FAQ Seite da mal einen Abschnitt dazu
eingebaut, damit man sich über solche Sachen nicht mehr den
Mund fusselig redet :-)

http://www.mikrocontroller.net/articles/FAQ#Globale_Variablen_.C3.BCber_mehrere_Dateien

von Stefan E. (sternst)


Lesenswert?

Karl heinz Buchegger wrote:

> Ich habe auf der FAQ Seite da mal einen Abschnitt dazu
> eingebaut, damit man sich über solche Sachen nicht mehr den
> Mund fusselig redet :-)

Danke!
Mir ging es auch gerade durch den Kopf, dass dieses Thema in letzter 
Zeit verdammt häufig aufgetaucht ist.

von Herbert (Gast)


Lesenswert?

Hallo,

ich denke, dass es mir schon klar ist.

Wenn ich eine Variable in init.c DEFINIERE, dann muss ich in main() 
diese bekanntmachen, also mit EXTERN. Dann kann ich Sie in ganz normal 
benutzen.
Sie wird nicht nochmal angelegt.

Zu meinem Beispiel:

init.h
------

EXTERN int g[];

init.c
------
#include "init.h"  //Optional
int g[2]={0,1}

main.c
------
#include "init.h"

EXTERN int g[];     //Keine neue Definition, sondern DEKLARATION
main()
{
g[1]=0;
}

Ist das jetzt korrekt ?

von Klaus (Gast)


Lesenswert?

erstens: Antwort auf deinen vorletzen Post:

> Jo, genau so ist es.

Warum änderst du es jetzt noch wieder?

zweitens: Überleg mal, was der preprozessor mit dem #include aus deiner 
main.c macht:

main.c
------

EXTERN int g[];

EXTERN int g[];     //Keine neue Definition, sondern DEKLARATION
main()
{
g[1]=0;
}

von Karl H. (kbuchegg)


Lesenswert?

Herbert wrote:

> Ist das jetzt korrekt ?

Nein.
In der Programmiersprache C ist korrekte Groß-/Kleinschreibung
wichtig.
Das Schlüsselwort heißt 'extern', nicht 'EXTERN'

Abgesehen davon ist es ok, auch wenn es die 2.-te Deklaration
in main.c nicht gebraucht hätte. Durch das #include hast du
ja sowieso eine Deklaration verfügbar.

von Herbert (Gast)


Lesenswert?

THX :)

von EEPROM (Gast)


Lesenswert?

Also ich habe soeben ebenfalls ein Problem aus diesem Bereich erlebt:

Und zwar habe ich eine Header Datei Namens eeprom.h mit folgendem Inhalt 
erzeugt:

>eeprom.h:
>#include <avr/io.h>
>#include <avr/eeprom.h>

>uint8_t EE_SPEICHER EEMEM;



Sobald ich diese in zwei anderen c-Files eingebunden habe, hat der 
Compiler gemeckert... und das hat mich verwundert, weil ich ja nach der 
Section-Bekanntgabe garkeinen Wert an die EEPROM-Speicherstelle 
überschrieben habe...


Nun habe ich eeprom.c noch dazu angelegt und folgende Änderungen 
vorgenommen:

>>eeprom.h:
>>#include <avr/io.h>
>>#include <avr/eeprom.h>
>>
>>uint8_t EE_SPEICHER;  //Deklaration


>>>eeprom.c:
>>>#include "eeprom.h"
>>>
>>>uint8_t EE_SPEICHER EEMEM;  //Definition


Dieses "Konstrukt" kann ich problemlos in mehreren artfremden C-Files 
mit einbinden..., aber eine Frage habe ich nun dazu:


Frage:
Wird die Variable EE_SPEICHER bei Verwendung der Funktionen

>X = eeprom_read_byte(&EE_SPEICHER);

bzw.
>eeprom_write_byte(&EE_SPEICHER, 123);

nun auch tatsächlich als einmalig und global angesehen, oder habe ich 
hier immernoch etwas nicht richtig verstanden?

mfg

von Karl H. (kbuchegg)


Lesenswert?

EEPROM schrieb:

> Nun habe ich eeprom.c noch dazu angelegt und folgende Änderungen
> vorgenommen:
>
>>>eeprom.h:
>>>#include <avr/io.h>
>>>#include <avr/eeprom.h>
>>>
>>>uint8_t EE_SPEICHER;  //Deklaration

Das ist nach wie vor eine Defintion und keine Deklaration.
1
extern uint8_t EE_SPEICHER EEMEM;
jetzt ist es eine Deklaration. Das 'extern' ist der entscheidende Punkt, 
nicht das EEMEM

http://www.mikrocontroller.net/articles/FAQ#Globale_Variablen_.C3.BCber_mehrere_Dateien

von EEPROM (Gast)


Lesenswert?

Hallo Karl Heinz,


...das EEMEM ist demnach garkeine Definition und die Code-Zeile:

>> extern uint8_t EE_SPEICHER EEMEM;

genügt als Header in allen C-Files zu inkludieren?

mfg

von EEPROM (Gast)


Lesenswert?

...was aber passiert bei meiner Lösung, die nämlich (zumindest derzeit) 
tut was sie tuen soll?

von EEPROM (Gast)


Lesenswert?

...und vor allem,

was schreieb ich denn als Definition in die eeprom.c, wenn die Codezeile

>> extern uint8_t EE_SPEICHER EEMEM;

eine Deklaration darstellen sollte...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

EEPROM schrieb:
> was schreieb ich denn als Definition in die eeprom.c, wenn die Codezeile
>
>>> extern uint8_t EE_SPEICHER EEMEM;
>
> eine Deklaration darstellen sollte...

Wie wäre es mit einer Definition?

   uint8_t EE_SPEICHER EEMEM;

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.