Q: 常量和變量的本質差別是什麼?
A: 常量是不可改變的"變量", 變量是可改變的"常量". 我中有你你中有我是世界的本質,常量和變量隻是為了友善表達特定含義的代名詞,它們都是一種實體存在。
我們可能會關注常量和變量在編譯器或者運作期表達方式的不同,常量的不可改變特性有優化和保護的可能。
Q: const int i = 1和int i = 1有何差異?
A: const是寫給編譯器看的, 為防止修改i的代碼出現,而彙編代碼不受const影響。
const int i = 1;
int i = 1;
對應的彙編代碼是一樣的:
movl $0x1, %esi
Q: const變量有何種方式可以修改?
A: 我們可以繞過普通的變量指派手法, 找到變量的位址,用位址解引用的方式繞過編譯器的"小腦袋瓜"。
/*
Xi Chen([email protected])
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
const int i = 1;
int *p;
// modify i = 2
p = &i;
*p = 2;
printf("%d %d\n", i, *p);
return 0;
}
運作結果:
1 2
雖然我們透過i的位址修改了i的數值, 但是輸出i還是最初的數值。這不難了解,編譯器早知道i的常量,不管後面怎麼間接修改i, printf方式輸出i都隻會拿最初的數值,但*p就不一樣了。
pushq %rbp
movq %rsp, %rbp
leaq 0x35(%rip), %rdi
movl $0x1, %esi // 直接拿常量1
movl $0x2, %edx // 直接拿常量2
xorl %eax, %eax
callq 0x100000f8e
xorl %eax, %eax
popq %rbp
retq
Q: 既然編譯器知道const變量不能被修改, 為何不建立一個特别的區域保護const變量避免間接修改呢?
A: 程式區段布局模型決定了如上的代碼沒辦法保護,位于堆棧區域本身可讀寫,如下放入全局區域才有機會做到。
/*
Xi Chen([email protected])
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
const int i = 1;
int main(int argc, char *argv[])
{
int *p;
// modify i = 2
p = &i;
*p = 2;
printf("%d %d\n", i, *p);
return 0;
}
運作:
Bus error: 10
i位于全局區域,而且是隻讀區域,代碼*p = 2修改全局隻讀區域将被作業系統擋掉。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL90UbZhmTtlFawJzY04kMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1MTO4MTO1AjM4EzNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
0x3e(%rip)即是全局隻讀i的位址:
作者: 陳曦
環境: MacOS 10.14.5 (Intel i5)
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Linux 3.16.83 (Ubuntu)
轉載請注明出處