如果它可能被更改,我应该将参数设置为指向 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
。由于编译器现在可以自由假设 a
和 b
没有别名,它可以优化掉 tmp = *a;
行,因为 tmp
已经包含 [=22] 的值=].
在这个具体的例子中,程序员通过将函数调用为 func(ptr,ptr)
来欺骗编译器,尽管参数被 restrict
ed 并且在函数内部打印 1 的意外结果是由来电号码。
例如,给定一个函数
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
。由于编译器现在可以自由假设 a
和 b
没有别名,它可以优化掉 tmp = *a;
行,因为 tmp
已经包含 [=22] 的值=].
在这个具体的例子中,程序员通过将函数调用为 func(ptr,ptr)
来欺骗编译器,尽管参数被 restrict
ed 并且在函数内部打印 1 的意外结果是由来电号码。