首页 > 解决方案 > 如果另一个指针指向它的引用,为什么编译器不会(通过符号表)优化 const int?

问题描述

这是对这个问题的答案的跟进:const 在 C/C++ 中提供什么样的优化?(如有)

在投票最多的答案中,陈述了以下内容:

当您在程序中声明 const 时,

int const x = 2;

编译器可以通过不向该变量提供存储而将其添加到符号表中来优化此 const。因此,后续读取只需要间接到符号表中,而不是从内存中获取值的指令。

注意:- 如果您执行以下操作:-

const int x = 1;
const int* y = &x;

然后这将强制编译器为 'x' 分配空间。因此,对于这种情况,这种优化程度是不可能的。

为什么会这样?看起来你无论如何都无法改变 x ,不是吗?

标签: c++pointersreferenceconstantscompiler-optimization

解决方案


恕我直言,彼得在他的评论中提供了解释:

如果一个指针被初始化为包含一个变量的地址,并且该指针可以从另一个编译单元访问,那么编译器允许指针在某个编译单元中初始化后被取消引用的可能性是合理的对编译器不可见。其结果之一是没有优化指针或变量不存在。还有许多其他推理方法可能导致相同的结果,具体取决于编译器实际可以看到的代码。

这正是我的想法。

C++ 中的const有点令人困惑。它看起来像“常量”的缩写,但实际上它的意思是“只读”。

考虑到这一点,我从没想过为什么以下代码在 C 中是合法的:

enum { N = 3 };
static int a[N]; /* legal in C: N is a constant. */

但这不是:

const int n = 3;
static int b[n]; /* illegal in C: n is a read-only variable */

当我切换到 C++ 时,我假设上述 C++ 的情况,直到我在与一位同事的讨论中意识到我错了。(并不是说这破坏了我的任何书面代码,但我讨厌它是错误的。);-)

const int n = 3;
static int b[n]; // legal in C++

常量传播是使其合法的技术。

但是,即使使用 const 传播const int x;仍然是一个可以解决的只读变量。

OP提供了一个关于这个主题的链接(这可能比上面解释得更好):

SO:为什么在 C 中不允许将数组的大小作为常量变量,但在 C++ 中允许?

为了使它成为一个功能齐全的答案,我尝试准备一个示例来说明差异:

#include <iostream>

const int x1 = 1;
static const int x1s = 11;
extern const int x1e = 12;

const int x2 = 2;
extern const int *const pX2 = &x2;

const int x3 = 3;
static const int *const pX3 = &x3;

int main()
{
  // make usage of values (to have a side-effect)
  std::cout << x1;
  std::cout << x1s;
  std::cout << x1e;
  std::cout << x2;
  std::cout << pX2;
  std::cout << x3;
  std::cout << pX3;
  // done
  return 0;
}

以及gcc 8.2with的结果-O3

; int main()
main:
; {
 sub rsp, 8
;   // make usage of values (to have a side-effect)
;   std::cout << x1;
  mov esi, 1
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << x1s;
  mov esi, 11
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << x1e;
  mov esi, 12
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << x2;
  mov esi, 2
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << pX2;
  mov esi, OFFSET FLAT:_ZL2x2
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSo9_M_insertIPKvEERSoT_
;   std::cout << x3;
  mov esi, 3
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSolsEi
;   std::cout << pX3;
  mov esi, OFFSET FLAT:_ZL2x3
  mov edi, OFFSET FLAT:_ZSt4cout
  call _ZNSo9_M_insertIPKvEERSoT_
;   // done
;   return 0;
; }
  xor eax, eax
  add rsp, 8
  ret

恕我直言,最有趣的部分——全局变量:

; const int x3 = 3;
_ZL2x3:
  .long 3
; extern const int *const pX2 = &x2;
pX2:
  .quad _ZL2x2
; const int x2 = 2;
_ZL2x2:
  .long 2
; extern const int x1e = 12;
x1e:
  .long 12
  1. x1, x1s, 和pX3已被优化掉,因为它们被const标记为外部链接并且没有被标记。

  2. x1epX2已被分配,因为它们被标记为外部链接。

  3. x2已被分配,因为它被引用,pX2它被标记为外部链接。(来自外部的东西可以x2通过pX2.)

  4. x3对我来说是最棘手的一个。pX3已使用(在std::cout << pX3;)。虽然,它的值本身是内联的,它指的是x3. 此外,虽然对x3(in std::cout << x3;) 的访问也是内联的,但使用pX3初始化的指针被&x3阻止以优化此存储。

Godbolt 上的现场演示(具有漂亮的彩色双视图,便于探索)

我做了同样的事情,clang 7.0.0结果是相似的。

(我也尝试过,msvc v19.15但我无法(不够耐心)评估结果。)


关于 4.,我还尝试了:

const int x4 = 4;
static const int *const pX4 = &x4;

并添加到main()

  std::cout << x4;
  // No: std::cout << pX4;

在这种情况下,没有分配存储空间——既不是 forx4也不是 for pX4。(pX4被优化掉了,没有留下任何“参考” x4,反过来也被优化掉了。)

太奇妙了...


推荐阅读