如果另一个指针指向它的引用,为什么编译器不优化const int(通过符号表)? [英] Why does a const int not get optimized by the compiler (through the symbol table) if another pointer points to its reference?

查看:133
本文介绍了如果另一个指针指向它的引用,为什么编译器不优化const int(通过符号表)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是此问题的答案的后续内容:解决方案

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

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

这也是我的想法.

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.2-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. x1x1spX3已被优化,因为它们是const并且未标记为外部链接.

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

  3. 已分配
  4. x2,因为它由pX2引用,并标记为用于外部链接. (来自extern的内容可能会通过pX2访问x2.)

  5. x3对我来说是最棘手的.已使用pX3(在std::cout << pX3;中).尽管其值本身是内联的,但它引用了x3.此外,尽管也内联了对x3的访问(在std::cout << x3;中),但使用&x3初始化的指针pX3的使用却无法优化该存储.

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;

在这种情况下,没有分配存储空间– x4pX4都没有. (pX4已被优化掉,对x4没有留下引用",而cc>也被优化了.)

太神奇了...

This is a follow up on an answer in this question: What kind of optimization does const offer in C/C++? (if any)

In the top voted answer, the following is stated:

When you declare a const in your program,

int const x = 2;

Compiler can optimize away this const by not providing storage to this variable rather add it in symbol table. So, subsequent read just need indirection into the symbol table rather than instructions to fetch value from memory.

NOTE:- If you do something like below:-

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

Then this would force compiler to allocate space for 'x'. So, that degree of optimization is not possible for this case.

Why is this the case? Looks like you will never be able to change x anyway, no?

解决方案

IMHO, Peter provided the explanation in his comment:

If a pointer is initialised to contain the address of a variable, and that pointer can be accessed from another compilation unit, then it would be reasonable for the compiler to allow for the possibility that the pointer IS dereferenced after being initialised in some compilation unit that is not visible to the compiler. One consequence of that is not optimising the pointer or the variable out of existence. There are numerous other reasoning approaches that might lead to the same outcome, depending on what code the compiler can actually see.

and this is exactly what I think too.

The const in C++ is a little bit confusing. It looks like the abbreviation of “constant” but actually it means “read-only”.

This in mind, I never wondered why the following code is legal in C:

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

but this not:

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

When I switched to C++, I assumed the above for C++ until I realized in a discussion with a colleague that I was wrong. (Not that this broke any written code of mine, but I hate it to be wrong.) ;-)

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

and Const propagation is the technique which makes it legal.

However, even with const propagation const int x; is still a read-only variable which might be addressed.

The OP provided a link about this topic (which might explain it even better than above):

SO: why the size of array as a constant variable is not allowed in C but allowed in C++?

To make this a ful-featured answer I tried to prepare a sample which illustrates the differences:

#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;
}

and the outcome of gcc 8.2 with -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

and the IMHO most interesting part – the global variables:

; 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, and pX3 have been optimized away because they are const and not remarked for external linkage.

  2. x1e and pX2 have been allocated because they are remarked for external linkage.

  3. x2 has been allocated because it is referred by pX2 which is remarked for external linkage. (Something from extern may access x2 via pX2.)

  4. x3 was the most tricky one for me. pX3 has been used (in std::cout << pX3;). Although, its value itself is inlined it refers to x3. Furthermore, although the access to x3 (in std::cout << x3;) was inlined as well, the usage of the pointer pX3 initialized with &x3 prevented to optimize this storage away.

Live Demo on godbolt (which has a nice colored dual-view to make it easy to explore)

I did the same with clang 7.0.0 and the outcome was similar.

(I tried it also with msvc v19.15 but I was not able (not patient enough) to evaluate the outcome.)


Concerning 4., I tried additionally:

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

and added to main():

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

In this case, there was no storage allocated – neither for x4 nor for pX4. (pX4 was optimized away, leaving no “reference” to x4 which in turn was optimized away as well.)

It's amazing...

这篇关于如果另一个指针指向它的引用,为什么编译器不优化const int(通过符号表)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆