webentwicklung-frage-antwort-db.com.de

C-Fehler: undefinierter Verweis auf Funktion, aber IS defined

Nur ein einfaches Programm, aber ich bekomme immer wieder diesen Compiler-Fehler. Ich benutze MinGW für den Compiler.

Hier ist die Header-Datei 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);

Und hier ist 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;
}

Und hier kommt das Compiler-Problem ins Spiel. Ich bekomme immer wieder:

testpoint.c: undefinierter Verweis auf 'create (double x, double y)'

Während es in point.c definiert ist.

Dies ist eine separate Datei mit dem Namen 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;
}

Ich bin ratlos, was das Problem sein könnte.

52
upswimsdn

Wie machst du das Kompilieren und Verknüpfen? Sie müssen beide Dateien wie folgt angeben:

gcc testpoint.c point.c

... damit es weiß, dass die Funktionen von beiden zusammengehören. Mit dem Code, wie er gerade geschrieben wurde, werden Sie jedoch auf das gegenteilige Problem stoßen: mehrere Definitionen von main. Sie müssen/möchten einen eliminieren (zweifellos den in point.c).

In einem größeren Programm kompilieren und verknüpfen Sie normalerweise separat, um zu vermeiden, dass nicht geänderte Elemente erneut kompiliert werden. Normalerweise geben Sie über ein Makefile an, was zu tun ist, und verwenden make, um die Arbeit zu erledigen. In diesem Fall hätten Sie so etwas:

OBJS=testpoint.o point.o

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

Das erste ist nur ein Makro für die Namen der Objektdateien. Sie erhalten es mit $(OBJS) erweitert. Die zweite Regel besagt, dass make 1) die ausführbare Datei von den Objektdateien abhängt und 2) angibt, wie die ausführbare Datei erstellt werden soll, wenn sie im Vergleich zu einer Objektdatei veraltet ist.

Die meisten Versionen von make (einschließlich der in MinGW, da bin ich mir ziemlich sicher) haben eine eingebaute "implizite Regel", nach der sie eine Objektdatei aus einer C-Quelldatei erstellen können. Normalerweise sieht es ungefähr so ​​aus:

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

Dies setzt voraus, dass sich der Name des C-Compilers in einem Makro mit dem Namen CC befindet (implizit definiert wie CC=gcc) und dass Sie in einem Makro mit dem Namen CFLAGS beliebige Flags angeben können (z. B. CFLAGS=-O3, um die Optimierung zu aktivieren) $< ist ein spezielles Makro, das zum Namen der Quelldatei erweitert wird.

Sie speichern dies normalerweise in einer Datei mit dem Namen Makefile, und um Ihr Programm zu erstellen, geben Sie einfach make in die Befehlszeile ein. Es sucht implizit nach einer Datei mit dem Namen Makefile und führt alle darin enthaltenen Regeln aus.

Das Gute daran ist, dass make die Zeitstempel der Dateien automatisch überprüft, sodass nur die Dateien neu kompiliert werden, die sich seit dem letzten Kompilieren geändert haben (dh Dateien, bei denen das ".c msgstr "Datei hat einen aktuelleren Zeitstempel als die passende" .o "- Datei).

Beachten Sie auch, dass 1) es viele Variationen bei der Verwendung von make gibt, wenn es um große Projekte geht, und 2) es gibt auch viele Alternativen. Ich habe hier nur das Nötigste an Höhepunkten erreicht.

83
Jerry Coffin

Ich hatte dieses Problem vor kurzem. In meinem Fall hatte ich mein IDE) festgelegt, um zu wählen, welcher Compiler (C oder C++) für jede Datei entsprechend ihrer Erweiterung verwendet werden soll, und ich versuchte, eine C-Funktion aufzurufen (dh von einem .c Datei) aus C++ Code.

Das .h -Datei für die C-Funktion wurde nicht in diese Art von Guard eingeschlossen:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Ich hätte das hinzufügen können, aber ich wollte es nicht ändern, also habe ich es einfach so in meine C++ - Datei eingefügt:

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

(Hutspitze an ncaAlby für seine klare Erklärung des Effekt von extern "C" .)

9
cp.engr

Ich denke, das Problem ist, dass, wenn Sie versuchen, testpoint.c zu kompilieren, es point.h enthält, aber es nicht über point.c weiß. Da point.c die Definition für create hat, schlägt die Kompilierung fehl, wenn point.c nicht vorhanden ist.

Ich bin nicht mit MinGW vertraut, aber Sie müssen den Compiler anweisen, nach point.c zu suchen. Zum Beispiel könnten Sie dies mit gcc tun:

gcc point.c testpoint.c

Natürlich müssen Sie, wie andere hervorgehoben haben, auch eine Ihrer main -Funktionen entfernen, da Sie nur eine haben können.

6
Cam

Fügen Sie den Funktionsdefinitionen in point.h das Schlüsselwort "extern" hinzu

1

Ich war vor kurzem von einem ähnlichen Problem verwirrt. Dann bemerkte er, dass die Forward-Deklaration der Funktion "statisch" war, also interne Verknüpfungen aufwies und unsichtbar war. Trotzdem hatte die Definition kein Schlüsselwort.

Achten Sie darauf, Ihre Erklärungen Jungs zu überprüfen!

0
Calmarius