为什么标准库(以及其他库)设计成这样?为什么现代代码中没有看到“const size_t”或“const int”?
C使用值传递。将函数参数标记为const
对编译器不会有任何帮助(请注意,memcmp()
的所有参数都不是const
。指针参数也可以声明为const
,你可能建议它们应该这样做:int memcmp(const void * const s1, const void * const s2, size_t const n);
。但它们不是)。
之所以将函数参数标记为const
对编译器没有帮助,是因为从函数的角度来看,函数参数仅仅是局部变量。只要函数没有获取参数的地址,编译器很容易看出该变量永远不会被修改。
相比之下,const
修饰符作为memcmp()
原型(const void *s1
)的一部分,是其契约的一部分:它表明该函数不修改指向的数据。永远不会像这样使用const
修饰符来处理参数本身,因为调用者不关心函数是否修改其参数:它们只是副本(同样地,因为C使用值传递)。
这里的const
有不同的含义。在
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
const void * ptr1
表示 memcmp
将把 ptr1
视为指向常量数据的指针,并且不会修改它; 对于 const void * ptr2
同样适用。 因此,调用者知道存储的值不会被更改,并可以进行相应的优化。在类似下面的函数调用中:
int result = memcmp(ptr1, ptr2, num);
变量 ptr1
、ptr2
和 num
被复制到函数中。 memcmp
不保证不调整它们,它只承诺不调整指针所指向的内容。实际上,为了遍历数组,它可能会增加/减少任何一个被复制的变量,以提高效率。如果它想承诺不改变其中任何一个,声明应该是:
int memcmp ( const void *const ptr1, const void *const ptr2, const size_t num );
对于简单数据类型(如指针和整数),这种方式几乎无法获得任何优化,此函数(以及其他函数)的原始规范显然没有理由阻止实现在某些情况下修改变量。
这样做的主要原因是为了保持库的一致性。将size_t
参数更改为const size_t
将需要重写修改大小的库。并非所有库的实现都需要使用相同的算法。这是现有库函数未被调整的主要原因。
通常会故意创建具有非const
参数的新库函数,以便某些机器相关的实现可以使用可修改的参数。
例如,Intel C++编译器版本的memcmp
在执行期间实际上会递减length
参数。其他一些实现可能不会这样做。
const
限定符。const
参数对函数的定义唯一的限制是你不能在函数内部更改它。对于声明来说,这并没有任何区别。 - Jens Gustedt
memcmp()
确实具有const
指针。 - dongle26const void *
,对吧?那就是“指向常量”的意思。指针本身不是常量。 - Pascal Cuoq