【问题标题】:Header guards ignored by preprocessor using gcc [duplicate]使用 gcc 的预处理器忽略标头保护 [重复]
【发布时间】: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_eerror_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.herror_desc,那么来自哪里?

【问题讨论】:

  • 看来是时候了解一下translation units这个概念了。简而言之,一个翻译单元(或简称TU)是一个single源文件及其所有包含的头文件。这是编译器使用的“单元”。它对其他可能的 TU 一无所知。 linkers 的工作是获取所有 TU(以目标文件和库的形式)并生成最终的可执行文件。
  • 您还需要了解声明定义之间的区别。全局范围内的变量只需在所有 TU 中定义一次。不过,它可以声明多次。因此,为了解决您的问题,您在头文件中声明变量error_desc,然后在单个源文件中定义它。
  • 我还建议您购买一些不错的初学者书籍,这些书籍将详细解释这些相当基本的事情(以及更多内容)。

标签: c gcc preprocessor


【解决方案1】:

标头保护仅保护标头不被多次包含在同一个文件中。在这种情况下,这意味着“error.h”将在“prep.c”和“error.c”中最多包含一次,导致他们每个人都有自己的 error_desc 副本,这会导致到你的编译错误。

使 error_desc 静态修复此问题的原因是因为它告诉编译器使其在其编译所在的文件之外不可见,从而消除了重复的定义冲突。您收到警告是因为您在“prep.c”和“error.c”中都有一个 error_desc 版本,但从未使用过“prep.c”中的版本。

要解决此问题,您应该将 error_desc 的定义移动到“error.c”,或者如果您希望 error_desc 可用于任何包含“error.c”的文件。 h" 改变

static char *error_desc[] = {
  "Fail to connect database",
  "invalid parameter",
  "Cannot connect to the network",
  "String length exceed established limit",
};

在“error.h”中

extern char *error_desc[4];

并在“error.c”中添加

char *error_desc[] = {
  "Fail to connect database",
  "invalid parameter",
  "Cannot connect to the network",
  "String length exceed established limit",
};

【讨论】:

    猜你喜欢
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 2021-05-02
    • 1970-01-01
    • 2014-01-02
    相关资源
    最近更新 更多