c - 具有多个文件(头文件)的 C 项目
问题描述
我对 C 语言相当陌生,但已经获得了一些经验。现在我正在使用(或多或少)复杂的数据结构(例如 Map(我将使用 Maps 作为示例))创建更大的项目。因为我希望我的数据结构代码可以在未来的项目中重复使用,所以我喜欢它们相当通用并且在单独的文件中。
由于C不使用/没有泛型(如Java)或模板(如C++)或任何类似的概念,我考虑过使用全局定义的数据类型,如
typedef union {
int integer;
char * str;
// etc.
} data_t;
并将其放入main.h
将包含在所有其他(头)文件中的文件中(可能使用警卫)。这对我来说效果很好,但是……</p>
有没有办法将数据结构集成到我的data_t
(包括main.h
使用data_t
)中?
简单但显然不起作用(由于循环包含)的解决方案是#include "map.h"
inmain.h
同时还包括main.h
in map.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
解决方案
让您的声明和类型定义在一个源代码文件中工作。
当它编译时,想想在标题中放什么。这样,您可以确保首先看到编译器对您的标头包含的一个(每个)代码文件所看到的内容。
如果你不能让它在一个不包含的代码文件中工作,那么你就会遇到一个无法用 headers 和 guards 解决的问题。
但是,如果您可以做到,那么只需查看单个文件并思考“现在我还想在下一个代码文件中使用什么?” 并将其放入标题中。(当然要确保只将声明、类型定义、宏定义移动到标题中。代码、变量定义不进入标题。)
循环依赖不能由守卫解决,只能通过(间接)多重包含而重新定义。
推荐阅读
- swift - 使用 DateFormatter().date(from: String) 时返回的日期格式化程序
- laravel - laravel 5.7 多重身份验证电子邮件验证
- ruby - 使用 Ruby 解压有效负载
- flutter - 是否可以将颤振源图或源代码上传到哨兵以便更好地调试?
- c# - 我怎样才能使我的笑脸(用 Windows 窗体制作)相对,这意味着给它一个不同的 (x,y) 坐标,它会保持不变?
- c++ - 从 .msg 文件的标头流中解析日期
- c# - 获取符合特定条件的所有子实体
- visual-studio-2010 - 在 Visual Studio 中调试 C++ 标准库函数
- java - 使用 talend 归档所有日期(前一天和当天)的文件
- oracle - .net 的 Oracle ManagedDataAccess 驱动程序是否支持所有 Oracle 数据库版本 12c 18c 19c