C 中的函数前缀与 "Function Struct"
Function Prefix vs "Function Struct" in C
我正在尝试找出 C(11) 中的最佳实践。因为没有名称空间,所以我可以想到两种避免名称冲突的方法:
a.) 函数前缀
void kernel_init(void) { ... }
int kernel_start(void* foo) { ... }
b.) "Function structs"
struct kernel {
void (*init)(void);
int (*start)(void* foo);
} kernel;
我不是在问哪种方法更漂亮,因为那是相当主观的。我要问的是 除了代码风格之外,这两种方法是否有任何明显的缺点? 这包括起初不相关的小问题,但一旦代码库增长就会成为更大的问题。
有趣的是,我以前从未想过这种解决方案。
第一个当然是标准的,我敢打赌你会在绝大多数 C 项目中找到它[*]。
第二个理论上占用内存,因为您实际上是在声明一个充满函数指针的数据对象。您当然还需要初始化 kernel
变量,即有类似的东西:
...
} kernel = {
.init = kernel_init,
.start = kernel_start,
};
但是哦,你又用前缀函数了。为了消除对这些的需求,函数必须是 static
,我想如果您将 extern
添加到 kernel.h
.[=17 中的 struct
声明中,这是可能的=]
所以一个更完整的例子可以是:
// kernel.h (public header)
typedef struct kernel_api {
void (*init)(void);
int (*start)(void* foo);
} kernel_api;
extern const kernel_api kernel;
// in kernel.c
static void init(void)
{
...
}
static int start(void *foo)
{
..
}
const kernel_api kernel = {
.init = init,
.start = start,
};
这可能有用,但我还没试过。
最后,拥有显式数据意味着需要足够智能的编译器来优化这些数据并进行更直接的调用,但我还没有尝试过,依赖它有点冒险。有意思。
[*] 我想我只是在统计上声称我已经看到(或想到)了世界上绝大多数的 C 项目,但这当然不是真的。 :)
功能版本是最常用的。
结构版本的缺点是它不是独立的。函数指针应该从 "constructor" 设置,而不是从调用者设置,因为那样会违反私有封装设计实践。你会给你的构造函数起什么名字?无论如何你都需要一个前缀函数。
像这样的结构通常只在你想实现多态性或者你有回调函数的时候使用。一些编码风格也使用结构来模拟 C++ class 成员,但这是否真的是好的做法是值得商榷的。
最佳 OO 实践需要第三个版本,使用不透明指针,这意味着您需要函数和结构:
typedef struct kernel_t kernel_t;
kernel_t* kernel_init (void) { ... }
int kernel_start (kernel_t* this, ...) { ... }
其中结构定义仅对 "kernel.c" 可见,对调用者不可见。
我正在尝试找出 C(11) 中的最佳实践。因为没有名称空间,所以我可以想到两种避免名称冲突的方法:
a.) 函数前缀
void kernel_init(void) { ... }
int kernel_start(void* foo) { ... }
b.) "Function structs"
struct kernel {
void (*init)(void);
int (*start)(void* foo);
} kernel;
我不是在问哪种方法更漂亮,因为那是相当主观的。我要问的是 除了代码风格之外,这两种方法是否有任何明显的缺点? 这包括起初不相关的小问题,但一旦代码库增长就会成为更大的问题。
有趣的是,我以前从未想过这种解决方案。
第一个当然是标准的,我敢打赌你会在绝大多数 C 项目中找到它[*]。
第二个理论上占用内存,因为您实际上是在声明一个充满函数指针的数据对象。您当然还需要初始化 kernel
变量,即有类似的东西:
...
} kernel = {
.init = kernel_init,
.start = kernel_start,
};
但是哦,你又用前缀函数了。为了消除对这些的需求,函数必须是 static
,我想如果您将 extern
添加到 kernel.h
.[=17 中的 struct
声明中,这是可能的=]
所以一个更完整的例子可以是:
// kernel.h (public header)
typedef struct kernel_api {
void (*init)(void);
int (*start)(void* foo);
} kernel_api;
extern const kernel_api kernel;
// in kernel.c
static void init(void)
{
...
}
static int start(void *foo)
{
..
}
const kernel_api kernel = {
.init = init,
.start = start,
};
这可能有用,但我还没试过。
最后,拥有显式数据意味着需要足够智能的编译器来优化这些数据并进行更直接的调用,但我还没有尝试过,依赖它有点冒险。有意思。
[*] 我想我只是在统计上声称我已经看到(或想到)了世界上绝大多数的 C 项目,但这当然不是真的。 :)
功能版本是最常用的。
结构版本的缺点是它不是独立的。函数指针应该从 "constructor" 设置,而不是从调用者设置,因为那样会违反私有封装设计实践。你会给你的构造函数起什么名字?无论如何你都需要一个前缀函数。
像这样的结构通常只在你想实现多态性或者你有回调函数的时候使用。一些编码风格也使用结构来模拟 C++ class 成员,但这是否真的是好的做法是值得商榷的。
最佳 OO 实践需要第三个版本,使用不透明指针,这意味着您需要函数和结构:
typedef struct kernel_t kernel_t;
kernel_t* kernel_init (void) { ... }
int kernel_start (kernel_t* this, ...) { ... }
其中结构定义仅对 "kernel.c" 可见,对调用者不可见。