C++咬文嚼字-’Hijack const’

來源:互聯網
上載者:User

晚上無意翻看Bjarne Stroustrup的'The C++ Programming Language Special Edition'(英文版)第94頁,章節5.4 Constants一節,看到這麼一句原文'C++ offers the concept of a user-defined constant, a const, to express the notion that a value doesn't change directly.'字眼就在directly上,既然不能directly change,那我試試indirectly change。

問題就發現於這個indirectly change,代碼如下:

#include <iostream>

int main() {
 const int a = 2007;   // 這是一個常量,我們'不能directly change'^_^
 int *p = const_cast<int*>(&a);   //我們換一種方法hijack
 *p = 2008;    //篡改

 std::cout << "a = " << a << std::endl;  //期待輸出2008
 std::cout << "*p = " << *p << std::endl;
 std::cout << "&a = " << &a << std::endl;
 std::cout << "p = " << p << std::endl;
 
 return 0;
}

我首先在Windows上使用Mingw的g++編譯,輸出結果讓我大驚失色:
a = 2007
*p = 2008
&a = 0x23ff74
p = 0x23ff74

原以為a應該被hijack了,結果a仍然原封未動;關鍵是後兩行列印的a的地址和p的指向都是一個地方,難道C++對常量的保護如此之好,如此智能。不行,換一個平台試試,我又把源碼搬到了Solaris上同樣是g++編譯器,輸出結果一致。

百思不得其解後繼續'咬文嚼字'的往下看該小節。突然發現這麼一句話:'If the compiler knows every use of the const, it need not allocate space to hold it.'...'The common simple and common case is the one in which the value of the constant is known at compile time and no storage needs to be allocated.',左思又想,這麼一來在某些時候a被當作類似宏的方式處理的,就如:std::cout << "a = " << a << std::endl;這裡cout輸出一個常量運算式,編譯器估計直接將a替換成2007了,實際上就相當於std::cout << "a = " << 2007 << std::endl;而後的int *p = const_cast<int*>(&a);操作,這時就需要為a分配地址了。有人說a的輸出操作是在分配地址之後,那為什麼還輸出2007呢,我們從編譯器的角度看看,編譯器在解析到const int a = 2007的時候發現這是一個常量,便將之首先記錄到常量符號表中,而後在解析const_cast<int*>(&a)時為a在棧上分配記憶體,但是在走到輸出a那塊時首先引用到的還是常量符號表,而輸出&a時,由於是取地址操作,所以就把前面分配的棧地址賦到這裡了。

我們繼續再看一個例子:

#include <iostream>

int main() {
 int i = 2006;
 const int a = i + 1;   
 int *p = const_cast<int*>(&a);   
 *p = 2008;    //篡改

 std::cout << "a = " << a << std::endl;  //期待輸出2008
 std::cout << "*p = " << *p << std::endl;
 std::cout << "&a = " << &a << std::endl;
 std::cout << "p = " << p << std::endl;
 
 return 0;
}

在這個例子中const int a = i + 1;用一個非常量運算式給常量a賦初值,按照Bjarne Stroustrup的說法,是需要給a分配記憶體了。這樣我想編譯器也許不會在常量符號表中給a留位置,在下面的a的列印輸出時,a真的被hijack了。

輸出結果:
a = 2008
*p = 2008
&a = 0x23ff70
p = 0x23ff70

再看一個例子:
#include <iostream>

int main() {
 const int i = 2006;
 const int a = i + 1;   
 int *p = const_cast<int*>(&a);   
 *p = 2008;    //篡改

 std::cout << "a = " << a << std::endl;  //期待輸出2008
 std::cout << "*p = " << *p << std::endl;
 std::cout << "&a = " << &a << std::endl;
 std::cout << "p = " << p << std::endl;
 
 return 0;
}

編譯器在解析到const int i = 2006時首先將i作為常量儲存到常量符號表中,在const int a = i + 1時實際上相當於const int a = 2006 + 1,編譯器作最佳化,編譯器直接得到a = 2007而且是一個常量,也被儲存到常量表中,下面的流程就和第一個例子一樣了。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.