Bonjour je commence tous juste de programmer en C++ j'ai déjà fais un peu de python l'année dernière.
J'ai commencé à lire le cours que propose le site sauf qu'au moment d'exécuter le programme hello world ben j'ai pas l'impression que ça marche pas. Je m'explique le terminal s'ouvre bien sauf que le message n'apparait pas. Alors qu'il y a le message "execution time..."
Je précise que je suis sous mac, que j'ai pris le logiciel Code::Blocks, que je ne suis pas vraiment à l'aise avec.
Oublie Code::Blocks. Sur Mac tu as XCode l'IDE fourni par Apple qui est parait il très bien, il vient avec clang qui est probablement le meilleur compilateur C++ du marché. Utiliser C::B sur Mac, je pense que c'est vraiment du masochisme. Et puis tant que tu y es, change de cours, celui-ci est obsolète et contient pas mal d'âneries.
Bonjour désoler de relancer le sujet mais je sais pas s'il fallait que j'en fasse un autre, alors je recommence avec celui ci, qui est un peu la suite d'ailleurs
Donc je suis passé sur Xcode, j'ai créer le premier programme main, où j'ai fais pas mal de test qui marchait machin. Sauf que j'ai voulu créer un nouveau fichier toujours en C++ et depuis que je l'ai fait je peux plus éxecuter de programme.. voici l'erreurs..
Voila j'ai supprimer et créer deux autre fichiers, après j'ai essayer de checker vite fait ce que je pouvais faire mais je comprends pas grand chose..
Si quelqu'un sait comment résoudre ce problème ce serait cool, surtout que je pense que une fois qu'il est fixé c'est good ou alors je l'aurai à nouveau et plus d'une fois. Merci d'avance
edit:quand je supprime le fichier créé, l'erreur disparait..
- Edité par SimonLecordier 21 septembre 2017 à 16:58:27
Les erreurs de type "duplicate symbol for ..." sont des erreurs qui nous viennent de l'éditeur de liens.
Il s'agit du dernier outil utilisé lors de la compilation, qui s'occupe (pour faire simple) de regrouper l'ensemble des fichiers objets (comprends: contenant le code binaire qui sera compris par le processeur) en un seul fichier exécutable.
Pour se faire, il va remplacer tous les appels aux différentes fonctions par l'adresse à laquelle ces fonctions se trouvent dans l'exécutable final.
Cet outil est assez capricieux car il ne sera pas content si:
il ne trouve pas le symbole correspondant à une fonction que tu as appelée;
il retrouve plusieurs fois le symbole correspondant à une même fonction
l'architecture (32 bits ou 64 bits) pour laquelle une fonction particulière a été compilée ne correspond pas à celle pour laquelle le reste des fonctions a été compilé
L'erreur dont tu nous parle est due à la deuxième situation. Il peut y avoir plusieurs raisons à cet état de fait:
Tu as peut-être utilisé la directive #include en fournissant un fichier portant l'extension .cpp: Il ne faut jamais le faire.
Tu as peut-être fourni l'implémentation d'une fonction directement dans un fichier d'en-tête: Tu aurais alors du la déclarer comme étant inline
Un des fichiers d'en-tête est inclus (parfois de manière indirecte) dans plusieurs unités de compilation sans être entouré des gardes anti inclusions multiples
Tu as peut-être déplacé l'implémentation d'une fonction dans une autre unité de compilation (dans un autre fichier .cpp), mais, pour une raison ou une autre, le fichier dans lequel cette fonction se trouvait à l'origine n'a pas été recompilé, et c'est "l'ancienne version" du fichier objet correspondant qui a été utilisée
Dans le troisième cas, tu dois veiller à ce que tous tes fichiers d'en-tête disposent bien de gardes anti inclusions multiples. Cela se fait généralement en veillant à ce que les fichiers d'en-tête ressemblent à quelque chose comme
#ifndef SYMBOLE_UNIQUE
#define SYMBOLE_UNIQUE
/* ton contenu à toi */
#endif
où tu veilleras à ce que SYMBOLE_UNIQUE soit un symbole qui n'existera que dans un seul et unique fichier d'en-tête.
Dans le dernier cas, il s'agira de "faire un peu le ménage":
Supprimes tous les fichiers objets (ils portent généralement l'extension .o ou .obj) générés
supprimes tous les fichiers que tu aurais "vidé" en placant leur contenu dans d'autres fichiers
vérifies qu'il n'y ait que les fichiers dont tu as besoin qui soient connus par ton projet
Une fois que ces trois étapes ont été faites, relances la compilation. Cela devrait fonctionner.
NOTA: Il se peut aussi que tu "combine" plusieurs de ces quatre situations. Tu devras donc apporter la correction adéquate à toutes
- Edité par koala01 22 septembre 2017 à 14:31:53
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Tu as peut-être déplacé l'implémentation d'une fonction dans une autre unité de compilation (dans un autre fichier .cpp), mais, pour une raison ou une autre, le fichier dans lequel cette fonction se trouvait à l'origine n'a pas été recompilé, et c'est "l'ancienne version" du fichier objet correspondant qui a été utilisée
- Edité par koala01 il y a environ 1 heure
L'autre grand classique, c'est copier l'implémentation dans l'autre fichier et oublier de la supprimer dans le fichier d'origine
Bonjour merci d'avoir pris le temps de me répondre, cependant j'ai beau lire et relire je ne comprends pas toutes vos explications.. j'ai donc supprimé quelques fichier que je jugeai inutile.
Comme vous avez du le comprendre je ne comprend pas grand chose aux IDE, avant je "travaillais" sur python et je n'en avais pas besoin car je ne m'en servais que pour les cours. Bref..
La seule chose que je peux confirmer c'est que je n'ai pas de #include avec un .cpp. Aussi j'ai surement pas été très clair dans mon message d'erreurs car je ne crois pas que ce soit la deuxième situation dont tu me parles. (encore une fois je ne sais pas.)
Je m'explique :
j'ai crée mon fichier main.cpp j'ai ajouté deux trois truc basique comme cin..
Maintenant j'ai voulu créer un autre fichier en C++ pour faire un petit truc sympas
un autre fichier se créer en .hpp et il est noté en #include nomfichier
j'initialise mon programme comme le fichier main avec #include <iostream> ; int main() { ; [programme ]; return0; }
jusqu'à la pas de soucis
je lance et bim l'erreur
Je vais vous mettre quelques screens ce sera surement plus explicite.
Voici le main fichier :
Voici le fichier que je crée avec son code :
Voici le fichier qui se crée avec l'autre :
et la le code erreur :
je vois bien qu'il me dit qu'il y a un symbole en double ou je sais pas trop quoi mais je vois pas où et comment régler cette erreur..
Désolé si je vous demande de vous répeter mais je ne vois pas vraiment ce que je dois faire pour régler ce problème.. Merci de bien vouloir éclairer ma lanterne
oui, mais si tu as une fonction nommée main dans main.cpp et une autre fonction qui est aussi nommée main dans BaseTest.cpp, faut pas t'étonner si l'éditeur de liens te dit qu'elle apparait deux fois
la fonction main() est une fonction particulière, car c'est celle qui est systématiquement appelée par le système d'exploitation lorsqu'il lance ton application. Mais ce n'est qu'une fonction comme une autre pour tout le reste
je te l'ai dit : l'éditeur de liens est un outil capricieux, car, pour savoir quelle fonction correspond à quel symbole, il utilise des règles bien précises, basées (entre autres)
sur le nom de la fonction
sur le type de retour de la fonction
sur les paramètres que la fonction s'attend à recevoir
Ces règle font que, s'il trouve une fonction dont la signature est
int main(){
/* on se fout de ce qu'elle fait */
}
dans deux fichiers objets (qui ont été générés par le compilateur, et qui contient le code binaire exécutable de cette fonction), par exemple, dans le fichier objet résultant de la compilation du fichier main.cpp et dans celui résultant de la compilation du fichier BaseTest.cpp (ce qui est ton cas pour l'instant), il ne saura pas quel symbole choisir pour permettre au système d'exploitation d'appeler la fonction main.
Retires l'une des deux implémentations de la fonction main (de préférence celle qui se trouve dans BaseTEst.cpp, vu que l'autre fera ce que tu veux), et recompile ton projet. L'éditeur de liens ne trouvera alors plus qu'un seul symbole correspondant à la fonction, et il sera content
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
SVP je suis désolé de ré-éditer mais personne n'a répondu à mon problème.
Voila pour ceux qui n'auraient pas suivi, j'utilise un IDE pour la première fois, Xcode, je programme en C++, j'ai quelques bases grace à python mais j'ai un soucis.
J'ai créer mon premier fichier : main.cpp
et un deuxième pour faire des tests : baseTest.cpp
J'écris quelques lignes de code sur le deuxième et quand je lance le programme, ça m'affiche le code du premier fichier dans le shell!
Est ce que c'est normal ou ça se règle? si oui je cherche une résolution à ce problème ou des indices merci!
SVP je suis désolé de ré-éditer mais personne n'a répondu à mon problème.
Voila pour ceux qui n'auraient pas suivi, j'utilise un IDE pour la première fois, Xcode, je programme en C++, j'ai quelques bases grace à python mais j'ai un soucis.
J'ai créer mon premier fichier : main.cpp
et un deuxième pour faire des tests : baseTest.cpp
J'écris quelques lignes de code sur le deuxième et quand je lance le programme, ça m'affiche le code du premier fichier dans le shell!
Est ce que c'est normal ou ça se règle? si oui je cherche une résolution à ce problème ou des indices merci!
c'est normal le linker rassemble tous tes fichiers dans le fichier principal ou il y a ta fonction main, du coup si tu as fais une fonction dans un fichier secondaire tu doit inclure le ficher au main et faire appel a ta fonction
- Edité par FlorianTobiasz 30 septembre 2017 à 11:42:31
De manière générale, tu dois te dire que, chaque fois que le compilateur travaille sur une unité de compilation (en gros, sur un fichier portant l'extension .cpp), il va lire le code exactement de la même manière que tu lirais un bon livre : de la première ligne à la dernière.
Mais, quand tu es arrivé à la page dix de ton livre, si tu sais ce qui s'est passé durant les neuf premières pages, tu ignore absolument tout de ce qu'il pourrait se passer à la page douze.
Le compilateur va travailler de la même manière : quand il travaille sur le contenu de la ligne 10, il connaît tout se qui se trouve dans les neuf premières, mais ignore tout de ce qui se trouve dans les lignes suivantes.
Le truc, c'est qu'il ne peut travailler qu'avec des éléments qu'il connaît. S'il croise l'appel d'une fonction, il doit savoir que la fonction existe; s'il croise l'utilisation d'une variable, il doit savoir que la variable existe et qu'elle représente une donnée de tel type; s'il croise l'utilisation d'un type de donnée particulier, il doit savoir que ce type de donnée existe ainsi le nombre de bytes nécessaire à sa représentation en mémoire.
En ce qui concerne les types de données, il y a quelques situations dans lesquelles il peut se contenter de savoir que le type existe. Mais dans la majeure partie des cas, il doit savoir "de quoi il est composé", et les fonctions que l'on peut appeler au départ de ce type de données.
Un fichier d'en-tête (typiquement, un fichier qui utilise l'extension .hpp ou .h) a pour but de permettre au compilateur de savoir que "quelque chose existe".
Quand il s'agit d'une fonction, cela correspond au nom que l'on utilise pour y faire appel, au type de la donnée qu'elle renvoie une fois qu'elle a fini de travailler et aux types des paramètres dont elle a besoin pour travailler.
Quand il s'agit d'un type de donnée, cela correspond à sa "catégorie" (énumération, structure, classe, alias de type), au nom qui permet de le désigner, et aux éléments qui le constituent.
Quand tu utilise la directive #include <nom_de_fichier>, tu indiques au preprocesseur (un outil qui est utilisé par le compilateur au tout début de son travail) qu'il doit copier l'intégralité du contenu du fichier dont tu as donné le nom à la place de la directive. De cette manière, lorsque le compilateur travaillera sur le code de l'unité de compilation, il trouvera le contenu du fichier en question, et il aura connaissance de... tout ce qui est déclaré dans le fichier d'en-tête.
Il faut savoir que le compilateur va travailler une unité de compilation à la fois, et qu'il va "oublier" ce qu'il a fait à la fin de chaque unité de compilation.
Quant à savoir ce qu'il a fait, ben, c'est très simple: il aura généré un code binaire compréhensible par le processeur pour toutes les fonctions dont il a croisé une implémentation dans un fichier que l'on appelle "fichier objet" (c'est un fichier dont l'extension est souvent .o ou .obj)
Une fois que le compilateur aura traité toutes les unités de compilation qu'il connaît, et qu'il aura donc généré tous les fichiers objets correspondant, un dernier outil va prendre le relais. On l'appelle l'éditeur de liens.
Le but de l'éditeur de liens est de regrouper tous les fichiers objets en un seul gros fichier exécutable, puis à faire faire pointer les différents appels aux fonctions qu'il croise vers l'adresse à laquelle la fonction en question se trouve dans le résultat final.
Seulement voilà: pour lui, l'implémentation de chaque fonction (ou, pour être précis: le code binaire exécutable correspondant à l'implémentation de chaque fonction) ne peut apparaître qu'une seule et unique fois dans le résultat final. Car il respecte des règles bien précises pour créer le symbole qui lui permet de retrouver les différentes fonctions.
Si bien que, si tu devais avoir le code binaire correspondant à une fonction portant un nom bien précis et acceptant des paramètres bien précis dans deux fichiers objet différents, le symbole qui identifie les deux fonctions serait strictement identique.
Et bien sur, cela poserait d'énormes problèmes, car cela signifie que le même symbole serait associé à deux adresses mémoire différentes : le premier, issus du premier fichier objet qu'il traite, se trouverait à l'adresse mémoire ABCDEF alors que le deuxième se trouvrait à l'adresse mémoire UVWXYZ. Et, du coup, l'éditeur de liens serait dans l'impossibilité de choisir vers quelle adresse mémoire il doit faire pointer l'appel à la fonction.
Le résultat d'une telle situation est alors sans appel : l'éditeur de liens réagira de la seule manière qui ait du sens, à savoir en t'indiquant qu'il a trouvé deux fois le même symbole et en s'arrêtant sur une erreur.
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Très bien merci à vous deux pour votre réponse! ;)
je crois que j'ai tous compris, je ne sais juste pas comment faire pour réutiliser une fonction dans un autre fichier
Tu inclus le fichier d'en-tête (le fichier .h / .hpp) dans le fichier d'implémentation (le fichier .cpp)
tu appelles la fonction de manière classique, en lui fournissant les paramètres dont elle a besoin et en récupérant le cas échéant la valeur qu'elle renvoie.
Un petit exemple:
fichier foo.hpp
// on appelle cela un garde anti inclusions multiples
#ifndef FOO_HPP_INCLUDED
#define FOO_HPP_INCLUDED
/* on doit connaitre la classe std::string... elle est fournie par le
* fichier d'en-tête <string>
*/
#include <string>
/* on déclare une fonction nommée foo prenant deux paramètres
* de type int et renvoyant une donnée de type std::string
*/
std::string foo(int a, int b);
// fin du garde anti inclusions multiples
#endif
fichier foo.cpp
/* indique au préprocesseur qu'il doit copier l'intégralité
* du contenu de foo.hpp ici
*/
#include <foo.hpp>
// l'implémentation de foo ... je fais simple ici
std::string foo(int a, int b){
std::string temp{"["};
temp.append(std::to_string(a))
.append(",")
.append(std::to_string(b))
.append("]");
return temp;
}
fichier main.cpp
/* notre fonction main va appeler foo. le compilateur doit
* savoir que cette fonction existe.
*
* on inclut donc le fichier foo.hpp
*/
#include <foo.hpp>
/* je veux utiliser std::cout... je dois aussi inclure <iostream>
*/
#include <iostream>
int main(){
/* auto permet de demander au compilateur de déterminer
* lui même le type de la donnée
*/
auto str1 = foo(5,6);
std::cout<<"str1 = "<<str1<<"\n";
std::cout<<"en direct = "<<foo(7,8)<<"\n";
return 0;
}
Pour que cela fonctionne, ton projet doit connaitre les trois fichiers indiqués (foo.hpp, foo.cpp et main.cpp). Tu peux faire un copier / coller du code de ceux-ci
Une fois que tu auras copié le code, sauvegarde les trois fichiers, compile et lance l'exécution du programme que tu auras obtenu
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
D'accord merci je crois que ça devrait le faire, je vais regarder tout ça ce soir ou demain je vous tiens au courant avec un petit edit !
Est ce que je suis obligé de déclarer ma fonction dans le fichier .hpp? Parce que à ce que je vois tu fais pareil dans le foo.cpp. Donc si je ne la déclare pas dans le .hpp et que je le fais dans le .cpp et que dans mon fichier main j'inclus le .cpp ?
Si petite flemme de répondre je ferais mes test quand j'aurais le temps
D'accord merci je crois que ça devrait le faire, je vais regarder tout ça ce soir ou demain je vous tiens au courant avec un petit edit !
Est ce que je suis obligé de déclarer ma fonction dans le fichier .hpp? Parce que à ce que je vois tu fais pareil dans le foo.cpp. Donc si je ne la déclare pas dans le .hpp et que je le fais dans le .cpp et que dans mon fichier main j'inclus le .cpp ?
Si petite flemme de répondre je ferais mes test quand j'aurais le temps
Attention: je déclare ma fonction dans foo.hpp, et j'implémente la fonction dans foo.cpp. Ce n'est pas pareil!
Si tu regardes biens dans foo.hpp, la parenthèse fermante est suivie d'un point-virgule ";". C'est donc une déclaration: En gros, ca dit "il existe une fonction foo, prenant deux entiers comme paramètres et renvoyant une donnée de type std::string".
Par contre, dans foo.cpp, la parenthèse et suivie par une accolade. C'est donc une implémentation (on parle aussi de une définition). En gros, ca dit "voici ce qu'il faut faire (les instructions à exécuter) lorsque l'on fait appel à la fonction qui s'appelle foo, qui prend deux entiers (a et b) en paramètre et qui renvoie une donnée de type std::string"
Et, pour répondre à ta question: oui, tu es obligé de déclarer ta fonction dans foo.hpp, parce que le compilateur devra savoir qu'elle existe quand il travaillera sur le fichier main.cpp. Autrement, il se plaindra à la ligne 15 et à la ligne 17 de main.cpp qu'il ne connait pas la fonction foo.
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
D'accord merci je vais voir ça tout à l'heure je te tiens au courant!
Du coup si j'ai bien compris le principe de l'accolade c'est comme pour un if ou while , la machine attend des instruction pour les exécuter ou qqch comme ça je me suis certainement très mal exprimé :')!
J'ai réussi à ajouter ma fonction à mon programme main!!! Merci pour votre aide
- Edité par SimonLecordier 3 octobre 2017 à 18:37:25
Debute en C++ MAC
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
technicien systèmes et réseaux