【发布时间】:2022-02-01 23:55:57
【问题描述】:
我正在构建一个简单的错误检查系统并得到错误:
/tmp/ccNY7emi.o:(.data+0x0): multiple definition of `error_desc'
/tmp/ccMI89tg.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
在 Linux 上使用 gcc,接下来是代码,我相信是非常不言自明的,
我不是 C 方面的专家,必须将 error_desc 声明为 extern?以及如何?
error.h
#ifndef ERROR_H_
#define ERROR_H_
typedef enum error_e {
ERR_DB_CONN_FAIL, ERR_BAD_PARAM, ERR_NET_CONN, ERR_STR_TO_LONG
} error_e;
char *error_desc[] = {
"Fail to connect database",
"invalid parameter",
"Cannot connect to the network",
"String length exceed established limit",
};
char *error_str(error_e err);
#endif
error.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error.h"
unsigned get_arr_size(char **arr) {
int count = 1;
while (*++arr) {
++count;
}
return count;
}
char *error_str(error_e err) {
unsigned arr_size = get_arr_size(error_desc);
if ((unsigned)err < arr_size) {
return error_desc[err];
}
return "Invalid error code";
}
use-error.c
#include <stdio.h>
#include "error.h"
int main(void){
printf("Error: %s\n", error_str(ERR_NET_CONN));
printf("Error: %s\n", error_str(ERR_STR_TO_LONG));
printf("%s\n", error_str(6));
}
如果我将 error_str 的声明/初始化移动到 error.c 它可以工作,但我更喜欢
有error_e 和error_str 由于它们之间的“映射”关系。
我修改main.c:
#include "error.h"
int main(void){}
然后检查预处理器的输出并得到这个:
gcc use-error.c error.c -E > prep.c
prep.c
# 1 "use-error.c"
# 1 "<command-line>"
# 1 "use-error.c"
# 1 "error.h" 1
typedef enum error_e {
ERR_DB_CONN_FAIL, ERR_BAD_PARAM, ERR_NET_CONN, ERR_STR_TO_LONG
} error_e;
static char *error_desc[] = {
"Fail to connect database",
"invalid parameter",
"Cannot connect to the network",
"String length exceed established limit",
};
char *error_str(error_e err);
# 4 "use-error.c" 2
int main(void){
}
# 1 "error.c"
# 1 "<command-line>"
# 1 "error.c"
# 1 "error.h" 1
typedef enum errors {
ERR_DB_CONN_FAIL, ERR_BAD_PARAM, ERR_NET_CONN, ERR_STR_TO_LONG
} errors_e;
static char *error_desc[] = {
"Fail to connect database",
"invalid parameter",
"Cannot connect to the network",
"String length exceed established limit",
};
char *error_str(errors_e err);
# 2 "error.c" 2
static unsigned get_arr_size(char **arr) {
int count = 1;
while (*++arr) {
++count;
}
return count;
}
char *error_str(error_e err) {
unsigned arr_size = get_arr_size(error_desc);
if ((unsigned)err < arr_size) {
return error_desc[err];
}
return "Invalid error code";
}
看起来预处理器忽略了标头保护,但为什么呢?
顺便说一句,如果声明static(这没有多大意义,我将仅用于探索)
变量error_desc 代码被构建,但如果我在 gcc 上使用-Wall 选项,我会收到消息:
warning: ‘error_desc’ defined but not used [-Wunused-variable]
如果error.c 上的代码没有使用来自error.h 的error_desc,那么来自哪里?
【问题讨论】:
-
看来是时候了解一下translation units这个概念了。简而言之,一个翻译单元(或简称TU)是一个single源文件及其所有包含的头文件。这是编译器使用的“单元”。它对其他可能的 TU 一无所知。 linkers 的工作是获取所有 TU(以目标文件和库的形式)并生成最终的可执行文件。
-
您还需要了解声明和定义之间的区别。全局范围内的变量只需在所有 TU 中定义一次。不过,它可以声明多次。因此,为了解决您的问题,您在头文件中声明变量
error_desc,然后在单个源文件中定义它。 -
我还建议您购买一些不错的初学者书籍,这些书籍将详细解释这些相当基本的事情(以及更多内容)。
标签: c gcc preprocessor