【问题标题】:C project with multiple files (header files)具有多个文件(头文件)的 C 项目
【发布时间】:2021-02-28 23:26:44
【问题描述】:

我是 C 语言的新手,但已经获得了一些经验。现在我正在使用(或多或少)复杂的数据结构(例如 Map(我将使用 Maps 作为示例))创建更大的项目。由于我希望我的数据结构代码可在未来的项目中重复使用,因此我喜欢它们相当通用且位于单独的文件中。
由于 C 不使用/没有泛型(如 Java)或模板(如 C++)或任何类似的概念,我考虑过使用全局定义的数据类型,如

typedef union {
    int integer;
    char * str;
    // etc.
} data_t;

并将其放入main.h 中,该main.h 将包含在所有其他(头)文件中(可能使用警卫)。这对我来说效果很好,但是……

有没有办法将数据结构集成到我的data_t(包括main.h 以使用data_t)?
简单但显然不起作用(由于循环包含)的解决方案是 #include "map.h"main.h 同时还包括 main.hmap.h;如前所述,由于显而易见的原因,这不起作用。
基本上我想要一个可以容纳其他地图的地图,同时只使用一个data_t 和一个地图实现。跟踪我所在的“层”将在周围的程序中完成(或者我可以在data_t 中添加一些关于其类型的信息,这不是这里的重点)。 我知道仅使用void * 就可以做到这一点;但如果我不需要的话,我不希望对原始数据类型(如 int)进行不必要的引用。

有什么干净的方法来做这种行为吗?

谢谢!
(如果需要任何实际代码,请告诉我)

实际代码

main.h 我想包含像我的data_t 这样的一般声明:

#ifndef _MAIN_H_
#define _MAIN_H_

#include "map.h"

typedef union {
    int integer;
    char * str;
    map_t map;
} data_t;

#endif

map.h:

#ifndef _MAP_H_
#define _MAP_H_

#include "main.h"

typedef char * key_t;

typedef struct {
    int (*hash_f)(key_t);
    int size;
    data_t * data;
} map_t;

int map_init(map_t * map, int (*hash_f)(key_t key));
int map_put(map_t map, key_t key, data_t data);
int map_get(map_t map, key_t key);

#endif

make编译:

% make
gcc -Wall -Wextra -Wpedantic -g -c main.c -o build/main.o
In file included from main.c:1:
./main.h:8:5: error: unknown type name 'map_t'
    map_t map;
    ^
1 error generated.
make: *** [build/main.o] Error 1

【问题讨论】:

  • 请举例说明map.h 中的内容。为什么它需要main.h?也就是说,给出一个具体而完整的代码示例来说明您正在尝试做什么。用文字描述事物很少准确或清晰。
  • 对于特定的 stackoverflow 答案,您的问题可能过于宽泛。但无论如何,C 没有模板。对于模板,请使用 C++。
  • C 中的通用代码有两个选择是宏和 void *(第三个选项是外部代码生成)。
  • 如果您正在寻找避免在多个头文件和包装函数中使用数据类型的选项,则不清楚您在这里面临的确切问题是什么。使用“void*”来跨函数传递地址,并在需要的函数定义中将数据类型转换为所需的格式。并且只在“.c”文件中包含“.h”文件。
  • 在 C++ 中,您将具有相同的循环依赖关系,因为我了解您的问题。请给我们一个具体的例子(edit你提问并添加它),即使它不会编译。

标签: c project header-files


【解决方案1】:

让您的声明和类型定义在一个源代码文件中工作。
当它编译时,想想在标题中放什么。这样一来,您可以确保首先看到编译器对包含头文件的一个(每个)代码文件所看到的内容。

如果你不能让它在一个不包含的代码文件中工作,那么你就会遇到一个无法用 headers 和 guards 解决的问题。
但是,如果您可以做到,那么只需查看单个文件并思考“现在我还想在下一个代码文件中使用什么?”并将其放入标题中。 (当然要确保只将声明、类型定义、宏定义移动到标题中。代码、变量定义不进入标题。)

守卫不能解决循环依赖,只能通过(间接)多重包含来重新定义。

【讨论】:

    猜你喜欢
    • 2014-12-03
    • 2010-10-13
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-16
    • 2023-04-01
    相关资源
    最近更新 更多