web-dev-qa-db-fra.com

Erreur C: référence indéfinie à la fonction, mais elle IS définie

Juste un programme simple, mais je continue à avoir cette erreur du compilateur. J'utilise MinGW pour le compilateur.

Voici le fichier d'en-tête, point.h:

//type for a Cartesian point
typedef struct {
  double x;
  double y;
} Point;

Point create(double x, double y);
Point midpoint(Point p, Point q);

Et voici point.c:

//This is the implementation of the point type
#include "point.h"

int main() {
  return 0;
}
Point create(double x, double y) {
  Point p;
  p.x = x;
  p.y = y;
  return p;
}

Point midpoint(Point p, Point q) {
  Point mid;
  mid.x = (p.x + q.x) / 2;
  mid.y = (p.y + q.y) / 2;
  return mid;
}

Et voici où le problème du compilateur entre en jeu. Je continue à obtenir:

testpoint.c: référence non définie à 'créer (double x, double y)'

Bien qu'il soit défini au point.c.

Ceci est un fichier séparé appelé testpoint.c:

#include "point.h"
#include <assert.h>
#include <stdio.h>
int main() {
  double x = 1;
  double y = 1;
  Point p = create(x, y);

  assert(p.x == 1);
  return 0;
}

Je ne sais pas ce que pourrait être le problème.

52
upswimsdn

Comment faites-vous la compilation et la liaison? Vous devrez spécifier les deux fichiers, quelque chose comme:

gcc testpoint.c point.c

... afin qu'il sache relier les fonctions des deux. Avec le code tel qu’il est écrit actuellement, vous rencontrerez alors le problème opposé: plusieurs définitions de main. Vous aurez besoin/envie d'éliminer un (sans doute celui en point.c).

Dans un programme plus important, vous compilez et liez séparément pour éviter de recompiler tout ce qui n'a pas changé. Vous spécifiez normalement ce qui doit être fait via un fichier makefile et utilisez make pour effectuer le travail. Dans ce cas, vous auriez quelque chose comme ceci:

OBJS=testpoint.o point.o

testpoint.exe: $(OBJS)
    gcc $(OJBS)

Le premier est juste une macro pour les noms des fichiers objets. Vous le développez avec $(OBJS). La seconde est une règle pour dire à make 1) que l’exécutable dépend des fichiers objets, et 2) lui indiquer comment créer l’exécutable quand/s’il est périmé par rapport à un fichier objet.

La plupart des versions de make (y compris celle de MinGW, j'en suis quasiment sûr) ont une "règle implicite" intégrée leur indiquant comment créer un fichier objet à partir d'un fichier source C. Cela ressemble normalement à ceci:

.c.o:
    $(CC) -c $(CFLAGS) $<

Cela suppose que le nom du compilateur C se trouve dans une macro nommée CC (définie implicitement comme CC=gcc) Et vous permet de spécifier les indicateurs qui vous intéressent dans une macro nommée CFLAGS (par exemple, CFLAGS=-O3 Pour activer l'optimisation) et $< Est une macro spéciale qui se développe au nom du fichier source.

Vous le stockez généralement dans un fichier nommé Makefile et pour créer votre programme, il vous suffit de taper make sur la ligne de commande. Il recherche implicitement un fichier nommé Makefile et exécute les règles qu'il contient.

Le bon point de ceci est que make regarde automatiquement les horodatages sur les fichiers, de sorte qu'il ne recompilera que les fichiers qui ont changé depuis la dernière fois que vous les avez compilés (c'est-à-dire, les fichiers où le ".c "Le fichier a un horodatage plus récent que le fichier" .o "correspondant).

Notez également que 1) il existe de nombreuses variantes dans la manière d’utiliser les grands projets et 2) de nombreuses alternatives. Je n'ai atteint que le strict minimum de points élevés ici.

83
Jerry Coffin

J'ai eu ce problème récemment. Dans mon cas, mon IDE) était configuré pour choisir le compilateur (C ou C++) à utiliser sur chaque fichier en fonction de son extension et j'essayais d'appeler une fonction C (c'est-à-dire .c fichier) à partir du code C++.

Le fichier .h de la fonction C n'était pas encapsulé dans cette sorte de garde:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

J'aurais pu ajouter cela, mais je ne voulais pas le modifier, alors je l'ai simplement inclus dans mon fichier C++ comme suit:

extern "C" {
#include "legacy_C_header.h"
}

(Pointe du chapeau à ncaAlby pour son explication claire de la effet de extern "C" .)

9
cp.engr

Je pense que le problème est que lorsque vous essayez de compiler testpoint.c, il inclut point.h mais il ne connaît pas point.c. Puisque point.c a la définition de create, ne pas avoir point.c entraînera l'échec de la compilation.

Je ne connais pas bien MinGW, mais vous devez demander au compilateur de rechercher point.c. Par exemple avec gcc vous pouvez faire ceci:

gcc point.c testpoint.c

Bien sûr, comme autres l'ont souligné, vous devez également supprimer l'une de vos fonctions main, car vous ne pouvez en avoir qu'une.

6
Cam

Ajoutez le mot clé "extern" aux définitions de fonction dans point.h

1

Un problème similaire m'a récemment dérouté. Ensuite, nous avons remarqué que la déclaration en aval de la fonction était 'statique', donc elle avait un lien interne et était invisible. Malgré le fait que la définition n'avait aucun mot clé.

Assurez-vous de vérifier vos déclarations les gars!

0
Calmarius