www.mikrocontroller.net

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


Autor: Herbert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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:
void g_f(void)
{
a=g[1];
b=g[2];
}
ist eine Definition während
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.

Autor: Herbert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
}

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Herbert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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;
}

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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].

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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#Global...

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Herbert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ?

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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;
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Herbert (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
THX :)

Autor: EEPROM (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
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#Global...

Autor: EEPROM (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: EEPROM (Gast)
Datum:

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

Autor: EEPROM (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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;

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.