Ошибка C: неопределенная ссылка на функцию, но она определена - PullRequest
52 голосов
/ 06 апреля 2011

Просто простая программа, но я продолжаю получать эту ошибку компилятора. Я использую MinGW для компилятора.

Вот заголовочный файл, 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);

А вот 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;
}

И вот тут возникает проблема с компилятором. Я продолжаю получать:

testpoint.c: неопределенная ссылка на 'create (double x, double y)'

Пока это определено в point.c.

Это отдельный файл с именем 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;
}

Я в недоумении относительно того, в чем может быть проблема.

Ответы [ 4 ]

82 голосов
/ 06 апреля 2011

Как дела с компиляцией и компоновкой?Вам нужно будет указать оба файла, что-то вроде:

gcc testpoint.c point.c

... чтобы он знал, как связать функции из обоих вместе.Однако, если код написан прямо сейчас, вы столкнетесь с противоположной проблемой: множественные определения main.Вам нужно / нужно устранить один (несомненно, тот, что в point.c).

В более крупной программе вы обычно компилируете и ссылаетесь отдельно, чтобы избежать повторной компиляции всего, что не изменилось.Обычно вы указываете, что нужно сделать через make-файл, и используете make для выполнения работы.В этом случае у вас будет что-то вроде этого:

OBJS=testpoint.o point.o

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

Первый - это просто макрос для имен объектных файлов.Вы получаете это расширенным $(OBJS).Второе - правило, сообщающее make 1), что исполняемый файл зависит от объектных файлов, и 2) указание, как создать исполняемый файл, если / если он устарел по сравнению с объектным файлом.

Большинство версийв make (включая тот, что в MinGW, я уверен), есть встроенное «неявное правило», которое говорит им, как создать объектный файл из исходного файла на языке Си.Обычно это выглядит примерно так:

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

Предполагается, что имя компилятора C находится в макросе с именем CC (неявно определенным как CC=gcc) и позволяет вам указать любые флаги, которые вам нужны вмакрос с именем CFLAGS (например, CFLAGS=-O3 для включения оптимизации) и $< - это специальный макрос, который расширяется до имени исходного файла.

Обычно его хранят в файле с именем Makefile, и для сборки вашей программы вы просто набираете make в командной строке.Он неявно ищет файл с именем Makefile и выполняет все содержащиеся в нем правила.

Хорошая точка зрения в том, что make автоматически просматривает временные метки на файлах, поэтому он будет перекомпилирован толькофайлы, которые изменились с момента их последней компиляции (т. е. файлы, в которых файл «.c» имеет более позднюю отметку времени, чем соответствующий файл «.o»).

Также обратите внимание, что 1) есть много вариантов использования make, когда речь идет о больших проектах, и 2) есть также много альтернативных вариантов.Здесь я получил только минимум рекордов.

7 голосов
/ 26 октября 2017

У меня недавно была эта проблема.В моем случае в моей среде IDE было установлено, какой компилятор (C или C ++) использовать для каждого файла в соответствии с его расширением, и я пытался вызвать функцию C (т.е. из файла .c) из кода C ++.

Файл .h для функции C не был обернут в этот тип защиты:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Я мог бы добавить это, но я не хотел изменять егопоэтому я просто включил его в свой файл C ++ примерно так:

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

(Шляпа, подсказывающая UncaAlby за его ясное объяснение эффекта extern "C" .)

6 голосов
/ 06 апреля 2011

Думаю, проблема в том, что когда вы пытаетесь скомпилировать testpoint.c, он включает в себя point.h, но не знает о point.c.Так как у point.c есть определение create, отсутствие point.c приведет к сбою компиляции.

Я не знаком с MinGW, но вы должны указать компилятору искать точку.с.Например, с помощью gcc вы можете сделать следующее:

gcc point.c testpoint.c

Конечно, как указали другие , вам также необходимо удалить одну из ваших main функций, поскольку вы можете иметь толькоодин.

1 голос
/ 06 апреля 2011

Добавьте ключевое слово "extern" к определениям функций в point.h

...