gcc編譯器優化給我們帶來的麻煩???
今天看到一個很有趣的程序,如下:
1
2
3
4
5
6
7
8
9
|
int
main()
{
const
int
a = 1;
int
*b = (
int
*)&a;
*b = 21;
printf
(
"%d, %d"
, a, *b);
return
0;
}
|
當我第一眼看到這個程序的時候,我想當然的認為輸出結果是21, 21,但是我錯了
一時很難理解,於是我又輸出了它們的地址:
int
main()
{
const
int
a = 1;
int
*b = (
int
*)&a;
*b = 21;
printf
(
"%d, %d"
, a, *b);
printf
(
"\n%p, %p"
, &a, &*b);
return
0;
}
|
它們的地址是一樣的,看到這里我更加的不解,於是我試着查看一下匯編代碼。
int
main()
{
const
int
a = 1;
int
*b = (
int
*)&a;
*b = 21;
printf
(
"%d"
, a);
return
0;
}
|
對應匯編代碼如下:
這里得到的是at&t的匯編代碼,與intel不同之處在於:
1,指令格式為:指令名稱 元操作數 目的操作數
2,寄存器前加%
3,操作數前加$
4,0x4(%esp)為內存尋址,實際表示的是esp寄存器中的內容 + 4(如果不是很明白,望自行查找資料,本人知識有限)
我們首先看標號為1的行,對應c語句為const int a =1,這是把1放進地址為0x18(%esp)的地方,再來看標號2的地方,對應的printf語句,發現並沒有引用地址為0x18(%esp)的地方的值,而是把1直接放到了0x4(%esp),然后輸出。
所以個人認為,之所以會出現最開始的結果,是因為編譯器給我們做了一些優化導致的。為了證明我的觀點,我修改了程序:
int
main()
{
int
c = 1;
const
int
a = c;
int
*b = (
int
*)&a;
*b = 21;
printf
(
"%d, %d"
, a, *b);
return
0;
}
|
輸出結果為:
對應的匯編代碼為:
在標號1處,我們可以確定a存放在0x14(%esp)的地方,在標號2處,對應的printf語句,此語句從右向左處理參數,2處理的是*b,3處理的是a,這時看到用的是地址,而不是直接用數值,同時看標號0處,我們是將c賦值1,再給a賦值時編譯器用的是數值,並沒有引用地址。
所以,個人猜測,編譯器在這方面有一個優化功能:如果一個變量在定義時賦值常量,那么在引用它的時候,編譯器會直接用該常量數值代替地址的引用來節省時間,但是也給我們帶來了以外的麻煩。
這些都是個人的觀點,希望各位指教!!!