使用CUnit测试时出现'main'重定义错误的求助
问题描述
执行make后运行make test出现链接错误:
/usr/bin/ld: test_errors.o: in function 'main': test_errors.c:(.text+0x35): multiple definition of `main'; motcache.o:motcache.c:(.text+0x0): first defined here /usr/bin/ld: test_file_handling.o: in function 'main': test_file_handling.c:(.text+0x96): multiple definition of `main'; motcache.o:motcache.c:(.text+0x0): first defined here collect2: error: ld returned 1 exit status make: *** [Makefile:21: link_test] Error 1
相关文件内容:
Makefile
CC = gcc OPTIONS = -Wall -Wextra EXE = motcache SRC_DIR = src TESTS_DIR = test all : start compile : $(CC) $(OPTIONS) -c $(SRC_DIR)/*.c test: run_test link: compile $(CC) *.o -o $(EXE) compile_test : $(CC) $(OPTIONS) -c $(TESTS_DIR)/*.c link_test : compile_test $(CC) $(OPTIONS) *.o -o cunit_tests -lcunit run_test: link_test ./cunit_tests clean: rm -f $(EXE) *.o cunit_tests start: link ./$(EXE)
测试文件test_file_handling.c
#include <stdio.h> #include <stdbool.h> #include <stdlib.h> #include "CUnit/CUnit.h" #include "CUnit/Basic.h" #include "../src/file_handling.h" void test_open_file_success(void){ char *argv[] = {"./motcache", "mandoline.txt"}; FILE *fptr = open_file(argv); CU_ASSERT_PTR_NOT_NULL(fptr); fclose(fptr); } int main(){ CU_initialize_registry(); CU_pSuite suite = CU_add_suite("Ouverture de fichiers", NULL, NULL); CU_add_test(suite, "Test d'ouverture de fichier avec succès", test_open_file_success); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); CU_cleanup_registry(); return 0; }
主文件motcache.c
#include <stdio.h> #include "motcache.h" #include "errors.h" #include "file_handling.h" int main(){ return 0; }
问题原因
C语言规定:一个可执行程序必须且只能有一个main函数,它是操作系统启动程序时的唯一入口点。你的错误源于:
- 执行
make后,主程序的motcache.c被编译成motcache.o,其中包含一个main函数 - 执行
make test时,编译测试文件生成test_errors.o、test_file_handling.o,每个都包含自己的main - 链接测试程序时用了
*.o,把所有目标文件(包括带main的motcache.o)都包含进来,导致链接器发现多个main定义,无法确定入口,因此报错。
解决方案
方法1:修改Makefile,分开主程序和测试的目标文件
调整Makefile,将主程序和测试的目标文件存放在不同目录,避免链接时混合。修改后的Makefile如下:
CC = gcc OPTIONS = -Wall -Wextra EXE = motcache SRC_DIR = src TESTS_DIR = test OBJ_DIR = obj TEST_OBJ_DIR = test_obj all : start # 编译主程序源码到obj目录 compile : mkdir -p $(OBJ_DIR) $(CC) $(OPTIONS) -c $(SRC_DIR)/*.c mv *.o $(OBJ_DIR)/ # 链接生成主程序 link: compile $(CC) $(OBJ_DIR)/*.o -o $(EXE) # 编译测试代码到test_obj目录 compile_test : mkdir -p $(TEST_OBJ_DIR) $(CC) $(OPTIONS) -c $(TESTS_DIR)/*.c mv *.o $(TEST_OBJ_DIR)/ # 链接测试程序:只包含主程序中除motcache.o(带main)的文件,加上测试文件 link_test : compile_test compile SRC_OBJS = $(filter-out $(OBJ_DIR)/motcache.o, $(OBJ_DIR)/*.o) $(CC) $(OPTIONS) $(SRC_OBJS) $(TEST_OBJ_DIR)/*.o -o cunit_tests -lcunit run_test: link_test ./cunit_tests clean: rm -rf $(OBJ_DIR) $(TEST_OBJ_DIR) rm -f $(EXE) cunit_tests start: link ./$(EXE)
方法2:每个测试文件单独编译为可执行程序
如果不需要合并所有测试到一个可执行文件,可以修改Makefile,为每个测试文件生成单独的测试程序:
CC = gcc OPTIONS = -Wall -Wextra EXE = motcache SRC_DIR = src TESTS_DIR = test all : start compile : $(CC) $(OPTIONS) -c $(SRC_DIR)/*.c link: compile $(CC) *.o -o $(EXE) # 编译所有测试文件为单独的可执行 test: for test_file in $(TESTS_DIR)/*.c; do \ test_name=$$(basename $$test_file .c); \ # 编译测试文件+主程序中除motcache.o的目标文件 $(CC) $(OPTIONS) $(filter-out motcache.o, *.o) $$test_file -o $$test_name -lcunit; \ ./$$test_name; \ rm $$test_name; \ done clean: rm -f $(EXE) *.o cunit_tests start: link ./$(EXE)
关键注意事项
- 测试程序不需要包含主程序的
motcache.o,因为测试的是业务逻辑函数(比如open_file),不需要主程序的入口 - 始终确保每个可执行程序只有一个
main函数,这是C程序的基本规则
内容的提问来源于stack exchange,提问作者TheGunner422
相关产品推荐
相关产品推荐

