首先,让我声明我理解以下两者之间的区别:
const char *a;
和
char * const a;
和
const char * const a;
然而,最近我在用于将参数传递给函数时反复遇到后者,举个例子,Maxmind GeoLite2 mmdb C API 用于读取数据库文件,对每个指针参数使用以下格式:
const T * const
例: https://github.com/maxmind/libmaxminddb/blob/master/include/maxminddb.h#L203
extern int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb);
我对 C/C++ 和 C/C++ 经常编译成的 x86 或 x86-64 有深入的了解,我知道指向文件名的指针和指向MMDB_s的指针都是堆栈分配的副本(或者在 x86-64 的情况下通过寄存器传递),那么使指针恒定的目的是什么?
我在这里错过了什么,还是他们不必要地用常量指针淹没了他们的 API,这些指针对使用它们的人真的没有最终影响?
C 标准明确指出,在函数原型声明中忽略顶级const
限定符。 (N1570 §6.7.6.3p15,段落末尾括号中的句子。 这意味着,从代码试图调用MMDB_open
的角度来看,声明
extern int MMDB_open(const char *const filename, uint32_t flags,
MMDB_s *const mmdb);
和
extern int MMDB_open(const char *filename, uint32_t flags,
MMDB_s *mmdb);
在语义上是相同的。 所以你认为这些限定符是不必要的是正确的。
但是,它们并非毫无意义,因为从功能实现的角度来看,它们是不一样的。 在MMDB的代码中,他们在某处有这个函数定义
int
MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb)
{
// code here
}
并且这个函数体中的代码不允许修改filename
和mmdb
变量(但允许通过mmdb
写入并修改它指向的内容)。这就是为什么他们首先把限定符放在这些变量上的原因。
他们可能将限定符复制到公共头文件中的原型中,因为他们希望原型与函数定义完全匹配。 这使得维护头文件更容易。