C语言项目函数重复定义错误无法消除,求解决方案
问题根源
你遇到的链接错误核心原因是头文件中直接定义了函数实现(clear_session、send_request、serialize这三个函数)。C语言中,头文件被多个.c文件包含时,每个.c文件编译为目标文件(.o)时都会把这些函数的实现复制一份,链接阶段合并所有目标文件时,就会出现同一个函数被多次定义的冲突。
修复方案
1. 拆分头文件与实现文件
把logging.h中的函数实现移到单独的logging.c文件中,头文件只保留函数声明。
修改后的logging.h(仅保留声明)
#ifndef __UTILS_LOGGING_H__ #define __UTILS_LOGGING_H__ #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> //open #include <fcntl.h> //flags for open #include <unistd.h> #define PIPENAME 256 #define BOXNAME 32 #define MSIZE 256 #define COMMAND 8 static const __uint8_t REGISTER_PUBLISHER = 1; static const __uint8_t REGISTER_SUBSCRIBER = 2; static const __uint8_t CREATE_BOX = 3; static const __uint8_t REMOVE_BOX = 5; static const __uint8_t LIST_BOX = 7; static const char INVALID_SESSIONS[] = "Invalid number of sessions"; static const char PIPENAME_TOO_BIG[] = "The pipe's name has too many characters"; static const char INVALID_BOXNAME[] = "The box's name is invalid"; static const char INVALID_NUMBER_OF_ARGUMENTS[] = "Insufficient or too many arguments"; static const char INVALID_ARGUMENTS[] = "One of the arguments is invalid"; static const char UNEXISTENT_PIPE[] = "Pipe doesn't exist"; static const char EXISTENT_PIPE[] = "Pipe already exists"; static const char ERROR_WRITING_PIPE[] = "Error writing to pipe"; static const char SIGNAL_FAIL[] ="Failed to initialize signals"; typedef struct box{ char boxname[BOXNAME]; int hasWriter; int n_readers; struct box *next; } box; typedef struct req{ int _code; char _client_pipe[PIPENAME]; char _box_name[BOXNAME]; } request; typedef enum { LOG_QUIET = 0, LOG_NORMAL = 1, LOG_VERBOSE = 2, } log_level_t; // 仅保留函数声明,去掉实现 void clear_session(int fd, char* fn); void send_request(int tx, request *r1); request *serialize(int code, char* client_pipe, char* box_name); void sig_handler(int sig); void set_log_level(log_level_t level); extern log_level_t g_level; #define INFO(...) do { char buf[2048]; snprintf(buf, 2048, __VA_ARGS__); fprintf(stderr, "[INFO]: %s:%d :: %s :: %s\n", __FILE__, __LINE__, __func__, buf); } while (0); #define PANIC(...) do { char buf[2048]; snprintf(buf, 2048, __VA_ARGS__); fprintf(stderr, "[PANIC]: %s:%d :: %s :: %s\n", __FILE__, __LINE__, __func__, buf); exit(EXIT_FAILURE); } while (0); #define WARN(...) do { if (g_level == LOG_NORMAL || g_level == LOG_VERBOSE) { char buf[2048]; snprintf(buf, 2048, __VA_ARGS__); fprintf(stderr, "[WARN]: %s:%d :: %s :: %s\n", __FILE__, __LINE__, __func__, buf); } } while (0); #define LOG(...) do { if (g_level == LOG_NORMAL || g_level == LOG_VERBOSE) { char buf[2048]; snprintf(buf, 2048, __VA_ARGS__); fprintf(stderr, "[LOG]: %s:%d :: %s :: %s\n", __FILE__, __LINE__, __func__, buf); } } while (0); #define DEBUG(...) do { if (g_level == LOG_VERBOSE) { char buf[2048]; snprintf(buf, 2048, __VA_ARGS__); fprintf(stderr, "[DEBUG]: %s:%d :: %s :: %s\n", __FILE__, __LINE__, __func__, buf); } } while (0); #endif // __UTILS_LOGGING_H__
创建logging.c文件(存放函数实现)
#include "logging.h" //deletes fifo void clear_session(int fd, char* fn){ close(fd); unlink(fn); } /*writes to pipe tx a pointer with information*/ void send_request(int tx, request *r1) { ssize_t ret = write(tx, &r1, sizeof(r1)); if (ret < 0) { fprintf(stdout, "ERROR: %s\n", ERROR_WRITING_PIPE); exit(EXIT_FAILURE); } } /*Returns a pointer to a struct containing the request*/ request *serialize(int code, char* client_pipe, char* box_name){ request *r1 = (request*) malloc(sizeof(request)); r1->_code = code; strcpy(r1->_client_pipe, client_pipe); strcpy(r1->_box_name, box_name); return r1; } // 如果sig_handler和set_log_level有实现,同样放到这里 // void sig_handler(int sig) { // // 你的实现 // } // void set_log_level(log_level_t level) { // // 你的实现 // } // 全局变量g_level的定义也放到这里(如果之前未定义) // log_level_t g_level = LOG_NORMAL;
2. 更新编译脚本(Makefile)
确保logging.c被纳入编译流程,比如在Makefile的源文件列表中添加utils/logging.c(根据你的目录结构调整路径):
# 添加logging.c到源文件列表 SRCS = mbroker/mbroker.c fs/operations.c fs/state.c utils/logging.c OBJS = $(SRCS:.c=.o) TARGET = mbroker $(TARGET): $(OBJS) gcc -o $@ $(OBJS) %.o: %.c gcc -c $< -o $@ clean: rm -f $(OBJS) $(TARGET)
3. 重新编译
先执行make clean清理旧目标文件,再执行make重新编译,链接错误即可解决。
额外提示
- 头文件只能存放声明(函数原型、宏、类型定义、extern全局变量),不能放函数实现或全局变量定义(除非用
static修饰,但static会导致每个.c文件生成独立副本,不符合共享函数的需求)。 - 之前引入
.c文件的操作会加剧重复定义问题,但你已删除该语句,当前核心问题已通过拆分头文件与实现文件解决。
内容的提问来源于stack exchange,提问作者tomhoq
相关产品推荐
相关产品推荐

