You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多文件夹C项目单Makefile编写及符号重定义问题排查

多目录C项目Makefile构建与多重定义错误解决

项目结构

src
├── client
│   ├── client.c
│   ├── client_functions.c
│   └── client_functions.h
├── common
│   ├── conf.c
│   ├── conf.h
│   ├── file_list.c
│   └── file_list.h
├── Makefile
└── server
    ├── server.c
    ├── server_functions.c
    └── server_functions.h

需求概述

我希望在src根目录编写单个Makefile,执行一次make就能构建整个项目,最终生成client/client和server/server两个可执行文件,各源文件的依赖关系如下:

  • client.c依赖client_functions.h
  • client_functions.c依赖conf.h
  • server.c依赖server_functions.h
  • server_functions.c同时依赖conf.h和file_list.h

问题重现

我之前只写过单文件夹项目的Makefile,尝试编写后报错,修改后的Makefile内容如下:

CC := gcc
CFLAGS := -Wall -Wextra -Wpedantic -O3
SRC := client/client.c client/client_functions.c client/client_functions.h server/server.c server/server_functions.c server/server_functions.h common/conf.c common/conf.h common/file_list.c common/file_list.h
OBJ := $(SRC:.c=.o)
.PHONY: all
all: client/client server/server
client/client: client/client.o client/client_functions.o common/conf.o
client/client.o: client/client.c client/client_functions.h
client/client_functions.o: client/client_functions.c client/client_functions.h common/conf.h
server/server: server/server.o server/server_functions.o common/conf.o common/file_list.o
server/server.o: server/server.c server/server_functions.h
server/server_functions.o: server/server_functions.c server/server_functions.h common/conf.h common/file_list.h
common/conf.o: common/conf.c common/conf.h
common/file_list.o: common/file_list.c common/file_list.h
clean:
	rm -f client/*.o server/*.o common/*.o core
cleanall:
	rm -f client/*.o server/*.o common/*.o core client/client server/server

执行make后出现多重定义错误,输出如下:

gcc -Wall -Wextra -Wpedantic -O3 -c -o client/client.o client/client.c
gcc -Wall -Wextra -Wpedantic -O3 -c -o client/client_functions.o client/client_functions.c
gcc -Wall -Wextra -Wpedantic -O3 -c -o common/conf.o common/conf.c
gcc client/client.o client/client_functions.o common/conf.o -o client/client
common/conf.o:(.rodata+0x0): multiple definition of `at'
client/client_functions.o:(.rodata+0x0): first defined here
common/conf.o:(.rodata+0x4): multiple definition of `T'
client/client_functions.o:(.rodata+0x4): first defined here
common/conf.o:(.rodata+0x8): multiple definition of `N'
client/client_functions.o:(.rodata+0x8): first defined here
common/conf.o:(.rodata+0x10): multiple definition of `p'
client/client_functions.o:(.rodata+0x10): first defined here
collect2: error: ld returned 1 exit status
<builtin>: recipe for target 'client/client' failed
make: *** [client/client] Error 1

注:at、T、N、p均为conf.h中定义的变量。


问题根源分析

这个错误的核心原因是你在头文件conf.h中直接定义了全局变量,而不是仅声明变量。当多个.c文件(比如client_functions.c和conf.c)都包含conf.h时,每个编译单元都会生成这些变量的独立副本,链接阶段就会触发"multiple definition"(多重定义)错误。

解决方案

1. 修正头文件的变量声明方式

在conf.h中,将变量的定义改为声明,使用extern关键字告知编译器变量在其他编译单元中存在:

// conf.h
extern const char at;
extern const char T;
extern const char N;
extern const char p;
// 其他函数声明...

然后在conf.c中定义这些变量(提供实际的内存存储):

// conf.c
#include "conf.h"

// 定义全局变量,仅在这个编译单元中存在实例
const char at = ...; // 替换为你的实际初始化值
const char T = ...;
const char N = ...;
const char p = ...;

// 其他conf.c的函数实现...

2. 优化Makefile(可选但推荐)

你的Makefile已经能处理基本依赖,但可以优化得更简洁,减少手动维护成本:

CC := gcc
CFLAGS := -Wall -Wextra -Wpedantic -O3
# 让gcc自动生成头文件依赖文件,无需手动维护
CFLAGS += -MMD -MP

# 定义最终目标可执行文件
CLIENT_TARGET := client/client
SERVER_TARGET := server/server

# 统一管理各可执行文件需要的源文件
CLIENT_SRCS := client/client.c client/client_functions.c common/conf.c
SERVER_SRCS := server/server.c server/server_functions.c common/conf.c common/file_list.c

# 自动生成对应的目标文件路径
CLIENT_OBJS := $(CLIENT_SRCS:.c=.o)
SERVER_OBJS := $(SERVER_SRCS:.c=.o)

# 自动包含生成的依赖文件
-include $(CLIENT_OBJS:.o=.d) $(SERVER_OBJS:.o=.d)

.PHONY: all clean cleanall
all: $(CLIENT_TARGET) $(SERVER_TARGET)

# 链接生成client可执行文件
$(CLIENT_TARGET): $(CLIENT_OBJS)
	$(CC) $(CFLAGS) $^ -o $@

# 链接生成server可执行文件
$(SERVER_TARGET): $(SERVER_OBJS)
	$(CC) $(CFLAGS) $^ -o $@

# 通用编译规则,make会自动推导,但显式写出更清晰
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(CLIENT_OBJS) $(SERVER_OBJS) core
	rm -f $(CLIENT_OBJS:.o=.d) $(SERVER_OBJS:.o=.d)

cleanall: clean
	rm -f $(CLIENT_TARGET) $(SERVER_TARGET)

优化点说明:

  • 使用-MMD -MP自动生成.d依赖文件,无需手动编写头文件依赖规则
  • 通过变量统一管理源文件和目标文件,后续新增文件只需修改变量
  • 通用的.o: .c规则避免重复编写每个目标的编译命令

内容的提问来源于stack exchange,提问作者Robb1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:40:16