编译器优化:C语言中非指针函数参数上的const

4
现代编译器在看到“const”时可以优化代码。然而,我从未见过C标准库在其非指针参数中使用“const”。例如,“memcmp()”就是一个例子。它有两个“const void *”参数,但第三个参数是“size_t”。
为什么标准库(以及其他库)设计成这样?为什么现代代码中没有看到“const size_t”或“const int”?
3个回答

7

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使用值传递)。


我的Linux man页显示memcmp()确实具有const指针。 - dongle26
1
@dongle26它说的是const void *,对吧?那就是“指向常量”的意思。指针本身不是常量。 - Pascal Cuoq

5

这里的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);

变量 ptr1ptr2num复制到函数中。 memcmp 不保证不调整它们,它只承诺不调整指针所指向的内容。实际上,为了遍历数组,它可能会增加/减少任何一个被复制的变量,以提高效率。如果它想承诺不改变其中任何一个,声明应该是:

int memcmp ( const void *const ptr1, const void *const ptr2, const size_t num );

对于简单数据类型(如指针和整数),这种方式几乎无法获得任何优化,此函数(以及其他函数)的原始规范显然没有理由阻止实现在某些情况下修改变量。


0

这样做的主要原因是为了保持库的一致性。将size_t参数更改为const size_t将需要重写修改大小的库。并非所有库的实现都需要使用相同的算法。这是现有库函数未被调整的主要原因。

通常会故意创建具有非const参数的新库函数,以便某些机器相关的实现可以使用可修改的参数。

例如,Intel C++编译器版本的memcmp在执行期间实际上会递减length参数。其他一些实现可能不会这样做。


1
不,你的回答没有多大意义。将函数的值参数设置为“const”对你毫无益处。 - Jens Gustedt
是的。如果编译器决定内联一个函数,那么const参数就不会被复制。 - Sergey L.
如果现代编译器内联一个函数,它会进行常量传播,无论参数是否带有const限定符。const参数对函数的定义唯一的限制是你不能在函数内部更改它。对于声明来说,这并没有任何区别。 - Jens Gustedt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接