Pytanie Błąd C: niezdefiniowane odniesienie do funkcji, ale jest zdefiniowane


Tylko prosty program, ale ciągle dostaję ten błąd kompilatora. Używam MinGW dla kompilatora.

Oto plik nagłówkowy, 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);

I oto jest 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;
}

A tu pojawia się problem z kompilatorem. Otrzymuję:

testpoint.c: niezdefiniowane odniesienie do 'create (double x, double y)'

Chociaż jest zdefiniowany w punkcie.c.

To jest osobny plik o nazwie 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;
}

Nie wiem, na czym polega problem.


44
2018-04-05 22:18


pochodzenie


Czy otrzymujesz jakieś inne wiadomości? - JaredPar
Czy mógłbyś opublikować swój plik Makefile? Ponadto masz zdefiniowane 2 główne funkcje, które nie mogą być dobre. - George
Prawdopodobnie redefinicja main() który jest punktem wejścia do twojego programu. Pozbądź się tego point.c - RageD
Już działa. Dziękuje wszystkim. - upswimsdn
Tak, to był dodatkowy problem, na który natknąłem się, ale głównym problemem nie było skompilowanie dwóch plików razem za pomocą "gcc testpoint.c point.c" (patrz zaakceptowana odpowiedź). - upswimsdn


Odpowiedzi:


Jak robisz kompilację i linkowanie? Musisz określić oba pliki, na przykład:

gcc testpoint.c point.c

... aby wiedział, jak połączyć funkcje obu. Z kodem jaki jest teraz napisany, wpadniesz na przeciwny problem: wiele definicji main. Będziesz potrzebować / chcesz go wyeliminować (niewątpliwie ten w punkcie.c).

Edycja: w większym programie zazwyczaj kompilujesz i łączysz osobno, aby uniknąć ponownego kompilowania wszystkiego, co się nie zmieniło. Zwykle określasz, co należy zrobić za pomocą pliku makefile i użyj make do wykonania pracy. W takim przypadku będziesz miał coś takiego:

OBJS=testpoint.o point.o

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

Pierwszy to tylko makro dla nazw plików obiektów. Otrzymasz jest rozszerzony o $(OBJS). Druga to reguła nakazująca make 1), że plik wykonywalny zależy od plików obiektów, oraz 2) informujący o tym, jak utworzyć plik wykonywalny, gdy / jeśli jest nieaktualny w porównaniu do pliku obiektowego.

Większość wersji make (włączając tę ​​w MinGW, jestem prawie pewien) ma wbudowaną "niejawną regułę", która mówi im, jak utworzyć plik obiektowy z pliku źródłowego C. Zwykle wygląda mniej więcej tak:

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

Zakłada się, że nazwa kompilatora C znajduje się w makrze o nazwie CC (niejawnie zdefiniowane jak CC=gcc) i pozwala określić dowolną flagę w makrze o nazwie CFLAGS (na przykład., CFLAGS=-O3 włączyć optymalizację) i $< to specjalne makro, które rozwija się do nazwy pliku źródłowego.

Zwykle przechowujesz to w pliku o nazwie Makefilei aby zbudować swój program, wystarczy wpisać make w linii poleceń. Niejawnie szuka pliku o nazwie Makefilei uruchamia wszelkie zawarte w nim reguły.

Najważniejsze jest to make automatycznie wyszukuje znaczniki czasu w plikach, więc tylko ponownie skompiluje pliki, które zmieniły się od czasu ich ostatniej kompilacji (tj. pliki, w których plik ".c" ma bardziej aktualny znacznik czasu niż odpowiadający " .o "plik).

Zwróć też uwagę, że 1) istnieje wiele odmian sposobu użycia, jeśli chodzi o duże projekty, oraz 2) istnieje również wiele alternatyw. Uderzyłem tu tylko o minimum wysokich punktów.


67
2018-04-05 22:20



Działa to, ale czy na ogół większe programy C są ze sobą połączone / kompilowane? Słowo kluczowe extern w pliku nagłówkowym najwyraźniej niczego nie naprawiło. - upswimsdn
@upswimsdn: Zobacz edytowaną odpowiedź. - Jerry Coffin
Wygląda na to, że odpowiedziałeś na tyle, aby rozwiązać problem osób pytających. To nie to samo, co udzielenie odpowiedzi na pytanie. Na przykład mam dokładnie to samo pytanie, co w tytule, ale wszystko jest w jednym pliku. Celem stackoverflow jest dostarczenie odpowiedzi, które można wyszukiwać, a nie tylko pomóc komuś, a następnie wprowadzić w błąd każdego, kto przeszukuje to samo pytanie. - Timothy Swan
@TimothySwan: Staram się udzielać rozsądnie ogólnych odpowiedzi, ale jest limit. Przełożenie każdej odpowiedzi na podręcznik nikomu nie pomoże - a jeśli masz do czynienia z pojedynczym plikiem, nawet jeśli wyszukiwanie to zmieniło, to nadal całkiem inne pytanie. - Jerry Coffin


Myślę, że problem polega na tym, że kiedy próbujesz skompilować testpoint.c, zawiera on point.h, ale nie wie o point.c. Ponieważ point.c ma definicję create, brak punktu.c spowoduje awarię kompilacji.

Nie jestem zaznajomiony z MinGW, ale musisz powiedzieć kompilatorowi, aby szukał punktu.c. Na przykład z gcc możesz to zrobić:

gcc point.c testpoint.c

Oczywiście jak inni wskazałem, musisz także usunąć jedną ze swoich main funkcje, ponieważ możesz je mieć tylko jeden.


6
2018-04-05 22:22





Ostatnio miałem ten problem. W moim przypadku miałem swój zestaw IDE, aby wybrać kompilator (C lub C ++) do użycia w każdym pliku zgodnie z jego rozszerzeniem i próbowałem wywołać funkcję C (tj. .c plik) z kodu C ++.

The .h plik dla funkcji C nie był zawinięty w tego rodzaju straży:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Mogłem to dodać, ale nie chciałem go modyfikować, więc po prostu umieściłem go w moim pliku C ++, jak na przykład:

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

(Czapka z kapeluszem do UncaAlby za jego jasne wyjaśnienie efekt extern "C".)


5
2017-10-26 20:58





Dodaj słowo kluczowe "extern" do definicji funkcji w punkcie.h


3
2018-04-05 22:25



extern na funkcji nie ma żadnego wpływu (przynajmniej w tych dniach), ponieważ każda funkcja zadeklarowana w nagłówku jest publiczna / zewnętrzna. - Arcane Engineer