为什么像 nginx 和 LuaJIT 这样的一些 C 项目会在所有代码文件、函数和数据类型前加上项目名称?

Why do some C projects like nginx and LuaJIT prefix all their code files, functions and data types with the project's name?

查看 nginx 的代码,我发现几乎所有内容都以 ngx_.

为前缀

文件:

ngx_list.c
ngx_list.h
ngx_log.c
ngx_log.h

代码:

ngx_log_t *ngx_log_init(u_char *prefix);
void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...);
void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...);

LuaJIT 与 lj_.

几乎相同

文件:

lj_alloc.c
lj_alloc.h
lj_api.c
lj_arch.h

代码:

LJ_ASMF void LJ_FASTCALL lj_vm_ffi_call(CCallState *cc);
LJ_FUNC CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o);
LJ_FUNC int lj_ccall_func(lua_State *L, GCcdata *cd);

其他项目做同样的事情,这只是我想到的两个。他们为什么这样做呢?如果它是项目的 public API 我会得到它,因为它将暴露给第三方代码。但是我复制的代码是(私有)实现的一部分,所以为什么要命名它?

我怀疑没有绝对的理由。我怀疑这只是人们(某些人)感觉更舒服的事情。我自己不会这样做,但我想我可以看到它的吸引力。

例如,我有一个多精度算术库,我有一天为了好玩而写的。它具有 mp_add()mt_sub() 等函数。这些函数的源代码位于文件 add.csub.c.

现在,由于该库的所有源代码都位于名为 mp 的子目录中,因此我从来没有想过给文件起 mp_add.cmp_sub.c 这样的名称。那将是多余的:这些名称已经具有非常真实的意义 mp/add.cmp/sub.c

但我不得不承认,使用名为 add.c 的文件来检查我的 multiprecision 添加代码确实感觉有点奇怪。它不是整数加法代码,也不是定点或有理数加法代码,也不是通用加法代码。它是非常具体的多精度加法代码,其中定义的函数都具有 mp_ 前缀。那么文件名不应该也有那个前缀吗?

正如我所说,不,最后我不会(我没有)给它那个前缀。但正如我也说过的,我想我可以看到吸引力。


附录:上面我回答了关于文件名的问题,但你也问了关于内部——"private"——函数名的问题。而那些是不同的;那些肯定需要一个前缀,至少在 C 中是这样。

问题是 C 实际上没有任何命名空间机制。因此,您几乎总是必须在所有全局符号上使用特定于项目的前缀来伪造它。

考虑函数 ngx_log_abort()。它是 nginx 私有的;客户端代码不会调用它。但它是一个全局函数,所以如果它只是命名为 log_abort,则很有可能与客户端代码(或其他一些库代码)中也命名为 [=] 的完全不同的函数发生冲突22=].

你可能会问,那为什么ngx_log_abort是一个全局函数呢?当然答案是组成 nginx 库的任何函数都可能需要调用它,所以它几乎必须是全局的。

您可能会问,那为什么 ngx_log_abort 不是文件范围的 static 函数?答案是,如果 如果 整个 nginx 库的所有源代码都限制在一个 C 源文件中 nginx.c,那将是可行的。但作者可能不想那样限制自己。

如果您想用 C 编写封装良好的库,您的 "private" 函数有两种选择:

  1. 将它们设为文件范围 static,并限制自己对大部分或所有库使用单个源文件。

  2. 使它们真正成为全球性的,但带有唯一前缀。也不要将它们的声明放在 public 头文件中。 (这样客户就不能在不作弊的情况下给他们打电话。)

在其他语言中,您有其他隐藏私有符号的机制,但在 C 中没有。