Erreur multiple definition
Fermé
rbarraud
-
Modifié le 17 janv. 2022 à 12:14
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 - 17 janv. 2022 à 12:30
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 - 17 janv. 2022 à 12:30
A voir également:
- Erreur multiple definition
- Erreur 0x80070643 - Accueil - Windows
- Erreur 0x80070643 Windows 10 : comment résoudre le problème de la mise à jour KB5001716 - Accueil - Windows
- Liste déroulante choix multiple excel - Guide
- Erreur 1001 outlook - Accueil - Bureautique
- Erreur 3005 france tv - Forum TV & Vidéo
2 réponses
[Dal]
Messages postés
6198
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
13 décembre 2024
1 096
Modifié le 12 janv. 2022 à 15:12
Modifié le 12 janv. 2022 à 15:12
Salut rbarraud,
Tu ne montres pas tes fichiers .c correspondants aux deux .h que tu postes.
Cependant, on y voit les problèmes décrits ci-après qui sont certainement à l'origine de tes erreurs.
Dans file_messages.h tu définis des variables globales :
Les .h ne doivent se contenter que de définir des types, des prototypes de fonctions, des #define, mais pas des variables (ni l'implémentation des fonctions, etc., l'implémentation étant réservée aux .c).
Autrement, à chaque usage de l'entête, les variables vont être redéfinies.
Pour éviter ce problème, si tu tiens vraiment à disposer de variables globales auxquelles tu puisses te référer pour les utiliser dans différents fichiers .c en faisant un
Le mot clef extern te permet de déclarer l'existence de cette variable, qui sera connue donc de l'utilisateur de l'entête, sans la définir, la définition devant se trouver dans l'un des autres fichiers compilés (en général le fichier .c qui correspond au .h), cette vérification étant faite au stade de la liaison et non de la compilation.
Tu as le même type de problème dans l'autre entête, qui définit
Je te recommande la lecture de ce document très clair et complet sur le bon usage des .h en C :
http://websites.umich.edu/~eecs381/handouts/CHeaderFileGuidelines.pdf
Dal
Tu ne montres pas tes fichiers .c correspondants aux deux .h que tu postes.
Cependant, on y voit les problèmes décrits ci-après qui sont certainement à l'origine de tes erreurs.
Dans file_messages.h tu définis des variables globales :
int tailleDemandeQuai = sizeof(demande_quai)-sizeof(long); int tailleReponseQuai = sizeof(reponse_quai)-sizeof(long); int tailleDemandePortique = sizeof(demande_portique)-sizeof(long); int tailleReponsePortique = sizeof(demande_portique)-sizeof(long);
Les .h ne doivent se contenter que de définir des types, des prototypes de fonctions, des #define, mais pas des variables (ni l'implémentation des fonctions, etc., l'implémentation étant réservée aux .c).
Autrement, à chaque usage de l'entête, les variables vont être redéfinies.
Pour éviter ce problème, si tu tiens vraiment à disposer de variables globales auxquelles tu puisses te référer pour les utiliser dans différents fichiers .c en faisant un
#include file_messages.h"tu dois :
- déclarer ces variables avec le mot clef
extern
dans le fichier .h - faire ta déclaration dans le fichier .c correspondant
Le mot clef extern te permet de déclarer l'existence de cette variable, qui sera connue donc de l'utilisateur de l'entête, sans la définir, la définition devant se trouver dans l'un des autres fichiers compilés (en général le fichier .c qui correspond au .h), cette vérification étant faite au stade de la liaison et non de la compilation.
Tu as le même type de problème dans l'autre entête, qui définit
int vehicule(int msgid_quai,TYPE type);.
Je te recommande la lecture de ce document très clair et complet sur le bon usage des .h en C :
http://websites.umich.edu/~eecs381/handouts/CHeaderFileGuidelines.pdf
Dal
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
Modifié le 17 janv. 2022 à 12:36
Modifié le 17 janv. 2022 à 12:36
Bonjour,
La définition multiple est dues au variables globale. Le header dans lequel elles sont définies est importé dans plusieurs fichiers
Comme le dit [Dal] c'est l'une des (nombreuses) raisons pour lesquelles il faut éviter les variables globales. Outre les problèmes de linkage, elles posent aussi des problèmes de lisibilité (on ne sait pas qui les modifie et qui en a besoin) et d'implémentation (notamment quand on commence à paralléliser le code). Et comme le dit [Dal], il suffit de passer les variables adéquates en paramètres aux fonctions (à voir si cela fait sens ou pas de les déclarer au travers d'une structure comme il le propose, mais selon moi tu ne devrais le faire que si cela a un sens "sémantique" de les rassembler, sinon, autant les passer chacune indépendamment en paramètre).
Par contre il y a deux manières à mon avis plus élégantes de contourner ton problème.
Méthode 1 : avec des
Pour rappel, les
Note que comme il s'agit d'une substitution, il n'y a pas de
Méthode 2 : par "héritage"
Le fait que tu aies besoin de ces tailles laisse entendre que l'ajout du
... ainsi :
Ensuite, il te suffit d'utiliser soit
Cependant, cela altère la manière dont tu accèdes aux portiques quand u manipules un
Note qu'avec cette seconde méthode, tu peux caster une instance de
Bonne chance
La définition multiple est dues au variables globale. Le header dans lequel elles sont définies est importé dans plusieurs fichiers
.cet donc présente dans chaque
.oengendré. Au moment de linker (c'est à dire de rassembler tous les
.opour former le binaire finale) ces variables globales existent donc en autant d'exemplaire.
Comme le dit [Dal] c'est l'une des (nombreuses) raisons pour lesquelles il faut éviter les variables globales. Outre les problèmes de linkage, elles posent aussi des problèmes de lisibilité (on ne sait pas qui les modifie et qui en a besoin) et d'implémentation (notamment quand on commence à paralléliser le code). Et comme le dit [Dal], il suffit de passer les variables adéquates en paramètres aux fonctions (à voir si cela fait sens ou pas de les déclarer au travers d'une structure comme il le propose, mais selon moi tu ne devrais le faire que si cela a un sens "sémantique" de les rassembler, sinon, autant les passer chacune indépendamment en paramètre).
Par contre il y a deux manières à mon avis plus élégantes de contourner ton problème.
Méthode 1 : avec des
#define
Pour rappel, les
#definesont résolus par le précompilateur (l'identifiant est substitué par sa valeur par le précompilateur). Il n'y aucune "intelligence" dans le précompilateur, c'est le compilateur qui une fois la substitution faite verra si le code engendré a un sens.
#define tailleDemandeQuai sizeof(demande_quai) - sizeof(long) #define tailleReponseQuai sizeof(reponse_quai) - sizeof(long) #define tailleDemandePortique sizeof(demande_portique) - sizeof(long) #define tailleReponsePortique sizeof(demande_portique) - sizeof(long)
Note que comme il s'agit d'une substitution, il n'y a pas de
=entre l'identifiant de la macro et sa valeur. Note également qu'il ne faut surtout pas mettre de
;en fin de ligne, sans quoi le pré-compilateur injectera des
;à des endroits inopportuns.
Méthode 2 : par "héritage"
Le fait que tu aies besoin de ces tailles laisse entendre que l'ajout du
longdans chacune de tes structures définit un autre objet. Tu pourrais donc réécrire :
typedef struct { long type; int portique1[2]; int portique2[2]; } reponse_quai;
... ainsi :
typedef struct { int portique1[2]; int portique2[2]; } reponse_quai; typedef struct { struct reponse_quai reponse_quai; long type; } reponse_quai_ext;
Ensuite, il te suffit d'utiliser soit
sizeof(reponse_quai), soit
sizeof(reponse_quai_ext).
Cependant, cela altère la manière dont tu accèdes aux portiques quand u manipules un
reponse_quai_ext rqe: il faudra par exemple écrire ;
rqe.reponse_quai.portique1[0]. Bref, la méthode 2 est plus contraignante pour adapter ton code que la méthode 1.
Note qu'avec cette seconde méthode, tu peux caster une instance de
reponse_quai_exten
reponse_quai, sous réserve que l'attribut
reponse_quaisoit le premier attribut de
struct reponse_quai_ext. C'est comme ça qu'on fait de "l'héritage" (au sens C++) en C (qui ne propose pas nativement de notion d'héritage).
Bonne chance
Modifié le 12 janv. 2022 à 15:25
Tu peux souvent faire mieux en encapsulant les valeurs dont tu as besoin dans un "objet" de type struct, que tu passes en argument des fonctions qui en ont besoin sous la forme d'un pointeur.
Cela permet de rendre ton code plus générique et réutilisable, et ta conception plus lisible.