使用指向非 const 的指针和指向相同地址的 const 参数的函数调用
Function call with pointer to non-const and pointer to const arguments of same address
我想编写一个函数,使用指针输入一个数据数组并输出另一个数据数组。
我想知道如果 src
和 dst
都指向同一个地址,结果是什么,因为我知道编译器可以针对 const 进行优化。它是未定义的行为吗? (我标记了 C 和 C++,因为我不确定它们之间的答案是否不同,我想了解两者。)
void f(const char *src, char *dst) {
dst[2] = src[0];
dst[1] = src[1];
dst[0] = src[2];
}
int main() {
char s[] = "123";
f(s,s);
printf("%s\n", s);
return 0;
}
除了上面的问题,如果我把原来代码中的const
删掉,这样定义好吗?
虽然该行为确实定义明确 - 不是编译器可以在您所说的意义上“针对 const 进行优化”。
也就是说,编译器是不 允许假设仅因为参数是const T* ptr
,ptr
指向的内存将不会通过另一个指针改变。指针甚至不必相等。 const
是一项义务,而不是保证 - 您(= 函数)有义务不通过该指针进行更改。
为了真正得到保证,您需要用 restrict
关键字标记指针。因此,如果你编译这两个函数:
int foo(const int* x, int* y) {
int result = *x;
(*y)++;
return result + *x;
}
int bar(const int* x, int* restrict y) {
int result = *x;
(*y)++;
return result + *x;
}
foo()
函数必须从x
读取两次,而bar()
只需要读取一次:
foo:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, DWORD PTR [rdi] # second read
ret
bar:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, eax # no second read
ret
在 GodBolt 上观看直播。
restrict
只是 C 中的关键字(自 C99 起);不幸的是,到目前为止它还没有被引入到 C++ 中(因为在 C++ 中引入更复杂的可怜的原因)。然而,许多编译器有点支持它,如 __restrict
.
底线:编译器在编译时必须支持您的“深奥”用例f()
,并且不会有任何问题。
有关 restrict
的用例,请参阅 this post。
这是明确定义的(在 C++ 中,在 C 中不再确定),有和没有 const
限定符。
首先要寻找的是严格的别名规则1。如果src
和dst
指向同一个对象:
- 在C中,它们必须是compatible types;
char*
和 char const*
不兼容。
- 在C++中,它们必须是similar types;
char*
和 char const*
相似。
关于 const
限定符,您可能会争辩说,因为当 dst == src
您的函数有效地修改了 src
指向的内容时,src
不应被限定为 const
。这不是 const
的工作方式。需要考虑两种情况:
- 当对象被定义为
const
时,如 char const data[42];
,对其进行修改(直接或间接)会导致未定义的行为。
- 当定义了指向
const
对象的引用或指针时,如在 char const* pdata = data;
中,可以修改基础对象,前提是它尚未定义为 const
2(见 1.)。所以以下是明确定义的:
int main()
{
int result = 42;
int const* presult = &result;
*const_cast<int*>(presult) = 0;
return *presult; // 0
}
1) What is the strict aliasing rule?
2)Is const_cast
safe?
这在 C 中有明确定义。严格的别名规则不适用于 char
类型,也不适用于同一类型的两个指针。
我不确定 "optimize for const
" 是什么意思。我的编译器 (GCC 8.3.0 x86-64) 为这两种情况生成了完全相同的代码。如果您将 restrict
说明符添加到指针,那么生成的代码会稍微好一些,但这对您的情况不起作用,指针是相同的。
(C11 §6.5 7)
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its
members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
在这种情况下(没有 restrict
),结果总是 121
。
我想编写一个函数,使用指针输入一个数据数组并输出另一个数据数组。
我想知道如果 src
和 dst
都指向同一个地址,结果是什么,因为我知道编译器可以针对 const 进行优化。它是未定义的行为吗? (我标记了 C 和 C++,因为我不确定它们之间的答案是否不同,我想了解两者。)
void f(const char *src, char *dst) {
dst[2] = src[0];
dst[1] = src[1];
dst[0] = src[2];
}
int main() {
char s[] = "123";
f(s,s);
printf("%s\n", s);
return 0;
}
除了上面的问题,如果我把原来代码中的const
删掉,这样定义好吗?
虽然该行为确实定义明确 - 不是编译器可以在您所说的意义上“针对 const 进行优化”。
也就是说,编译器是不 允许假设仅因为参数是const T* ptr
,ptr
指向的内存将不会通过另一个指针改变。指针甚至不必相等。 const
是一项义务,而不是保证 - 您(= 函数)有义务不通过该指针进行更改。
为了真正得到保证,您需要用 restrict
关键字标记指针。因此,如果你编译这两个函数:
int foo(const int* x, int* y) {
int result = *x;
(*y)++;
return result + *x;
}
int bar(const int* x, int* restrict y) {
int result = *x;
(*y)++;
return result + *x;
}
foo()
函数必须从x
读取两次,而bar()
只需要读取一次:
foo:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, DWORD PTR [rdi] # second read
ret
bar:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, eax # no second read
ret
在 GodBolt 上观看直播。
restrict
只是 C 中的关键字(自 C99 起);不幸的是,到目前为止它还没有被引入到 C++ 中(因为在 C++ 中引入更复杂的可怜的原因)。然而,许多编译器有点支持它,如 __restrict
.
底线:编译器在编译时必须支持您的“深奥”用例f()
,并且不会有任何问题。
有关 restrict
的用例,请参阅 this post。
这是明确定义的(在 C++ 中,在 C 中不再确定),有和没有 const
限定符。
首先要寻找的是严格的别名规则1。如果src
和dst
指向同一个对象:
- 在C中,它们必须是compatible types;
char*
和char const*
不兼容。 - 在C++中,它们必须是similar types;
char*
和char const*
相似。
关于 const
限定符,您可能会争辩说,因为当 dst == src
您的函数有效地修改了 src
指向的内容时,src
不应被限定为 const
。这不是 const
的工作方式。需要考虑两种情况:
- 当对象被定义为
const
时,如char const data[42];
,对其进行修改(直接或间接)会导致未定义的行为。 - 当定义了指向
const
对象的引用或指针时,如在char const* pdata = data;
中,可以修改基础对象,前提是它尚未定义为const
2(见 1.)。所以以下是明确定义的:
int main()
{
int result = 42;
int const* presult = &result;
*const_cast<int*>(presult) = 0;
return *presult; // 0
}
1) What is the strict aliasing rule?
2)Is const_cast
safe?
这在 C 中有明确定义。严格的别名规则不适用于 char
类型,也不适用于同一类型的两个指针。
我不确定 "optimize for const
" 是什么意思。我的编译器 (GCC 8.3.0 x86-64) 为这两种情况生成了完全相同的代码。如果您将 restrict
说明符添加到指针,那么生成的代码会稍微好一些,但这对您的情况不起作用,指针是相同的。
(C11 §6.5 7)
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
在这种情况下(没有 restrict
),结果总是 121
。