如果它可能被更改,我应该将参数设置为指向 const 的指针吗?

Should I set argument to pointer to const if it might be changed?

例如,给定一个函数

void func(const int* a, int* b){
    *b = 2 + *a;
}

如果经常像这样调用 func,我应该发出 const 吗:func(ptr, ptr) ?

函数定义中的单词 const 表明该函数不会更改其地址作为第一个参数传递给该函数的对象。它说 a 是指向“常量整数”的指针。

因此,如果您的函数不更改 a 指向的对象,则保留 const.

是有意义的

虽然没有必要包含 const,但这样做是一种很好的做法。如果您不打算更改 a 指向的对象,但您的代码仍然试图修改 *a,这应该是您的编译器将检测到的错误。

简答:

如果函数应该在两个参数中使用相同的指针调用,则不应使用 const。否则,如果该函数不支持两个参数相同并且不应修改 a 指向的内容,则 const 应该作为“常量正确性”的一部分存在。


长答案:

const 不保证指针是唯一的别名。相反,de-referencing a const int* 被允许作为 de-referencing an int* 的别名作为 so-called“严格别名规则”的例外之一,请参阅C17 6.5 以下的例外列表:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
...

  • a qualified version of a type compatible with the effective type of the object

在实践中,请查看此示例:

#include <stdio.h>

void func(const int* a, int* b)
{
    int tmp = *a;
    printf("%d\n", tmp);

    *b = 2 + *a;

    tmp = *a;
    printf("%d\n", tmp);
}

int main (void)
{
    int x = 1;
    int* ptr=&x;
    func(ptr,ptr);
    printf("%d\n",*ptr);
}

使用 gcc 11.2 或 clang 14.0.0 x86_64 -O3 编译,打印:

1
3
3

也就是说,tmp 中的值被重新加载,因为编译器无法假定左值 *a 没有被前面的代码修改。

如果我们将此代码更改为:

void func(const int* restrict a, int* b)

则输出变为:

1
1
3

restrict 是编译器和程序员之间的契约,所有对 a 指向的内容的访问都通过指针 a。由于编译器现在可以自由假设 ab 没有别名,它可以优化掉 tmp = *a; 行,因为 tmp 已经包含 [=22] 的值=].

在这个具体的例子中,程序员通过将函数调用为 func(ptr,ptr) 来欺骗编译器,尽管参数被 restricted 并且在函数内部打印 1 的意外结果是由来电号码。