使用指向非 const 的指针和指向相同地址的 const 参数的函数调用

Function call with pointer to non-const and pointer to const arguments of same address

我想编写一个函数,使用指针输入一个数据数组并输出另一个数据数组。

我想知道如果 srcdst 都指向同一个地址,结果是什么,因为我知道编译器可以针对 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* ptrptr指向的内存将不会通过另一个指针改变。指针甚至不必相等。 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。如果srcdst指向同一个对象:

  • 在C中,它们必须是compatible typeschar*char const* 不兼容。
  • 在C++中,它们必须是similar typeschar*char const* 相似。

关于 const 限定符,您可能会争辩说,因为当 dst == src 您的函数有效地修改了 src 指向的内容时,src 不应被限定为 const。这不是 const 的工作方式。需要考虑两种情况:

  1. 当对象被定义为 const 时,如 char const data[42];,对其进行修改(直接或间接)会导致未定义的行为。
  2. 当定义了指向 const 对象的引用或指针时,如在 char const* pdata = data; 中,可以修改基础对象,前提是它尚未定义为 const2(见 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