C-fout: ongedefinieerde verwijzing naar functie, maar IS gedefinieerd

Gewoon een eenvoudig programma, maar ik krijg steeds deze compilerfout. Ik gebruik MinGW voor de compiler.

Hier is het header-bestand, 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);

En hier is 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;
}

En hier komt het probleem met de compiler om de hoek kijken. Ik krijg steeds:

testpoint.c: ongedefinieerde verwijzing naar ‘create(double x, double y)’

Terwijl het is gedefinieerd in punt.c.

Dit is een apart bestand met de naam 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;
}

Ik weet niet wat het probleem zou kunnen zijn.


Antwoord 1, autoriteit 100%

Hoe gaat het met het compileren en koppelen? Je moet beide bestanden specificeren, zoiets als:

gcc testpoint.c point.c

…zodat het de functies van beide weet te koppelen. Met de code zoals deze nu is geschreven, loop je echter tegen het tegenovergestelde probleem aan: meerdere definities van main. Je zult er een moeten/willen elimineren (ongetwijfeld die in punt.c).

In een groter programma compileer en koppel je meestal afzonderlijk om te voorkomen dat je iets opnieuw compileert dat niet is veranderd. Normaal gesproken specificeert u wat er gedaan moet worden via een makefile, en gebruikt u makeom het werk te doen. In dit geval zou je zoiets hebben als dit:

OBJS=testpoint.o point.o
testpoint.exe: $(OBJS)
    gcc $(OJBS)

De eerste is slechts een macro voor de namen van de objectbestanden. Je krijgt het uitgebreid met $(OBJS). De tweede is een regel om make te vertellen 1) dat het uitvoerbare bestand afhangt van de objectbestanden, en 2) om het te vertellen hoe het uitvoerbare bestand moet worden gemaakt wanneer/als het verouderd is in vergelijking met een objectbestand.

De meeste versies van make (inclusief die in MinGW waar ik vrij zeker van ben) hebben een ingebouwde “impliciete regel” om hen te vertellen hoe ze een objectbestand moeten maken van een C-bronbestand. Normaal ziet het er ongeveer zo uit:

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

Hierbij wordt ervan uitgegaan dat de naam van de C-compiler zich in een macro met de naam CC bevindt (impliciet gedefinieerd als CC=gcc) en kunt u alle vlaggen opgeven die u belangrijk vindt in een macro met de naam CFLAGS(bijv. CFLAGS=-O3om optimalisatie in te schakelen) en $<is een speciale macro die uitbreidt naar de naam van het bronbestand.

U slaat dit meestal op in een bestand met de naam Makefile, en om uw programma te bouwen, typt u gewoon makeop de opdrachtregel. Het zoekt impliciet naar een bestand met de naam Makefileen voert alle regels uit die het bevat.

Het goede punt hiervan is dat makeautomatisch naar de tijdstempels op de bestanden kijkt, dus het zal alleen de bestanden opnieuw compileren die zijn gewijzigd sinds de laatste keer dat u ze hebt gecompileerd (dwz bestanden waarbij het “.c”-bestand een recentere tijdstempel heeft dan het overeenkomende “.o”-bestand).

Houd er rekening mee dat 1) er veel variaties zijn in het gebruik van make als het gaat om grote projecten, en 2) er zijn ook veel alternatieven om te maken. Ik heb hier alleen het absolute minimum aan hoogtepunten bereikt.


Antwoord 2, autoriteit 17%

Ik had dit probleem onlangs. In mijn geval had ik mijn IDE ingesteld om te kiezen welke compiler (C of C++) op elk bestand moest worden gebruikt volgens de extensie, en ik probeerde een C-functie aan te roepen (dwz van een .cbestand) uit C++-code.

Het .h-bestand voor de C-functie was niet verpakt in dit soort beveiliging:

#ifdef __cplusplus
extern "C" {
#endif
// all of your legacy C code here
#ifdef __cplusplus
}
#endif

Ik had dat kunnen toevoegen, maar ik wilde het niet wijzigen, dus heb ik het als volgt in mijn C++-bestand opgenomen:

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

(Petje tip aan UncaAlbyvoor zijn duidelijke uitleg van de effect van externe “C”.)


Antwoord 3, autoriteit 8%

Ik denk dat het probleem is dat wanneer u testpoint.c probeert te compileren, het in punt is, maar het weet niet van Point.c. Sinds Point.c heeft de definitie voor create, geen Point.c zal de compilatie mislukken.

Ik ben niet bekend met Mingw, maar je moet de compiler vertellen om naar Point.c te zoeken. Bijvoorbeeld met GCC zou u dit kunnen doen:

gcc point.c testpoint.c

Zoals anderen hebben Opgemerkt, moet u ook een van uw main-functies verwijderen, omdat u er maar één kunt hebben.


Antwoord 4, Autoriteit 3%

Voeg het “extern” -woord toe aan de functiedefinities in Point.h

Other episodes