網路城邦
上一篇 回創作列表 下一篇  字體:
搞什麼鬼?C 語言的關鍵字:const
2008/07/30 18:13:57瀏覽27365|回應1|推薦1

    int BombLimitSec[3] = {5, 10, 15};

    void BomberCounter(int i)

    {

        static int up_count=0;

        if(i < 3)

        {

            if(++up_count == BombLimitSec[i])

            {

                bomb_explore = 1;

            }

        }

    }

    上述範例可以看出,炸彈的爆炸計時,只有5,10,15秒三種,麻煩來了,如果此時BombLimitSec[]這個陣列放在動態記憶體】,如果程式有問題,是有可能被不小心改到的,所以我們可以要求compiler,特別注意這筆數值有沒有人誤寫到,並同時把它放到唯讀記憶體】,這樣便會安全很多,而這個要求,就是const這個關鍵字。

        const int BombLimitSec[3] = {5, 10, 15};

    一般解釋都喜歡講成"如果不小心改到,所以const有用",說實話,這是很虛弱的解釋的,一個正確的程式怎麼會有"不小心改到"這種事發生呢?如果我程式內直接使用pointer來改,const內容也是照改不誤,是擋不住的;

    此時又有人會說啦!現代的cpu加上現代作業系統,如Linux,WinXP,Vista等,有記憶體保護,是沒辦法改到const內容的,說實話,這種解釋也是毫無用處,當誤改發生時,系統中斷了程式執行,那這個程式也沒在走了,有個屁用呢?如果這種錯誤是偶發性,是導因於太空中某顆高速粒子正好掉在這台電腦上,那還可以利用這種機制重新開機,可是如果根本是程式寫錯,電路做錯,只要走到這裡就會完蛋,那保護了這個const又有什麼用處呢?

    當然如果程式沒有任何bug,電路也非常穩定,有沒有加const,個人是覺得沒有很大的差別的。

    但是在embedded system中,甚至是single chip(小型MCU),差別可大了,原因很簡單,問題就出在動態記憶體(RAM)】與唯讀記憶體(ROM or Flash)】容量大小差很多。

    embedded system可能還好,因為RAMROM也相當的大,可是single chip就不一樣了,8kflash256bytesRAM,還可能更小,用起來可相當的吃緊,仔細觀察一下大家周圍的電表、除濕機、冷氣機、微波爐等產品,大致上都是這類CPU做出來的,先暫時別管程式是否有效率,先把不耗腦袋的佔用移除再說,這個時候,const就非常的有用。

    如果我們做出一個宣告:

        long var=100;

    這時該有一種認知,var100這兩個東西一定存在在整個電路的某個地方,而且這個地方一定可以控制的到。

    答案是var擺在RAM100放在ROM,至於放在哪裡?是compiler根據整個程式大小、最佳化等,東算西算決定放在這個位置。

    此時又有一個問題出現了,上面的描述在使用時,100早就已經放在var裡面了,很明顯的,一定有一個東西把100放進去var那個RAM的位置。C-Language程式的進入點是main(),所以在main()之前,一定有一個東西做了很多事,讓C的程式能夠從main()開始順利運行,那個東西,叫做boot loader,換句話說,因為刻意的安排,CPU會先執行boot loader,等所有環境建立完成後,再進入main()

    所以boot loader內有很多參數弄不好,程式是不會正常的,就連static的宣告也是boot loader幫你清掉的,忘了清,裡面就都是隨機值。

    CPU通電開始執行時,依照CPU不同種類,一定固定由某個位置開始抓第一行指令開始執行,boot loader的第一行就放在這裡。

    依據CPU的複雜度,如ARM7,9就有專門的boot loader,可以做出很複雜的功能,例如可以用TFTP載入,單純的如8051compiler:IAR51,因為事情很固定,它會自動幫你加上已經寫好的boot loader

    注意單純並不等於容易,不要隨便劃上等號。

    細節部份,如果有機會,另闢章節再說,先知道有這件事就好。

    回到const,以一個8k flash, 256bytes RAMCPU(8052)來說:

        char array[]={1,2,3,4,5,6};

    flash裡面已經放了array的內容,因為上述的寫法是一個有預設值的動態陣列,所以boot loader會把array的內容重新放進RAM後再進入main(),如果今天我們只是把array[]當成常數table存取,那RAM6bytes就白白浪費了,這是很可怕的事,因為總共只有256bytes,就是0.25K

    一個程式一定有不少table或字串要宣告,尤其是還有function table,沒多久RAM就會被榨乾,到時也不用做了。

    但如果換成

    const char array[]={1,2,3,4,5,6};

    此時array[]就只有放在flash內了,當然存取時就是存取flash,當然每種compiler都會提供不同的keyword來指定擺放的位置,甚至可以強制指定要放在哪裏,但constC language所定義,雖然只能指定唯讀區,但可攜性比較高。

    不過要注意,當flashRAM存取速度一樣時(一般MCU大致是如此),這樣是OK沒有問題,但如果是如ARM7的系統,或是TI DSP 54xx,使用外掛flash,當你在存取flash時,因為flash存取速度本來就慢,會發現程式變的好慢,偷雞不成蝕把米,這時還是把它放進RAM裡面去吧!就是把const拿掉,反正ARM系統的RAM一般都加的很大。

    不過要小心,因為可攜性導致的豬羊變色,那邊的好處,攜過來就變壞處了,const就是一個很容易發生的例子。

    要注意const不是一定絕對放在flash內,完全是看user怎麼搞而定,使用MCU一定要會定義記憶體區塊,不會定義就可能面臨怪事發生,compiler編譯整個程式時,會依據這些資訊,把整體空間計算好,會把這些空間(如static的RAM區域)的開始結束define成一個符號,而boot loader便依據這樣的資訊把環境建好,只要能讓程式正常運作即可,如果有記憶體的問題,看compiler產生的map檔,通常都會有答案,記得把這樣的option打開。

 

 

( 興趣嗜好電腦3C )
回應 推薦文章 列印 加入我的文摘
上一篇 回創作列表 下一篇

引用
引用網址:https://classic-blog.udn.com/article/trackback.jsp?uid=cchahacaptain&aid=2092923

 回應文章

123
請問
2009/08/31 20:52

不好意思我是初學者.......

大致上意思是不是..加入const

1.等於把佔用的RAM少了 直接存Flash中?

2.無法變更裡面的數值

3.還有一個疑問就是有沒有限定使用條件?我知道這樣問很奇怪

  像是只有陣列時 或是 10進制或16進制數值才可使用....

謝.......

彼得鄧(cchahacaptain) 於 2009-09-01 23:34 回覆:

想像一下,一個cpu的電路,一定有存程式的地方(flash)和可以變動數值的地方(RAM),不管怎麼樣跑,絕對是cpu, flash, ram這三個東西在做組合。

c語言是很早期發展出來的,所以首要觀念先要明白它只是那個時代認為"如何用程式來解決問題"的方法,他們認為有for, while, int, const等等的語法就可以以那個時代的計算機來解決問題,只是目前計算機結構還是差不多,所以c語言現在還可以使用。

const的意思是"這個變數是不可變更的常數值",所以要如何達到這個目的,就算是同一種cpu,不同公司設計的compiler的解決方法也不一定一樣,所以你的問題"1,2"點是你的希望達到的目的,const這個關鍵字不一定能完成,所以很可能你用這家的compiler const是放在flash內,結果換了compiler後const卻跑到ram內,一般來說,說明文件一定要看,記得要配合compiler產生的map檔的說明,map檔內會告訴你某個變數是安排在某個位址,所以如果你大致上看的懂電路是如何接,就會明白變數到底是在電路板上的哪裡,8bit, 16bit,甚至因為電路的安排,要指定某個變數到某個特定位址,懂了如何辦到這件事,其實你會發現不一定需要const,也一樣可以辦到。

第三個疑問是個很大的問題,我不會覺得很奇怪,10/16進制互相轉換是compiler的基本能力,應該是不會出問題才對,其他的問題倒滿多,例如"#define VAR 100000",如果compiler預設是16bit,這個100000就有可能會被砍錯,要寫成"#define VAR (long)100000"才會正確,embedded system compiler這類問題很多,"char var;"也不一定是被分配成8bit寬,甚至是compiler翻譯程式碼時,如果compiler基本上是設計成單工系統,結果你移植了一個open source的多工作業系統,函式庫或其他古里古怪的問題都會出現。

彼得鄧(cchahacaptain) 於 2009-09-02 11:34 回覆:

您可以再參考一下"程式鬼扯蛋"裡的"const加pointer該如何用呢"一篇,這篇對您的第三點有比較詳細的說明。

編譯的時候,記得list 檔案要看,我看過許多工程師list檔案都不看的,裡面的assembler output打開(這個功能不一定所有compiler都會有),看這個assembler的目的不是為了學組合語言,是為了瞭c程式如何變成assembler的,這是一個重要捷徑,現在的大型程式絕對都是高階語言,不一定是C,不過目前c語言最重要,熟悉c的人就很容易移植程式,目前網路上許多原始碼都是c語言。組合語言是函式庫或為了加速某段程式碼用,不要傻到用組合去寫大型程式,千萬記得,學組合語言一定要搭配演算法(例如jpeg)才有意義,cpu指令一定是為了解決某件事而設計,我們的課本都沒有教到底是哪件事,導致大家老是在"Hello world"這類無聊透頂的顯示打轉,儘量去碰自己不會的部分,乃至於焊接都是一門學問,當有一天把ic焊壞掉,順便去了解一下ic是甚麼東西,可能是甚麼地方壞,嘗試去解釋可能的答案,參考一下前輩的答案,但也不是絕對,我看過太多亂扯的前輩。

懂該顆cpu的組合語言,就很容易寫出執行有效率的程式,debug也快,許多匪夷所思的怪事只要看得懂組合語言就會找的到答案,但要小心高階語言低階化的問題,我看過一些低階化的c語言,整包程式碼都是互通的,誇張的連變數命名都沒有原則,完全沒有層級概念,還覺得自己寫的很好很複雜,很誇張,也要小心不要困在複雜的註解,過多的註解等於沒有,因為眼睛花了,關鍵的地方寫一下,留下該函式使用範例就好了。

如果把c語言搭配硬體線路弄清楚,再看一下c++語法,如果可以把c++語法和硬體線路連結起來的時候,大概很多程式語言,含組合語言,x86不算,因為cpu結構比較囉嗦,的上手,ARM7/9,MPC860,乃至於PIC系列的CPU,都只要看一下語法就能夠使用了。

此時開始學習一些小型的RTOS,你開始會發現看很多東西,都能夠很清楚感受到那到底是甚麼東西,這些東西在電路上可能會怎麼跑,就算是如同C++內的"虛擬類別"這樣虛構化的東西在電路上到底發生了甚麼事,這些都是學校沒教的