Error LNK2005 - Mehrfachdeklaration Verständnisproblem

Skid

Erfahrenes Mitglied
Hallo,

also ich habe in einen meiner Programme den Fehler:

Code:
Error LNK2005: "int windowHeight" (?windowHeight@@3HA) ist bereits in main.obj definiert.
Error LNK2005: "int windowWidth" (?windowWidth@@3HA) ist bereits in main.obj definiert.
Fatal error LNK1169: Mindestens ein mehrfach definiertes Symbol gefunden.

Ich weiss warum der Fehler zustande kommt, verstehe jedoch nicht genau warum.
Ich habe hier die Lösung für das Problem gefunden, verstehe jedoch nicht, warum das Problem so zulösen ist.

Im Studium habe ich gelernt, dass die Fehler -wie die Fehlermeldungen selbst schon sagen- durch eine mehrfach definition einer Variable zustande kommen, was der Fall ist, wenn die Variable in einer Headerdatei definiert ist und zwei CPP-Dateien diese einbinden.

Bis hier hin komme ich mit, jedoch haben wir auch gelernt, wie man das Problem beseitigen kann und zwar wie folgt:

Angenommen wir haben eine Header "header.h" die wie folgt aussieht:
Code:
//----------------------------------------
// GLOBAL
//----------------------------------------
int windowH;
int windowW;

diese wird von main.cpp und neben.cpp eingebunden, dann kommt es zu den oben genannten Fehlern.
Zur Lösung habe ich gelernt, dass man mit define, ifndef und endif arbeiten sollte, um eine Mehrfacheinbindung der Headerdatei zu unterdrücken, also so:
Code:
#ifndef HEADER_H
#define HEADER_H

//----------------------------------------
// GLOBAL
//----------------------------------------
int windowH;
int windowW;

#endif HEADER_H

Jedoch wird bei mir der Fehler weiterhin angezeigt, d.h. immernoch Mehrfachdeklarationen.

In der obengenannten Seite wird mir eine Lösung vorgeschlagen die das Schlüsselwort extern nutzt was die Variable definiert, aber die eigentliche Deklaration findet in den CPP-Dateien statt die die Variable nutzen, dass bringt mir bei meinem Problem nichts, da beide CPP-Dateien die gleiche Variable nutzen sollen, deswegen definiere ich diese in der Header als globale Variable.

Habe ich hier gerade etwas verwechselt ?
Mir kommt das Problem gerade recht merkwürdig vor, da ich diesem Problem mit der obengenannten Problemlösung aus dem Weg gehen konnte.

Vielleicht sieht das bei DirectX-Programmierung anders aus, was dieses Problem angeht ?

Liebe Grüße,
Skid.
 
Hi.

Der Include-Guard funktioniert nur um mehrfach deklarierte Objekte in einem Modul (Übersetzungseinheit) zu vermeiden.

Gegen Mehrfachdefinition einer Variablen in 2 Modulen hilft diese Methode nicht. Deshalb sollte man auch keine Variablen in einer Headerdatei definieren.

Gruß
 
Mit extern funktioniert das schon.

Du musst dir das so vorstellen:
Nach dem kompilieren hast du ganz viele Dateien drin mit sogenannten Symbolen. So ist dann zb. deine Variable ein solches Symbol. Findet der Linker (das ist der, der dann aus all den Symbolen den Code generiert) dann dieses Symbol an mehreren Stellen definiert, so motzt er.

Indem du extern verwendest hilfst du einerseits dem Compiler als auch dem Linker. Der Compiler kennt dann die Variable und motzt nicht wegen einem unbekannten Bezeichner und gleichzeit sagst aber auch, dass dies nicht eine Definition einer Variablen sei, sondern sozusagen nur ein Platzhalter, die Definition ist ausserhalb (extern). Damit ist dann auch der Linker glücklich, da das keine Initalisierung ist sondern für ihn eigentlich gar nichts.

Wenn du dann natürlich in keiner Objektdatei das Symbol initialisierst (das heisst, wenn du es nirgends "korrekt" definierst), so wird dir der Linker den netten "Nicht aufgelöstes externes Symbol"-Fehler an den Kopf werfen.
 
Okay, dass ergibt natürlich Sinn!

Hi.

Der Include-Guard funktioniert nur um mehrfach deklarierte Objekte in einem Modul (Übersetzungseinheit) zu vermeiden.

Gegen Mehrfachdefinition einer Variablen in 2 Modulen hilft diese Methode nicht. Deshalb sollte man auch keine Variablen in einer Headerdatei definieren.

Gruß

Wie immer haben wir etwas gelernt, was man wohl lieber nicht lernen sollte ;)
Okay, ich werde mir das merken.

Was sollte man in einer Header-Datei maximal definieren ?
Gibt es da irgendwo einen Artikel, der definition und nichtdefinition in Header-Dateien beschreibt?

Mittlerweile bin ich mir nicht mehr so sicher, was man von den was man lernt, umsetzen sollte ;)

//Edit:
Mir ist gerade noch etwas eingefallen.
Wenn ich eine Variable habe, die ich in mehreren Modulen benötige, ist dann der bessere Weg die Variable an jedes Modul zu übergeben um damit zu arbeiten ?

Der Grund warum ich das frage ist folgender:
Wenn die Variable ja nun nicht mehr im Header steht, bzw. stehen sollte, also nicht mehr global ist, müsste ich ja einen anderen Weg wählen die Variable in einen anderen Modul bekannt zu machen (bspw. als Paramerter), oder ?
Das empfinde ich als weniger komfortabel :D

Mit extern funktioniert das schon.

Du musst dir das so vorstellen:
Nach dem kompilieren hast du ganz viele Dateien drin mit sogenannten Symbolen. So ist dann zb. deine Variable ein solches Symbol. Findet der Linker (das ist der, der dann aus all den Symbolen den Code generiert) dann dieses Symbol an mehreren Stellen definiert, so motzt er.

Indem du extern verwendest hilfst du einerseits dem Compiler als auch dem Linker. Der Compiler kennt dann die Variable und motzt nicht wegen einem unbekannten Bezeichner und gleichzeit sagst aber auch, dass dies nicht eine Definition einer Variablen sei, sondern sozusagen nur ein Platzhalter, die Definition ist ausserhalb (extern). Damit ist dann auch der Linker glücklich, da das keine Initalisierung ist sondern für ihn eigentlich gar nichts.

Wenn du dann natürlich in keiner Objektdatei das Symbol initialisierst (das heisst, wenn du es nirgends "korrekt" definierst), so wird dir der Linker den netten "Nicht aufgelöstes externes Symbol"-Fehler an den Kopf werfen.

Okay, also den Befehl mit den nicht aufgelösten extrenen Symbol hatte ich schon mehrfach, wusste aber nicht recht warum, gut das ich das jetzt weiss!

Für mich klingt das alles plausibel, nur wirft dass für mich eine weitere Frage auf:
Wenn ich bspw. eine Variable als Extern definiere muss ich in beiden CPP-Dateien die Datei deklarieren ?
Greifen die dann beide auf den gleichen Speicherbereich zu ?
D.h. Enthalten diese dann die gleichen Werte ?

Liebe Grüße,
Skid.
 
Zuletzt bearbeitet:
Es ist bisschen anders ;)

Aber grundsätzlich schlage ich dir vor, wenn du schon globale Variabeln in einer Headerdatei definieren möchtest, dann definiere sie als static, das sollte eigentlich wenn ich mich recht entsinne das bewirken, was du möchstest!

Also zb:
1.cpp
Code:
#include <iostream>
#include "2.h"

int main()
{
	Init();
	printf("%u\n", test);

	return 0;
}

2.h
Code:
static int test;

static void Init()
{
	test = 0;
}

3.cpp
Code:
#include "2.h"

void q()
{
	Init();
	test = 2;
}

Und wenn du es mit extern machen möchtest:
3.cpp
Code:
#include <stdio.h>

int ext;

void foo(void)
{
	ext = 2;
}

1.cpp
Code:
#include <stdio.h>

extern int ext;

extern void foo();

int main(void)
{
	foo();
	return 0;
}
 
Zuletzt bearbeitet:
Was sollte man in einer Header-Datei maximal definieren ?
Gibt es da irgendwo einen Artikel, der definition und nichtdefinition in Header-Dateien beschreibt?
Nicht das ich wüßte. Ist aber eigentlich "gesunder Menschenverstand". Prinzipiell definiert man überhaupt nichts in Headerdateien.
Mittlerweile bin ich mir nicht mehr so sicher, was man von den was man lernt, umsetzen sollte ;)

//Edit:
Mir ist gerade noch etwas eingefallen.
Wenn ich eine Variable habe, die ich in mehreren Modulen benötige, ist dann der bessere Weg die Variable an jedes Modul zu übergeben um damit zu arbeiten ?
Das kommt darauf an was praktischer bzw. wartbarer ist. Die errno Variable wird z.B. ebenfalls global für alle Module zur Verfügung gestellt die mit der Standard C Bibliothek arbeiten.
Der Grund warum ich das frage ist folgender:
Wenn die Variable ja nun nicht mehr im Header steht, bzw. stehen sollte, also nicht mehr global ist, müsste ich ja einen anderen Weg wählen die Variable in einen anderen Modul bekannt zu machen (bspw. als Paramerter), oder ?
Durch die extern Deklaration ist die Variable global verfügbar. Der Linker löst nachher die undefinierten Symbole auf und ersetzt die Referenz auf die extern deklarierte Variablen mit der richtigen Variablen(-adresse).
Für mich klingt das alles plausibel, nur wirft dass für mich eine weitere Frage auf:
Wenn ich bspw. eine Variable als Extern definiere muss ich in beiden CPP-Dateien die Datei deklarieren ?
Du hast offensichtlich noch arge Probleme mit der Terminologie. Mit extern wird eben keine Variable definiert, sondern nur deklariert. Und deine Frage bezog sich dann sicherlich auf die Definition in beiden CPP-Dateien, nicht auf die Deklaration.

Wenn du die Variable doppelt definierst bekommst du ja wieder einen Linkerfehler! Du darfst die Variable nur einmal definieren.
Greifen die dann beide auf den gleichen Speicherbereich zu ? D.h. Enthalten diese dann die gleichen Werte ?
Ja und ja. Wie sollte es anders funktionieren?! Bei Funktionen ist es natürlich das Gleiche. Die Konflikte kommen ja dadurch zustande, das eine Variable (bzw. Funktion) einfach durch einen eindeutigen Namen identifiziert wird.

Gruß
 
Zuletzt bearbeitet:
@ Cromon:

Ich danke dir für das ausführliche Beispiel, dass hilft mir schon wesentlich weiter :-)
Ich werde das einbisschen testen und ausprobieren wie es im Endeffekt für mich am besten erscheint :)

@ deepthroat:

Du hast offensichtlich noch arge Probleme mit der Terminologie. Mit extern wird eben keine Variable definiert, sondern nur deklariert. Und deine Frage bezog sich dann sicherlich auf die Definition in beiden CPP-Dateien, nicht auf die Deklaration.

Wenn du die Variable doppelt definierst bekommst du ja wieder einen Linkerfehler! Du darfst die Variable nur einmal definieren.

Da hast du vollkommen recht, dass liegt allerdings daran, dass beide Wörter mit d anfangen ;)
Ich bringe das ziemlich schnell durcheinander, d.h. ich nutze da wo deklariert wird, definiert als Wort, obwohl ich das andere meinte, dass ist dann wohl mein Fehler :)

Ich werde demnächst mehr darauf achten, was an welcher Stelle richtig ist, damit es nicht zu Missverständnissen kommt, danke dir nochmal für die genaue Erklärung der beiden Wörter!

Ja und ja. Wie sollte es anders funktionieren?! Bei Funktionen ist es natürlich das Gleiche. Die Konflikte kommen ja dadurch zustande, das eine Variable (bzw. Funktion) einfach durch einen eindeutigen Namen identifiziert wird.

Der Grund für meine Unsicherheit ist die Sache, dass Variablen mit gleichen Namen in unterschiedlichen Modulen durchaus unterschiedliche Werte und Speicherbereiche haben können.
Wie auch bei bspw. bei OOP, wenn man einen Konstruktor erstellt und eine string Variable mit den Namen "name" übergibt und in einer Spielerklasse meinetwegen eine string Variable mit den Namen "name" schon eingebunden hat, dann muss man ja auch mittels this-Zeiger sagen, welche Variable von beiden genau gemeint ist, bei der Zuweisung!

Das war eigentlich auch der Grund warum ich vorsichtshalber nochmal nachgefragt habe, bevor ich dann wieder auf einen fehler stoße mit den ich nicht so wirklich etwas anfangen kann :)

Also ich danke euch für die Hilfe und die Lösung des Problems, dass bringt mich um einiges weiter!

Liebe Grüße,
Skid.
 
Der Grund für meine Unsicherheit ist die Sache, dass Variablen mit gleichen Namen in unterschiedlichen Modulen durchaus unterschiedliche Werte und Speicherbereiche haben können.
Nein, das ist nicht möglich. Wenn in 2 Modulen eine Variable mit gleichem Namen definiert ist (wobei hierbei der gleiche Symbolname gemeint ist) und du diese Module zusammenlinkst, dann gibt es einen Konflikt bzw. Linkerfehler.
Wie auch bei bspw. bei OOP, wenn man einen Konstruktor erstellt und eine string Variable mit den Namen "name" übergibt und in einer Spielerklasse meinetwegen eine string Variable mit den Namen "name" schon eingebunden hat, dann muss man ja auch mittels this-Zeiger sagen, welche Variable von beiden genau gemeint ist, bei der Zuweisung!
Da sprichst du jetzt einen völlig anderen Aspekt an. Einmal gibt es natürlich den Gültigkeitsbereich (Skopus) und die Ausdehnung (Extend) einer Variablen. Außerdem ist es möglich das eine (lokale) Variable eine andere Variable verdeckt, auf welche man dann z.B. durch den Skopus-Auflösungsoperator wieder zugreifen kann.

In C++ ist es noch etwas komplizierter mit den Symbolnamen, da in C++ "name mangling" verwendet wird um z.B. das Überladen von Funktionen zu ermöglichen.

Gruß
 
Einen wunderschönen guten Morgen,

Der Grund für meine Unsicherheit ist die Sache, dass Variablen mit gleichen Namen in unterschiedlichen Modulen durchaus unterschiedliche Werte und Speicherbereiche haben können.

Wenn das der Fall ist, solltest du dir eventuell überlegen, ob du die Variable wirklich global definierst. Dann hat diese Variable in allen Modulen den gleichen Wert und den gleichen Speicherbereich zugeordnet. Eine Änderung wirkt sich auf alle Module aus. Das ist aber anscheinend nicht das, was du willst.

Gruss
Mizi
 
Zuletzt bearbeitet:
Zurück