(.text+0x20):未定义对 `main' 的引用和未定义对函数的引用。

24

我遇到了一个问题,无法使我的makefile正常工作而没有错误。我遇到的第一个问题是未定义对主函数的引用。在我的producer.c文件中,我已经将main作为一个函数声明了。第二个问题是未定义对SearchCustomer()的引用。

错误:

bash-4.1$ make
gcc -Wall -c producer.c shared.h
gcc -Wall -c consumer.c shared.h
gcc -Wall -c AddRemove.c shared.h
gcc -pthread -Wall -o producer.o consumer.o AddRemove.o
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
AddRemove.o: In function `AddRemove':
AddRemove.c:(.text+0xb1): undefined reference to `SearchCustomer'
AddRemove.c:(.text+0x1e9): undefined reference to `SearchCustomer'
AddRemove.c:(.text+0x351): undefined reference to `SearchCustomer'
collect2: ld returned 1 exit status
make: *** [producer] Error 1

makefile:

COMPILER = gcc
CCFLAGS = -Wall
all: main

debug:
    make DEBUG=TRUE


main: producer.o consumer.o AddRemove.o
    $(COMPILER) -pthread $(CCFLAGS) -o producer.o consumer.o AddRemove.o
producer.o: producer.c shared.h
    $(COMPILER) $(CCFLAGS) -c producer.c shared.h
consumer.o: consumer.c shared.h
    $(COMPILER) $(CCFLAGS) -c consumer.c shared.h
AddRemove.o: AddRemove.c shared.h
    $(COMPILER) $(CCFLAGS) -c AddRemove.c shared.h


ifeq ($(DEBUG), TRUE)
    CCFLAGS += -g
endif

clean:
    rm -f *.o

代码太长了。缩小问题的范围并提供一个简短、自包含、正确(可编译)的示例 - Yu Hao
1
几个建议:在main规则中,您可以编写:$(COMPILER) -o $@ $^ $(LDFLAGS)并将-pthread放入LDFLAGS。在其他规则中,您可以编写一次%.o: %.c并作为配方:$(COMPILER) $(CCFLAGS) -c -o $@ $<不要.h文件交给编译器!)然后为特殊目标添加依赖项,例如:producer.o: shared.h other_header.hconsumer.o: shared.h yet_another_header.h等。 - Shahbaz
1
另外还有几个建议:使用$(MAKE)而不是仅使用make以保留调用make的命令行标志。您可能还想使用--no-print-directory来防止输出几行无用的内容。此外,对于C语言,请使用CFLAGS(而不是可能被误认为是C ++的CCFLAGS)。 - Shahbaz
还有:添加 .PHONY: all clean debug 和其他目标,这些目标实际上并不是文件,让 make 知道它不应该期望从配方中得到一个实际的文件。你也可以使用 @$(MAKE) DEBUG=TRUE(添加 @)来防止 make 回显命令。 - Shahbaz
最后,您可以从阅读手册中受益:https://www.gnu.org/software/make/manual/make.html - Shahbaz
3个回答

20

这条规则

main: producer.o consumer.o AddRemove.o
   $(COMPILER) -pthread $(CCFLAGS) -o producer.o consumer.o AddRemove.o

有误。它说要创建名为producer.o的文件(使用-o producer.o),但是你想创建一个名为main的文件。请原谅我高声喊出,但始终使用$@引用目标

main: producer.o consumer.o AddRemove.o
   $(COMPILER) -pthread $(CCFLAGS) -o $@ producer.o consumer.o AddRemove.o

正如Shahbaz所指出的,gmake专业人士也会使用$^,它可以扩展到规则中的所有先决条件。一般来说,如果你发现自己在重复一个字符串或名称,那么你做错了,应该使用一个变量,无论是内置的还是你创建的。

main: producer.o consumer.o AddRemove.o
   $(COMPILER) -pthread $(CCFLAGS) -o $@ $^

6
请使用 $^ 来引用其所有依赖项。 - Shahbaz

7
这个错误意味着编译器在链接时无法找到任何地方定义main()函数。
在您的makefile中,main规则将扩展为类似于以下内容。
main: producer.o consumer.o AddRemove.o
   gcc -pthread -Wall -o producer.o consumer.o AddRemove.o

根据手册页gcc使用-o开关的方法如下:

-o file     将输出放置在文件file中。无论产生什么类型的输出,无论是可执行文件、目标文件、汇编文件还是预处理的C代码,都适用。如果未指定-o,则默认将可执行文件放在a.out中。

这意味着,gcc会将输出放置在紧接着-o开关后面提供的文件名中。因此,在这里,它创建了二进制文件producer.o,并链接其他的.o文件,而不是将所有的.o文件连接起来创建二进制文件[在你的情况下,是main]。请进行更正。

1
这里有另一个可能导致此错误的bug:
我在一个命名空间中写了一些代码,所以我的头文件看起来像这样:namespace MY_NAMESPACE { ... }
在c文件中,我愚蠢地做了同样的事情,写了namespace MY_NAMESPACE { ... main() ... }。
编译时,它会失败并显示给定的错误。相反,我将c文件中的代码更改为using namespace MY_NAMESPACE {... main() ...}。这个方法可行是因为现在,main不再属于一个独立的命名空间,而是使用另一个命名空间的定义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接