带有指向非常量的指针和指向相同地址的常量参数的指针的函数调用

Wil*_*lly 15 c c++ constants undefined-behavior

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

我想知道如果两者都指向同一个地址会产生什么结果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;
}
Run Code Online (Sandbox Code Playgroud)

除了上面的问题,如果我删除const原来的代码,这个定义是否明确?

ein*_*ica 17

虽然行为是明确定义的,但编译器不能按照您的意思“针对 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;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

看到这个直播GodBolt

restrict只是 C 中的关键字(自 C99 起);不幸的是,到目前为止它还没有被引入到 C++ 中(因为在 C++ 中引入它更复杂)。然而,许多编译器确实有点支持它,因为__restrict.

底线:编译器在编译时必须支持您的“深奥”用例f(),并且不会有任何问题。


请参阅这篇文章关于用例restrict

  • @EricPostpischil:1.你在这里吹毛求疵。2. 这不是真的。 (2认同)

YSC*_*YSC 5

这是明确定义的(在 C++ 中,在 C 中不再确定),带和不带const限定符。

首先要寻找的是严格的别名规则1。如果srcdst指向同一个对象:

  • 在 C 中,它们必须是兼容的类型char*并且char const*不兼容。
  • 在 C++ 中,它们必须是相似的类型char*并且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
}
Run Code Online (Sandbox Code Playgroud)

1) 什么是严格的别名规则?
2) const_cast安全的吗?