简体   繁体   English

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

[英]Why can't the C compiler optimize changing the value of a const pointer assuming that two pointers to the same variable would be illegal/UB?

Recently I stumbled over a comparison between Rust and C and they use the following code:最近我偶然发现了 Rust 和 C 之间的比较,它们使用以下代码:

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

In Rust (same code, but with Rust syntax), it produces the following Assembler Code:在 Rust(相同的代码,但使用 Rust 语法)中,它产生以下汇编代码:

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

While with gcc it produces the following:而使用 gcc 它会产生以下结果:

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

The text claims that the C function can't optimize the first line away, because a and b could point to the same number.文本声称 C function 无法优化第一行,因为ab可以指向相同的数字。 In Rust this is not allowed so the compiler can optimize it away.在 Rust 中这是不允许的,因此编译器可以优化它。

Now to my question:现在我的问题:

The function takes a const int* which is a pointer to a const int . function 采用const int* ,它是指向 const int 的指针 I read this question and it states that modifying a const int with a pointer should result in a compiler warning and in the worst cast in UB.我读了这个问题,它指出用指针修改 const int 应该会导致编译器警告和 UB 中最糟糕的转换。

Could this function result in a UB if I call it with two pointers to the same integer?如果我用两个指向同一个 integer 的指针调用它,这个 function 会导致 UB 吗?

Why can't the C compiler optimize the first line away, under the assumption, that two pointers to the same variable would be illegal/UB?为什么 C 编译器不能优化第一行,假设指向同一个变量的两个指针是非法的/UB?

Link to godbolt链接到上帝螺栓

Why can't the C Compiler optimize the first line away, under the assumption, that two pointers to the same variable would be illegal/UB?为什么 C 编译器不能优化第一行,假设两个指向同一个变量的指针是非法的/UB?

Because you haven't instructed the C compiler to do so -- that it is allowed to make that assumption.因为您没有指示 C 编译器这样做——它允许做出这样的假设。

C has a type qualifier for exactly this called restrict which roughly means: this pointer does not overlap with other pointers (not exactly , but play along). C 有一个类型限定符,正是这个称为restrict ,大致意思是:这个指针不与其他指针重叠(不完全是,但一起玩)。

The assembly output for总成 output 用于

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

is

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

... which removes the assignment *a = 2 ...删除分配*a = 2

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

In the C programming language, restrict is a keyword that can be used in pointer declarations.在 C 编程语言中,restrict 是可以在指针声明中使用的关键字。 By adding this type qualifier, a programmer hints to the compiler that for the lifetime of the pointer, only the pointer itself or a value directly derived from it (such as pointer + 1) will be used to access the object to which it points.通过添加此类型限定符,程序员向编译器提示,在指针的生命周期内,只有指针本身或直接从它派生的值(例如指针 + 1)将用于访问它指向的 object。

The function int f(int *a, const int *b); function int f(int *a, const int *b); promises to not change the contents of b through that pointer ... It makes no promises regarding access to variables through the a pointer.承诺不会通过该指针更改b的内容......它没有承诺通过a指针访问变量。

If a and b point to the same object, changing it through a is legal (provided the underlying object is modifiable, of course).如果ab指向同一个 object,则通过a更改它是合法的(当然前提是底层 object 是可修改的)。

Example:例子:

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

While the other answers mention the C side, it is still worth taking a look at the Rust side.虽然其他答案提到了 C 方面,但仍然值得一看 Rust 方面。 With Rust the code you have is probably this:使用 Rust 您拥有的代码可能是这样的:

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

The function takes in two references, one mutable, one not. function 接受两个引用,一个可变,一个不可变。 References are pointers that are guaranteed to be valid for reads, and mutable references are also guaranteed to be unique, so it gets optimized to引用是保证对读取有效的指针,可变引用也保证是唯一的,因此它被优化为

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

However, Rust also has raw pointers that are equivalent to C's pointers and make no such guarantees.但是,Rust 也有原始指针,它们等同于 C 的指针,并且不做这样的保证。 The following function, which takes in raw pointers:以下 function 采用原始指针:

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

misses out on the optimization and compiles to this:错过了优化并编译为:

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

Godbolt Link神栓链接

The function takes a const int* which is a pointer to a const int. function 采用const int* ,它是指向 const int 的指针。

No, const int* is not a pointer to a const int .不, const int*不是指向 const int 的指针 Anyone who says that is deluded.说这话的人都被骗了。

  • int* is a pointer to an int that definitely isn't const. int*是指向绝对不是 const 的 int 的指针。

  • const int* is a pointer to an int of unknown constness. const int*是一个指向未知常量的 int 的指针。

  • There is no way to express the notion of a pointer to an int that definitely is const.没有办法表达指向绝对是 const 的 int 的指针的概念。

If C was a better designed language, then const int * would be a pointer to a const int, mutable int * (borrowing a keyword from C++) would be a pointer to a non-const int, and int * would be a pointer to an int of unknown constness.如果 C 是一种设计更好的语言,那么const int *将是指向 const int 的指针, mutable int * (从 C++ 借用关键字)将是指向非 const int 的指针,而int *将是指向一个未知常量的 int。 Dropping the qualifiers (ie, forgetting something about the pointed-to type) would be safe – the opposite of real C in which adding the const qualifier is safe.删除限定符(即,忘记有关指向类型的某些内容)将是安全的——与真正的 C 相反,其中添加const限定符是安全的。 I haven't used Rust, but it appears from examples in another answer that it uses a syntax like that.我没有使用过 Rust,但是从另一个答案的示例中可以看出它使用了类似的语法。

Bjarne Stroustrup, who introduced const , originally named it readonly , which is much closer to its actual meaning.引入const的 Bjarne Stroustrup 最初将其命名为readonly ,这更接近其实际含义。 int readonly* would have made it clearer that it's the pointer that's read-only, not the pointed-to object. int readonly*会更清楚地表明它是只读的指针,而不是指向的 object。 The renaming to const has confused generations of programmers.重命名为const混淆了几代程序员。

When I have the choice, I always write foo const* , not const foo* , as the next best thing to readonly* .当我有选择的时候,我总是写foo const* ,而不是const foo* ,作为readonly*的下一个最好的东西。

It should be noted that this question is talking about optimisation on -Ofast and how it's even the case there.应该注意的是,这个问题是关于-Ofast的优化以及它是如何出现的。

Essentially, the C compiler of the function does not know the full discrete set of addresses that might be passed to it as that isn't known until link time / runtime as the function can be called from multiple translation units, and therefore it makes considerations that handle any legal address that a and b might point to, and of course that includes the case where they overlap.从本质上讲,function 的 C 编译器不知道可能传递给它的完整离散地址集,因为直到链接时间/运行时才知道,因为 ZC1C425268E68385D1AB5074C17A 可以从多个翻译单元调用 F19Z处理ab可能指向的任何合法地址,当然也包括它们重叠的情况。

Therefore, you need to use restrict to tell it that updating a (which the function allows because it is not a pointer-to-const, but even then the function could cast off const) doesn't update the value b is pointing to, which needs to be included in the comparison to 0, hence the store to a that happens before the comparison needs to go ahead, whereas on rust the default assumption is restrict.因此,您需要使用restrict告诉它更新a (function 允许,因为它不是指向 const 的指针,但即使这样,function 也可以抛弃 const)不会更新b指向的值,它需要包含在与 0 的比较中,因此在比较之前发生的存储到a需要提前到 go,而在 rust 上,默认假设是限制。 The compiler of the function does however know that *a is the same as *(a+1-1) and therefore will not produce 2 separate stores, but it does not know whether a or b overlap.然而,function 的编译器确实知道*a*(a+1-1)相同,因此不会产生 2 个单独的存储,但它不知道ab是否重叠。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如果两个指针的地址相同,则改变其中一个指针的值 - changing the value of one of the pointer in case where, adrress of two pointers are same 为什么创建指向C中const的Pointer的指针是非法的? - Why is it Illegal to create a Pointer to a Pointer that points to a const in C? 为什么程序不能在这个指针问题中显示 C 中的变量的值? - Why Can't the Program Show the Value of A Variable in C in this Pointer Problem? 在UB同时修改指向值和指针 - Is modifying the pointed value and the pointer at the same time UB 通过指针更改const的值 - Changing value of const by pointer 更改const指针的值 - Changing the value of a const pointer XCode C 编译器给出了关于指向 const 整数值的整数指针的不同结果,为什么? - XCode C compiler gives different result about integer pointer to const integer value, why? 当我将字符串文字分配给非常量指针时,为什么我的 C 编译器不发出警告? - Why doesn't my C compiler warn when I assign a string literal to a non-const pointer? C中的两个指针指向同一个结构 - 从一个指针更改结构 - Two pointers in C pointing to the same struct - changing the struct from one pointer 在 C 中,可以通过指针修改 const 变量吗? - In C, can a const variable be modified via a pointer?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM