Linker meldet "Multiple Definitions" - Warum ?

Gast #908122
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.
#908127
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.
Gast #908132
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
}
Persönliche Seite #908141
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.
#908144
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.
Gast #908162
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;
}
#908185
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.
#908209
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.
Gast #908232
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 ?
Gast #908252
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;
}
#908263
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.
Gast #2253128
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
#2253287
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren