gcc編譯選項中有個 -o選項,表示編譯**的時候進行優化。這樣就會出現一種可能:優化後的**和優化前的**順序不一致。
來看個例子:
8 #include9
10 int a, b;
11 12 int main()
13
**很簡單,我們使用不加優化選項來將其編譯為組合語言
yuhao@laplace:~/workspace/test/youhua$ gcc -s test.c
yuhao@laplace:~/workspace/test/youhua$
然後檢視彙編中的幾行關鍵邏輯**
movl b(%rip), %eax 將b的值放到暫存器%eax中
addl $1, %eax 暫存器%eax的值加1,相當於b+1
movl %eax, a(%rip) 將暫存器%eax的值賦給a,即 a = b + 1;
movl $1, b(%rip) 將1賦值給b,即b = 1
可以看出沒加編譯優化選項,編譯出來的彙編指令和c**的邏輯順序是一樣的。
再來看加優化選項編譯
yuhao@laplace:~/workspace/test/youhua$ gcc -s -o3 test.c
yuhao@laplace:~/workspace/test/youhua$
再來看彙編**
同樣的我們來分析關鍵的幾行彙編**
movl b(%rip), %eax 將b的值賦給暫存器%eax ,即tmp = b
movl $1, b(%rip) 將立即數1賦值給b,即 b = 1
addl $1, %eax 將暫存器%eax加1,即tmp + 1
movl %eax, a(%rip) 將暫存器%eax的值賦給a,即a = tmp + 1
顯然優化後的彙編指令將b = 1這個邏輯提前了,即發生了指令重排。這種重排指令是非常危險的,稍有不慎,你的業務會直接掛死。看一段**示例:
data_s *psttmp = malloc(sizeof(data_s));
psttmp->a = 0;
psttmp->b = 1;
g_pstdata = psttmp;
這是段**主要做了三個事:
1.定義了乙個棧指標變數
2.並給該指標申請了記憶體,並對記憶體中的成員變數進行修改賦值
3.將該指標複製給全域性變數g_pstdata
這段**如果被優化重排為如下邏輯:
1. data_s *psttmp = malloc(sizeof(data_s));
2. g_pstdata = psttmp;
3. g_pstdata->a = 0;
4. g_pstdata->b = 1;
那麼,當**執行到3的時候,有在別的執行緒訪問到g_pstdata,別的執行緒雖然拿到了g_pstdata的記憶體,但是記憶體裡面的b變數的值並沒有被賦值。與我們期望中的g_pstdata指向的不是null,就表示pstdata這塊記憶體可以正常讀取不相符合,這種不確定性會對程式產生不可估量的嚴重後果。這時我們就需要使用記憶體屏障技術:
8 #include9
10 typedef struct data
11 data_s;
16
17 data_s *pstdata = null;
18 19 int foo()
20
這樣我們就能保證在其他執行緒訪問到pstdata時,其記憶體一定是準備好了的。 一文搞懂C 動態記憶體
了解動態記憶體在 c 中是如何工作的是成為一名合格的 c 程式設計師必不可少的。c 程式中的記憶體分為兩個部分 很多時候,您無法提前預知需要多少記憶體來儲存某個定義變數中的特定資訊,所需記憶體的大小需要在執行時才能確定。在 c 中,您可以使用特殊的運算子為給定型別的變數在執行時分配堆內的記憶體,這會...
一文搞懂transform skew
目錄 如何理解斜切 skew,先看乙個 demo。在下面的 demo 中,有 4 個正方形,分別是 紅色 不做 skew 變換,綠色 x 方向變換,藍色 y 方向變換,黑色 兩個方向都變換,拖動下面的滑塊可以檢視改變 skew 角度後的效果。切換 selector 可以設定 transform or...
一文搞懂property函式
接下來我帶大家了解乙個函式的作用以及使用技巧,希望對大家都有幫助,話不多說,接下來就開始我的表演特性 首先property有兩種用法,一種是作為函式的用法,一種是作為裝飾器的用法,接下來我們就逐一分析 property函式 看一下作為函式它包含的引數都有哪些 property fget none,f...