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

2022-09-20 00:00:00 c constants c++ 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
", s);
    return 0;
}

除了上面的问题,如果我删除原始代码中的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。

相关文章