不一樣優化選項對ARM下C語言編譯的影響

      咱們知道在C語言編譯時,有那麼幾個經常使用的優化編譯選項,分別是-O0,-O1,-O2,-O3以及-Os。以前一直以爲既然是優化選項,頂可能是優化一下邏輯,提升一些效率或者減小一下程序大小而已。不多會以爲它們會影響程序的最終結果。直到最近在ARM平臺上發現一個程序裏的一個bug,才以爲這些優化選項有時候也沒那麼智能。或者說針對ARM平臺,尚未那麼智能。
      首先看這麼一段程序,此程序是我將問題簡單化的程序:linux

#include<stdio.h>
#include<string.h>

int main()
{
  char buffer[1024] = {0,1,2,3,4,5,6,7};
  int iTest = 0x12345678;
  int *p = (int *)(buffer + 7);
  memcpy(p, &iTest, sizeof(iTest));
  printf("%x\n", buffer[6]);  
  printf("%x\n", buffer[9]);   
  return 0;
}

    乍看之下,以爲這個程序沒啥問題。而後咱們將此程序文件名稱叫point.c。而後分別用交叉編譯鏈進行以下編譯:
    arm-xxx-linux-gcc point.c -o point0 -O0
    arm-xxx-linux-gcc point.c -o point1 -O1
    arm-xxx-linux-gcc point.c -o point2 -O2
    最終再分別執行三個程序,結果卻有點出人意料:
    ./point0
    6
    34
    ./point1
    34
    0
    ./point2
    6
    0
    只有在-O0,也就是沒有優化的狀況下,結果才和假想的一致。可是一樣的問題在x86平臺上卻沒有問題。
    因而我經過用如下命令,分別來生成不一樣優化選項下的彙編代碼,來肯定在ARM平臺上編譯到底出了什麼問題。
    arm-xxx-linux-gcc point.c -o point0.s -O0 -S
    arm-xxx-linux-gcc point.c -o point1.s -O1 -S
    arm-xxx-linux-gcc point.c -o point2.s -O2 -S
    而後對比三個彙編的代碼,發現問題出在memcpy這句話上。
    在point0.s中,程序是老老實實的調用的memcpy,而後就將0x12345678老老實實按照字節一個個的放到了buffer+7的位置。
    而在point1.s中程序則是沒有調用memcpy,而是用的語句:
    str        r3, [sp, #7]
    而此時r3中存儲的就是0x12345678;而因爲我採用的ARM平臺是32位的,此語句執行時,地址線應該不會發生變化,因此最終的結果是buffer+4到buffer+7的數據被覆蓋了,而不是buffer+7到buffer+10的數據被修改。
    而在point2.s中,貌似又針對流水線進行了優化,程序執行順序會有所變化,在對buffer部分位置賦初值的順序是在str  r3, [sp, #7]以後,因此buffer+6處的數據反而是正確的6。
    分析到這兒,也許有人會說寫個簡單的程序,都會由於編譯的優化選項不一樣致使結果不一樣,那這memcpy是否是就不敢用了?
    其實通常只要有較好的編程習慣的話,都不會遇到此類問題,好比下面的程序:編程

#include<stdio.h>
#include<string.h>

int main()
{
  char buffer[1024] = {0,1,2,3,4,5,6,7};
  int iTest = 0x12345678;
  char *p = buffer + 7;
  memcpy(p, &iTest, sizeof(iTest));
  printf("%x\n", buffer[6]);  
  printf("%x\n", buffer[9]);   
  return 0;
}

    這段程序其實只是簡單的改變了p的類型,就能保證在各類優化下,結果都同樣。可見好的編程習慣是有多麼的重要。優化

相關文章
相關標籤/搜索