WinAVR(AVR-GCC)的奇怪問題

(這篇是從舊文章整理出來的)


GCC在compile的時候可以用-o的參數來設定optimize的程度,可設為不作最佳化(-○0),或是-o1~3和-os幾種最佳化的等級。-o0的時候是不會有什麼問題啦,不過程式不作optimize的話塞不進Tiny2313裏面。所以程式才寫到大概一半大小的時候就開始用最佳化compile,然後就發現有個地方一直很奇怪…..。
來看一小段程式:

1
2
3
4
5
6
7
8
tmp = RRR/5-3;      //tmp型別為int,所以不會取到小數部份
if (tmp > 5) {
AAA = 5;
} else if (tmp < 1) {
AAA = 1;
} else if (((RRR%5)*10/5) > 4) {  //手動四捨五入
AAA = tmp+1;
}

這段程式主要是要把RRR轉換成AAA 1~5的數值。RRR正常是20~40,有時會超出這個範圍,所以用了兩個判斷式去限制住讓AAA不會爆掉,就這麼簡單而已。
然後…我發現不管RRR的數值是多少,他在第一個else if的地方程式執行時總認為tmp=0,所以,判斷式就成立,結果就是AAA永遠=1。
怪吧?更怪的是在如果第一個if前面先去判斷tmp的值來看看,他會告訴你tmp不等於0…。然後才過了兩行程式,tmp就變0了。
裏面所有的變數都是宣告為signed int,跟其它變數也都沒有衝突的問題。而且只要compile的時候不要作optimize,就一切正常,所以程式本身應該是沒問題的。

結果怎麼解?我把tmp拿掉就好了…直接寫成:

1
2
3
4
5
6
7
8
AAA = RRR/5-3;
if (AAA > 5) {
AAA = 5;
} else if (AAA < 1) {
AAA = 0;
} else if (((RRR%5)*10/5) > 4) {  //手動四捨五入
AAA++;
}

這樣跑起來就正常了(?),看來大概這compiler討厭我的tmp吧?好吧,不去管原因了,就先加減用。結果過沒兩天我改了程式的其它不相干的地方之後,他又壞了。
這次一樣壞在第一個else if的地方,他總認為AAA = 0…最後又讓AAA變成永遠=1。 (你那麼愛讓AAA=1嗎?)

我只好再換另一種寫法:

1
2
3
4
5
6
7
8
9
10
AAA = RRR/5;
if (AAA > 8) {
AAA = 8;
} else if (AAA < 4) {
AAA = 4;
} else if (((RRR%5)*10/5) > 4) {  //手動四捨五入
AAA++;
}
 
AAA -= 4

把減4的動作移到最後面再作,這樣就又好了….這什麼道理?

所以…
有遇到程式跑起來很奇怪的時候,試試看先把compile optimize設成-o0吧

在 “WinAVR(AVR-GCC)的奇怪問題” 有 5 則留言

  1. hello,

    你可以 DUMP LSS, 看看編譯後的 ASSEMBLY CODE 作什麼. 可能是STACK OVERFLOW 的問題.

    另外, 可以考慮以下的寫法, 看看是不是比較好
    AAA = (RRR-15)/5; //find quotient
    BBB = ((RRR-15)%5) >> 1; //remiander x 10 / 5 = remainder x 2

    if (AAA > 5) {AAA = 5;}
    if (AAA < 1) {AAA = 0;} if (BBB > 4) {AAA++;} //手動四捨五入

  2. 不知你的這個問題解決沒
    還沒的話 試著在
    宣告變數temp前加上volatile試試

    ex: volatile int temp;

    然後在-os試看看

  3. 這好久前了我也不知道是怎麼發生的
    後來好像比較少遇到奇奇怪怪的問題了
    遇到變數有問題也會像你說的給他volatile一下啦

    3Q~

  4. 其實問題就是,樓上那位大大說的,volatile加上去就了,原因是compiler在做最佳化時,會自作聰明地幫你把同一個變數的值給更改,導致有時候不想被更改的值就變了,所以加上volatile時,就是不希望這個變數被”最佳化”了~~~

    以前上課的經驗,提供參考,不對請糾正

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *