77

Just a simple program, but I keep getting this compiler error. I'm using MinGW for the compiler.

Here's the header file, 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);

And here's 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;
}

And here's where the compiler issue comes in. I keep getting:

testpoint.c: undefined reference to 'create(double x, double y)'

While it is defined in point.c.

This is a separate file called 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;
}

I'm at a loss as to what the issue could be.

alk
  • 66,653
  • 10
  • 83
  • 219
upswimsdn
  • 874
  • 1
  • 6
  • 8
  • 8
    Could you post your makefile? Also, you have 2 main functions defined, that can't be good. – George Apr 05 '11 at 22:20
  • Probably a redefinition of `main()` which is the entry point to your program. Get rid of the one in `point.c` – RageD Apr 05 '11 at 22:25
  • @upswimsdn, was it because double definition of main()? – Chan Kim Mar 10 '16 at 05:55
  • 2
    Yes that was an additional problem I ran into, but the main problem was not compiling the two files together using "gcc testpoint.c point.c" (see the accepted answer). – upswimsdn Mar 10 '16 at 16:13

4 Answers4

114

How are you doing the compiling and linking? You'll need to specify both files, something like:

gcc testpoint.c point.c

...so that it knows to link the functions from both together. With the code as it's written right now, however, you'll then run into the opposite problem: multiple definitions of main. You'll need/want to eliminate one (undoubtedly the one in point.c).

In a larger program, you typically compile and link separately to avoid re-compiling anything that hasn't changed. You normally specify what needs to be done via a makefile, and use make to do the work. In this case you'd have something like this:

OBJS=testpoint.o point.o

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

The first is just a macro for the names of the object files. You get it expanded with $(OBJS). The second is a rule to tell make 1) that the executable depends on the object files, and 2) telling it how to create the executable when/if it's out of date compared to an object file.

Most versions of make (including the one in MinGW I'm pretty sure) have a built-in "implicit rule" to tell them how to create an object file from a C source file. It normally looks roughly like this:

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

This assumes the name of the C compiler is in a macro named CC (implicitly defined like CC=gcc) and allows you to specify any flags you care about in a macro named CFLAGS (e.g., CFLAGS=-O3 to turn on optimization) and $< is a special macro that expands to the name of the source file.

You typically store this in a file named Makefile, and to build your program, you just type make at the command line. It implicitly looks for a file named Makefile, and runs whatever rules it contains.

The good point of this is that make automatically looks at the timestamps on the files, so it will only re-compile the files that have changed since the last time you compiled them (i.e., files where the ".c" file has a more recent time-stamp than the matching ".o" file).

Also note that 1) there are lots of variations in how to use make when it comes to large projects, and 2) there are also lots of alternatives to make. I've only hit on the bare minimum of high points here.

Jerry Coffin
  • 437,173
  • 71
  • 570
  • 1,035
  • 1
    This works, but is this generally how larger C programs are linked / compiled together? The extern keyword in the header file didn't seem to fix anything. – upswimsdn Apr 05 '11 at 22:40
  • 1
    It seems like you only answered enough to solve the askers problem. That's not the same as answering the question. For instance, I have the exact same question as in the title, but everything is in one file. The purpose of stackoverflow is to provide searchable answers, not just help someone once then mislead anyone who searches the same question. – Timothy Swan Feb 20 '18 at 19:53
  • 3
    @TimothySwan: I try to provide reasonably general answers, *but* there's a limit. Turing every answer into a textbook won't help much of anybody--and if you're dealing with a single file, even if your search turned this up, it's still *quite* a different question. – Jerry Coffin Feb 21 '18 at 06:41
  • @JerryCoffin Thanks for the answer! However this gives me another problem: when we use some library functions like printf, we usually don't add library files to gcc commands during compile. We only need to include stdio.h and it works. Is there any difference between the situation I described and the one in the problem? – Zirui Bai Feb 27 '20 at 09:47
  • @ZiruiBai: The only real difference is that when `gcc` (or most other compilers) spawns the linker, by default it'll tell the linker to link to the standard library. But, for any other library, that doesn't happen (and it doesn't usually even happen for any math functions in the standard library--at least in most cases you need to explicitly link that). – Jerry Coffin Feb 27 '20 at 09:55
17

I had this issue recently. In my case, I had my IDE set to choose which compiler (C or C++) to use on each file according to its extension, and I was trying to call a C function (i.e. from a .c file) from C++ code.

The .h file for the C function wasn't wrapped in this sort of guard:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

I could've added that, but I didn't want to modify it, so I just included it in my C++ file like so:

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

(Hat tip to UncaAlby for his clear explanation of the effect of extern "C".)

cp.engr
  • 1,862
  • 3
  • 24
  • 37
8

I think the problem is that when you're trying to compile testpoint.c, it includes point.h but it doesn't know about point.c. Since point.c has the definition for create, not having point.c will cause the compilation to fail.

I'm not familiar with MinGW, but you need to tell the compiler to look for point.c. For example with gcc you might do this:

gcc point.c testpoint.c

As others have pointed out, you also need to remove one of your main functions, since you can only have one.

Cam
  • 13,963
  • 16
  • 70
  • 121
2

Add the "extern" keyword to the function definitions in point.h

Richard Schneider
  • 33,296
  • 8
  • 52
  • 68
  • 3
    `extern` on a function has no effect whatsoever (at least these days), since every function declared in a header is public / external. – Engineer Nov 05 '14 at 15:52