代码之家  ›  专栏  ›  技术社区  ›  Marc

C优化:为什么编译器不将对象视为常量?

  •  5
  • Marc  · 技术社区  · 7 年前

    编译以下C模块

    static const int i = 1;
    
    void f (const int *i);
    
    int g (void)
    {
      f (&i);
      return i;
    }
    

    使用 gcc -S -O3 在x86_64机器上,为函数生成以下程序集 g :

    g:
            leaq    i(%rip), %rdi
            subq    $8, %rsp
            call    f@PLT
            movl    $1, %eax           # inlined constant as an immediate
            addq    $8, %rsp
            ret
    

    换句话说 return 语句编译为移动常量 $1 进入返回寄存器 %eax ,这是有道理的,因为 i 声明为常量。


    然而,如果我去掉这个 const 所以我有

    static int i = 1;
    
    void f (const int *i);
    
    int g (void)
    {
      f (&i);
      return i;
    }
    

    的输出 gcc-S-O3

    g:
            leaq    i(%rip), %rdi
            subq    $8, %rsp
            call    f@PLT
            movl    i(%rip), %eax     # reload i
            addq    $8, %rsp
            ret
    

    f .

    为什么会这样?的论点 f 声明为指向常量int的指针,因此 f 不允许更改 一、 f 无法调用修改的函数 g 一、 声明为静态。

    2 回复  |  直到 7 年前
        1
  •  4
  •   prl    7 年前

    将指向常量的指针转换为指向非常量的指针并修改引用的对象并不是未定义的行为,只要引用的对象未声明为常量。

    6.7.3p6说:“如果试图通过使用具有非常量限定类型的左值来修改使用常量限定类型定义的对象,则行为是未定义的。”

        2
  •  -2
  •   0___________    7 年前

    改变现状。尸体是已知的。无需采取特殊行动。

    static int i = 1;
    
    __attribute__((noinline)) void f (int *i)
    {
        *i *=2;
    }
    
    int g (void)
    {
      f (&i);
      return i;
    }
    
    
    f:
      sal DWORD PTR [rdi]
      ret
    g:
      mov edi, OFFSET FLAT:i
      call f
      mov eax, DWORD PTR i[rip]
      ret
    i: