Makefile自动生成对象文件时链接器无法找到函数引用问题
链接器错误排查:undefined reference与multiple definition问题
编译生成对象文件后,链接器报错undefined reference to <function>,但相关函数已完成声明与定义,手工编译链接所有对象文件可正常运行,但使用Makefile执行时出现以下错误:
mkdir -p ./build/ clang -g -I./include/ -c src/file1.c -o build/file1.o clang -g -I./include/ -c src/file1.c -o build/file2.o clang -g -I./include/ -o a.out main.c ./build/file1.o ./build/file2.o /usr/bin/ld: ./build/file2.o: in function `some_bit_func': /home/rat/dev/some_project/src/file1.c:4: multiple definition of `some_bit_func'; ./build/file1.o:/home/rat/dev/some_project/src/file1.c:4: first defined here /usr/bin/ld: /tmp/main-6cf008.o: in function `main': /home/rat/dev/some_project/main.c:13: undefined reference to `some_func' clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [Makefile:21: a.out] Error 1
问题代码与结构
简化后的Makefile
CC := clang CFLAGS := -g BIN := a.out BUILDDIR := ./build/ SRCDIR := ./src/ INCDIR := ./include/ CFLAGS += -I$(INCDIR) SRCEXCL := $(SRCDIR)file3.c INCEXCL := $(INCDIR)file3.h SOURCES := $(filter-out $(SRCEXCL), $(wildcard $(SRCDIR)*.c)) INCLUDE := $(filter-out $(INCEXCL), $(wildcard $(INCDIR)*.h)) OBJECTS := $(addprefix $(BUILDDIR), $(subst $(SRCDIR),, $(SOURCES:.c=.o))) .PHONY: all all: $(BIN) run clean $(BIN): main.c $(OBJECTS) $(CC) $(CFLAGS) -o $@ $< $(OBJECTS) $(OBJECTS): | $(BUILDDIR) $(BUILDDIR)%.o: $(SOURCES) $(INCLUDE) build $(CC) $(CFLAGS) -c $< -o $@ build: mkdir -p $(BUILDDIR) run: ./$(BIN) clean: rm -rf $(BUILDDIR) $(BIN)
项目文件结构
./include/shared.h
#pragma once #ifndef _DATA_H_ #define _DATA_H_ #define CHECK_BIT(num, pos) ((num) & (pos)) // just some enum enum bits { ONE = 1, TWO = 2, THREE = 4, FOUR = 8, FIVE = 16, SIX = 32, SEVEN = 64, EIGHT = 128, NINE = 256, INVALID = -1 }; #endif // _DATA_H_
./include/file1.h
#pragma once #ifndef _FILE1_H_ #define _FILE1_H_ #include <shared.h> #include <stdint.h> // just some function enum bits some_bit_func(uint16_t number); #endif // _FILE1_H_
./src/file1.c
#include <file1.h> enum bits some_bit_func(uint16_t number) { enum bits result; // 100% valid code here return result; }
./src/file2.c
#include <shared.h> #include <stdint.h> uint16_t some_func(uint16_t number) { return CHECK_BIT(number, FOUR); }
main.c
#include <file1.h> #include <stdio.h> #include <sys/types.h> extern uint16_t some_func(uint16_t number); int main(void) { size_t arr_size = 3; uint16_t numbers[] = { 5, 9, 7 }; for (size_t i = 0; i < arr_size; i++) { printf("some_func(%d):[%d]: %d\n", numbers[i], i, some_func(numbers[i])); printf("some_bit_func(%d):[%d]: %d\n\n", numbers[i], i, some_bit_func(numbers[i])); } return 0; }
错误原因分析
从Make的执行输出能直接定位问题:
- multiple definition错误:编译
build/file2.o时用的是src/file1.c作为源文件,导致file2.o中也包含some_bit_func的定义,和file1.o重复。 - undefined reference错误:
src/file2.c根本没被编译,所以some_func的目标代码不存在,链接时找不到。
根源在Makefile的目标文件编译规则:
$(BUILDDIR)%.o: $(SOURCES) $(INCLUDE) build $(CC) $(CFLAGS) -c $< -o $@
- 规则的依赖是
$(SOURCES)(所有源文件),$<代表第一个依赖,也就是src/file1.c,所以不管是生成哪个.o文件,都是编译src/file1.c。 - 正确的规则应该让每个
$(BUILDDIR)%.o对应$(SRCDIR)%.c,只依赖自身的源文件和头文件。
修复后的Makefile
CC := clang CFLAGS := -g BIN := a.out BUILDDIR := ./build/ SRCDIR := ./src/ INCDIR := ./include/ CFLAGS += -I$(INCDIR) SRCEXCL := $(SRCDIR)file3.c INCEXCL := $(INCDIR)file3.h SOURCES := $(filter-out $(SRCEXCL), $(wildcard $(SRCDIR)*.c)) INCLUDE := $(filter-out $(INCEXCL), $(wildcard $(INCDIR)*.h)) OBJECTS := $(addprefix $(BUILDDIR), $(notdir $(SOURCES:.c=.o))) .PHONY: all run clean build all: $(BIN) run clean $(BIN): main.c $(OBJECTS) $(CC) $(CFLAGS) -o $@ $^ $(BUILDDIR)%.o: $(SRCDIR)%.c $(INCLUDE) | $(BUILDDIR) $(CC) $(CFLAGS) -c $< -o $@ $(BUILDDIR): mkdir -p $@ run: ./$(BIN) clean: rm -rf $(BUILDDIR) $(BIN)
关键修改点
- 修正目标文件匹配规则:
$(BUILDDIR)%.o: $(SRCDIR)%.c $(INCLUDE) | $(BUILDDIR),让每个.o文件对应src目录下同名的.c文件。 - 调整OBJECTS生成方式:用
$(notdir)替代subst,更简洁地提取源文件名。 - 链接命令用$^替代$<:
$^会包含所有依赖(main.c + 所有OBJECTS),避免遗漏。 - 简化BUILDDIR创建规则:直接用
$(BUILDDIR)作为目标,替代单独的build伪目标。
内容的提问来源于stack exchange,提问作者rat
相关产品推荐
相关产品推荐

