假设我们有一个int
变量,其引用为const int*
,而别名又是int *
。从标准中是否清楚通过int *
指针修改变量是否是未定义的行为?
作为说明,请考虑以下代码:
void increment(int* p) {
(*p)++;
}
void call_increment(const int* p) {
increment(p);
}
int main(void) {
int x = 7;
int* p = &x;
call_increment(p);
}
答案 0 :(得分:8)
通过指向const
的指针修改对象是不正确的行为,不是不确定的行为。
除非引用的对象实际上是const
,否则通过丢弃const
可以解决此问题。
您的代码有另一个问题:
将const
从p
传递到call_increment()
时,您将丢弃increment()
限定符。
任何有用的编译器都会抱怨even without being prompted:
g++ -x c -std=c18 main.cpp && ./a.out
main.cpp: In function 'call_increment':
main.cpp:6:15: warning: passing argument 1 of 'increment' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
increment(p);
^
main.cpp:1:21: note: expected 'int *' but argument is of type 'const int *'
void increment(int* p) {
~~~~~^
请注意,最好至少提供-Wall -Wextra
。
答案 1 :(得分:2)
C中的const
限定符指定不使用左值来修改对象,但通常不会说明该对象是否可以其他方式(包括通过从派生的非const限定的指针)进行修改。有问题的左值。
两种明显的情况确实与对象有关:
如果声明了顶级左值const
但未声明volatile
,则将没有定义的方法可以更改其值,并且标准将邀请实现自由假定不会的。如果顶级左值是const volatile
,则标准将不会预期通过任何特定方式来更改值,但是质量实现通常应考虑到可能通过对值一无所知而自发更改值的可能性。
如果指向const
对象的指针是合格的restrict
,则通过该指针观察到的任何对象或从存储在其中的地址派生的任何左值在整个有效生命周期内必须具有相同的值。指针。因此,例如
int test(int const *restrict p)
{
if (*p == 1)
{
doSomething(p);
return *p;
}
else
return 1;
}
将允许编译器生成返回1而无需重新加载*p
的代码,但是如果没有restrict
限定符,则不允许编译器这样做。例如,
int test(int const *p)
{
int const * restrict pp = p;
if (*pp == 1) // Restrict-qualified pointer used to observe value
{
doSomething(pp);
return *pp;
}
else
return 1;
}
但不是
int test(int const *p)
{
int const * restrict pp = p;
if (*p == 1)
{
doSomething(pp);
return *p;
}
else
return 1;
}
如果先前已将p
的副本存储在全局对象中,并且doSomething
将完全忽略pp
,则对*p
所做的更改将不会影响通过从pp
派生的指针。
如果要通过指示通过指针标识的对象永远不会改变来最大化优化,则除了将对象标识为restrict
之外,通常还应将指针限定为const
。