【问题标题】:How to structure my c code to build up functionality如何构建我的 c 代码以构建功能
【发布时间】:2019-03-01 17:01:44
【问题描述】:

我是 c 编程语言的新手,从另一篇文章中看到,一旦将头文件包含到另一个头文件中,就不会保留头文件保护。我认为这意味着如果我要将第二个头文件包含到第三个头文件中,我会遇到符号重新定义问题。我决定使用另一篇文章中的示例代码进行尝试,是的,我确实收到了错误。我附上了下面的代码。是否因为 foo.h 包含定义而不仅仅是声明而出现此错误? foo.h 中定义的结构的定义应该在 foo.c 中吗?如果是这样,我应该使用什么方法让多个编译单元建立在前一个功能的基础上?在堆栈架构中,各层如何访问前一层的功能(它们是否包含头文件或遵循其他方法)?

/*
 * foo.h
 *
 */

#ifndef FOO_H_
#define FOO_H_

#include  "stdint.h"

uint8_t test();

typedef struct regVal{
    uint16_t add;
    uint8_t val;
}reg;

const struct regVal regArr[] =
 {
     {0x3103,0x03},
     {0x3008,0x82},
     {0xFFFF,0xFF}
 };

#endif /* FOO_H_ */

/*
 * foo.c
 */

#include "foo.h"
#include  "stdint.h"

uint8_t test();


uint8_t test(){
    return 0x01;
}

/*
 * foo2.h
 *
 */

#ifndef FOO2_H_
#define FOO2_H_

#include "foo.h"
#include  "stdint.h"



#endif /* FOO2_H_ */

/*
 * foo3.h
 *
 */

#ifndef FOO3_H_
#define FOO3_H_

#include "foo2.h"
#include "stdint.h"



#endif /* FOO3_H_ */

/*
 * foo3.c
 */

#include "foo3.h"
#include "stdint.h"

这是链接器错误

 >symbol "regArr" redefined: first defined in "./foo.obj"; redefined in "./foo3.obj" null: symbol "regArr" redefined: first defined in "./foo.obj"; redefined in "./foo3.obj"

【问题讨论】:

  • 您的头球后卫没问题,您遇到的问题与头球后卫无关。全局变量必须在 .c 文件中声明,而不是在头文件中。在您的情况下,应该声明 regArr,例如在foo3.c
  • 我可以在 foo.c 中声明 regArr 吗?
  • 是的,您可以在需要的C文件中声明。
  • @kial 不,你必须 define 它,而不仅仅是 declare 它(见我的回答)

标签: c header


【解决方案1】:

符号“regArr”重新定义:首先在“./foo.obj”中定义;在“./foo3.obj”中重新定义 null:符号“regArr”重新定义:首先在“./foo.obj”中定义;在“./foo3.obj”中重新定义

在头文件中你只需要声明变量和函数,而不是定义它们(如果文件只被#included一次,这是可能的,但这是一个糟糕的选择)

foo.h 被#included 多次,每次你重新定义regArr

动起来

const struct regVal regArr[] =
{
     {0x3103,0x03},
     {0x3008,0x82},
     {0xFFFF,0xFF}
};

int foo.c

【讨论】:

  • 嗨布鲁诺。非常感谢您的澄清
  • @kial,如果您只 declare 它(而不是 define 它),您将遇到相反的错误:找不到符号(而不是重新定义)
  • 嗨布鲁诺。非常感谢您的澄清。当我将 regArry[] 移动到 foo.c 时, foo.h 是否应该包含它的声明,如 const struct regVal regArr[]: ??
  • @kial 都取决于是否在其他地方使用了 regArr。如果 regArr 只在 foo.c 中使用,则不要在 foo.h 中声明,否则在 foo.h
  • 不幸的是,它在其他地方使用。在 foo.h 中声明的 const struct regVal regArry[] 的接口如何?
【解决方案2】:

首先让我解释一下什么是编译单元。在 c 中,每个单独的 .c 文件都被单独编译以得到一个 .o 文件,然后将所有 .o 文件链接起来以生成最终的可执行文件。

现在让我们看看如何处理包含文件。所有包含在 c 文件中的包含文件都会在预处理阶段复制到该 c 文件中。要看到这个,你可以运行 gcc -E your-c-file.c -o out.i 您可以检查输出文件以了解我的意思。

因此,当您在 foo.c 中包含 foo.h 时,它会被包含在内,并且在编译阶段会创建一个 regArr[] 实例。 现在,当编译 foo3.c 时,会在此编译单元中创建另一个 regArr[] 实例,因为最终 foo.h 也包含在其中。请注意,所有编译单元都是独立的。现在,当链接器尝试链接两个目标文件时,它会引发此错误,因为在 c 中没有两个变量或函数可以具有相同的名称。

【讨论】:

  • 所以如果我将 regArr[] 移动到 foo.c 中并简单地在 foo.h 中声明它的接口,那么它将不会在下游编译单元中编译。你的解释很有道理,我要试试。非常感谢您不厌其烦地帮助我。我真的很感激。
  • 我认为 extern qualifier 会帮助你。在 c 中,当我们想要访问在其他文件中定义的函数时,我们只需声明它们以告诉编译器它们将在链接器阶段解析,但对于变量我们使用 extern 关键字。所以在 foo.c 中你声明了这个变量,当你想在另一个文件中访问它时,你只需在前面加上 extern 关键字,告诉编译器请耐心等待它存在于另一个编译单元中,链接器会解析它。跨度>
  • 我可以使用“extern const struct regVal *regName;”并在从另一个单元访问它时使用 regArr[] 提供填充 *regName 吗?
  • 我查看了您发送的链接。它建议 - 在其他文件中可见的所有内容都在 h 文件中定义(例如 typedef struct {...} 如果被两个或多个 c 文件使用,则在 h 文件中)。全局(用于所有 c 文件)在 global.h 中。这不是我要避免的问题吗?
  • 它表示结构定义保留在头文件中,并在 c 文件中创建实例。这样当你做 extern const struct regVal *regVal;在 foo3.c 中它不会抱怨未定义的结构类型 regVal。
猜你喜欢
  • 2018-11-19
  • 1970-01-01
  • 2022-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-31
  • 1970-01-01
相关资源
最近更新 更多