假设指向同一变量的两个指针是非法的/UB,为什么C编译器不能优化更改const指针的值?

最近我偶然发现了 Rust 和 C 之间的比较,它们使用以下代码:

bool f(int* a, const int* b) {
  *a = 2;
  int ret = *b;
  *a = 3;
  return ret != 0;
}

在 Rust(相同的代码,但使用 Rust 语法)中,它生成以下汇编代码:

    cmp      dword ptr [rsi], 0 
    mov      dword ptr [rdi], 3 
    setne al                    
    ret

使用 gcc 时,它会产生以下结果:

   mov      DWORD PTR [rdi], 2   
   mov      eax, DWORD PTR [rsi]
   mov      DWORD PTR [rdi], 3        
   test     eax, eax                  
   setne al                           
   ret

文本声称 C 函数不能优化第一行,因为ab可能指向相同的数字。在 Rust 中这是不允许的,因此编译器可以将其优化掉。

现在我的问题:

该函数采用一个const int*其为一个指向const int的。我读了这个问题,它指出用指针修改 const int 应该导致编译器警告和 UB 中最糟糕的强制转换。

如果我用两个指向同一个整数的指针调用它,这个函数会导致 UB 吗?

为什么 C 编译器不能优化第一行,假设指向同一个变量的两个指针是非法的/UB?

链接至神箭

回答

为什么 C 编译器不能优化第一行,假设指向同一个变量的两个指针是非法的/UB?

因为您没有指示 C 编译器这样做 - 允许做出这种假设。

C 有一个类型限定符,正好用于 this 调用restrict,大致意思是:this 指针不与其他指针重叠(不完全是,而是一起玩)。

汇编输出为

bool f(int* restrict a, const int* b) {
  *a = 2;
  int ret = *b;
  *a = 3;
  return ret != 0;
}

        mov     eax, DWORD PTR [rsi]
        mov     DWORD PTR [rdi], 3
        test    eax, eax
        setne   al
        ret

...删除了分配 *a = 2

来自https://en.wikipedia.org/wiki/Restrict

在 C 编程语言中,restrict 是一个可以在指针声明中使用的关键字。通过添加这个类型限定符,程序员向编译器提示,在指针的生命周期内,只有指针本身或直接从它派生的值(例如指针 + 1)将用于访问它指向的对象。

  • Strictly speaking, all Rust references are `restrict`, even the shared ones -- so the equivalent of Aiden4's Rust code would be `bool f(int * restrict a, const int* restrict b)`. Wikipedia omits to mention two facts about `restrict` that make this work: first, that the single pointer access rule only applies *if the object behind the pointer is modified* (so `b` can be both `restrict` and aliased, because `*b` is not modified); and second, that a `const`-qualified `restrict` pointer *must not* point to something that is modified (unlike normal `const` pointers). (C99 6.7.3.1p4)
  • @trentcl: A `const restrict` pointer may point to objects that are modified, if it is not used to access any of the modified portions of the object. For example, given `void test(int * restrict p, int const * restrict q)`, code could write `p[0]` and read both `p[1]` and `q[1]`, provided it never reads `q[0]`, nor (for clang or gcc) tests any restrict-qualified pointer for equality with anything not derived from it.

回答

该函数int f(int *a, const int *b);承诺不会b 通过该指针更改内容......它没有承诺通过a指针访问变量。

如果ab指向同一个对象,改变它通过a是合法的(提供的底层对象是可修改的,当然)。

例子:

int val = 0;
f(&val, &val);

  • Notably, changing `*b` through `b` (after casting away `const`ness) would *also* be legal C. `const` is essentially a lint for the programmer, not a hint for the compiler -- C is not allowed to optimize calls to `f` by assuming that it does not change `*b`. `restrict`-qualified pointers are another matter (see Morten Jensen's answer, and my comment there).
  • @trentcl: Not sure if you meant “hint” or actually “lint,” but it works either way.

回答

虽然其他答案提到了 C 方面,但仍然值得一看 Rust 方面。使用 Rust,你的代码可能是这样的:

fn f(a:&mut i32, b:&i32)->bool{
    *a = 2;
    let ret = *b;
    *a = 3;
    return ret != 0;
}

该函数接受两个引用,一个可变,一个不可变。引用是保证对读取有效的指针,并且可变引用也保证是唯一的,因此它被优化为

        cmp     dword ptr [rsi], 0
        mov     dword ptr [rdi], 3
        setne   al
        ret

然而,Rust 也有与 C 的指针等价的原始指针,并且不做这样的保证。以下函数接受原始指针:

unsafe fn g(a:*mut i32, b:*const i32)->bool{
    *a = 2;
    let ret = *b;
    *a = 3;
    return ret != 0;
}

错过了优化并编译为:

        mov     dword ptr [rdi], 2
        cmp     dword ptr [rsi], 0
        mov     dword ptr [rdi], 3
        setne   al
        ret

神弩链接

  • So they're comparing apples to oranges. Nice one! +1 😀

以上是假设指向同一变量的两个指针是非法的/UB,为什么C编译器不能优化更改const指针的值?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>