• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tbwshc

            tbw

              C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
              95 Posts :: 8 Stories :: 3 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(4)

            我參與的團(tuán)隊(duì)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            2013年9月11日 #

            從一個(gè)任務(wù)轉(zhuǎn)變到另一個(gè)任務(wù)的實(shí)際過(guò)程叫作設(shè)備場(chǎng)景切換。因?yàn)樵O(shè)備場(chǎng)景是處理器專用的,實(shí)現(xiàn)設(shè)備場(chǎng)景切換的實(shí)現(xiàn)也是這樣。那意味著它總是要用匯編來(lái)寫。與其向你展示我在ADEOS 中使用的80x86 專用的匯編代碼,不如我用一種類C 的偽代碼來(lái)展示設(shè)備場(chǎng)景切換tb例程。
            void
            contextSwitch(PContext pOldContext, PContext pNewContext)
            {
            if(saveContext(pOldContext))
            {
            //
            // Restore new context only on a nonzero exit from saveContext().
            //
            restoreContext(pNewContext);
            // This line is never executed!
            }
            // Instead, the restored task continues to execute at this point.
            }

            例程 contextSwitch()實(shí)際上是被調(diào)度程序凋用,而調(diào)度程序又在那此終止中斷的tb系統(tǒng)調(diào)用中被調(diào)用,因此它不一定在這里終止中斷。此外,由于調(diào)用調(diào)度程序的操作系統(tǒng)調(diào)用是用高級(jí)語(yǔ)言寫的,所以大部分運(yùn)行任務(wù)的寄存器已經(jīng)被保存到它自己當(dāng)?shù)氐臈V辛恕_@減少了例程saveContext()和restoreContext()需要做的工作。它們只需要關(guān)心指令指針,棧指針以及標(biāo)志位的保存。例程 contextSwitch()的實(shí)際行為是很難僅僅通過(guò)看前面的代碼來(lái)理解的。大部分的軟件開發(fā)者以連續(xù)的方式思考問題,認(rèn)為每一行代碼會(huì)緊接著上一條代碼破執(zhí)行。然而,這個(gè)代碼實(shí)際為并行地執(zhí)行了兩次。當(dāng)一個(gè)任務(wù)(新任務(wù))轉(zhuǎn)變到運(yùn)行狀態(tài),另一個(gè)(舊任務(wù))必須同時(shí)返回到就緒狀態(tài)。想一下新任務(wù)當(dāng)它在restoreContext()代碼中被恢復(fù)的時(shí)候就會(huì)明白。無(wú)論新任務(wù)以前做什么,它在saveContext 代碼里總是醒著的——因?yàn)檫@就是它的指令存放的地方。新任務(wù)如何知道它是否是第一次(也就是,在準(zhǔn)備休眠的過(guò)程)或者是第二次(醒來(lái)的過(guò)程)從saveContext()中出來(lái)的呢?它確實(shí)需要知道這個(gè)差別,因此我不得不用一種有點(diǎn)隱蔽的方法來(lái)實(shí)現(xiàn)saveContext()。例程saveContext()不是保存了準(zhǔn)確的目前的指令指針。實(shí)際上是保存了一些指令前面的地址。那樣,當(dāng)保存的設(shè)備場(chǎng)景恢復(fù)的時(shí)候,程序從saveContext 中另一個(gè)下同的點(diǎn)繼續(xù)。這也使得saveContext 可能返回不同的值:當(dāng)任務(wù)要休眠的時(shí)候?yàn)榉橇?,?dāng)任務(wù)喚起的時(shí)候?yàn)榱恪@蘡ontextSwitch()利用這個(gè)返回的值來(lái)決定是否調(diào)用restoreContext()。如果不進(jìn)行這個(gè)檢測(cè),那么與這個(gè)新任務(wù)相關(guān)的代碼永遠(yuǎn)不會(huì)執(zhí)行。

            我知道這可能是一個(gè)復(fù)雜的事件序列,因此我在圖8-3 中說(shuō)明了整個(gè)的過(guò)程。

            posted @ 2013-09-11 16:34 tbwshc 閱讀(342) | 評(píng)論 (0)編輯 收藏

            2013年9月5日 #

            作為 ADEOS 的開發(fā)者(或是其他操作系統(tǒng)的開發(fā)者),你需要知道如何創(chuàng)建和使用任務(wù)。就像別的抽象數(shù)據(jù)結(jié)構(gòu),Task 類有自己的成員函數(shù)。ADEOS的的任務(wù)接口比別的大多數(shù)操作系統(tǒng)要簡(jiǎn)單一些,因?yàn)樗皇莿?chuàng)建一個(gè)新的Task 對(duì)象。一旦創(chuàng)建,ADEOS 任務(wù)繼續(xù)在系統(tǒng)中存在,直到相關(guān)的函數(shù)返回。當(dāng)然,這也許永遠(yuǎn)不會(huì)發(fā)生(意即ADEOS 任務(wù)也許永遠(yuǎn)不會(huì)結(jié)束),但是,如果一旦發(fā)生了,那么該任務(wù)就會(huì)被操作系統(tǒng)刪除掉。Task 的構(gòu)造函數(shù)如下所示。調(diào)用者通過(guò)構(gòu)造函數(shù)的參數(shù)分配一個(gè)函數(shù),一個(gè)權(quán)限值,和一個(gè)可選擇的新任務(wù)的堆棧大小。第一個(gè)參數(shù),fUnCtion,是一個(gè)指向C/C++語(yǔ)言或匯編語(yǔ)言的函數(shù)指針,該函數(shù)是要在新任務(wù)的上下文環(huán)境中運(yùn)行的。該函數(shù)不需要任何輸人參數(shù),也不返回任何結(jié)果。第二個(gè)參數(shù)P,是一個(gè)單字節(jié)的整數(shù)(從1 到255),代表了任務(wù)的權(quán)限級(jí)別,這個(gè)權(quán)限級(jí)別是與別的任務(wù)相對(duì)而言的,在tb任務(wù)調(diào)度器選擇新的任務(wù)運(yùn)行的時(shí)候會(huì)用到(p 的值越大,表示權(quán)限越高)。
            TaskId Task::nextId = 0
            /**************************************************************
            *
            * Method : Task()
            *
            * Description : Create a new task and initialize its state.
            *
            * Notes :
            *
            * Returns :
            *
            **************************************************************/
            Task:Task(void (*function)(), Priority p, int stackSize)
            {
            stackSize /= sizeof(int); //Convert bytes to words.
            enterCS(); //Critical Section Begin
            //
            // Initialize the task-specific data.
            //
            if = Task::nextId++;
            state = Ready;
            priority = p;
            entryPoint = function;
            pStack = new int[stackSize];
            pNext = NULL;
            //
            // Initialize the processor context.
            //
            contextInit(&context, run, this, pStack + stackSize);
            //
            // Insert the task into the ready list.
            //
            os.readyList.insert(this);
            os.schedule(); // Scheduling Point
            exitCS(); // Critical Section End
            } /* Task() */
            注意這個(gè)例程的功能塊被兩個(gè)函數(shù) enterCS()和exitCS()的調(diào)用包圍。在這些調(diào)用之間的代碼塊叫作tb臨界區(qū)(critical section)。臨界區(qū)是一個(gè)程序必須完整執(zhí)行的一部分。也就是說(shuō),組成這一個(gè)部分的指令必須沒有中斷地按照順序執(zhí)行。因?yàn)橹袛嗫赡茈S時(shí)發(fā)生,保證不受到中斷的唯一辦法就是在執(zhí)行關(guān)鍵區(qū)期間禁止中斷。因此在關(guān)鍵區(qū)的開始調(diào)用enterCS 以保存中斷的允許狀態(tài)以及禁止進(jìn)一步的中斷。在關(guān)鍵區(qū)尾部調(diào)用exitCS 以恢復(fù)前面保存的中斷調(diào)用。我們會(huì)看到在下面每一個(gè)例程中都應(yīng)用了同樣的技巧。
            在前面代碼中,有幾個(gè)在構(gòu)造函數(shù)里調(diào)用的其他例程,但是在這里我沒有空間列出。它們是contextInit()和os.readyList.insert()例程。例程contextInit()為任務(wù)建立了初始的設(shè)備場(chǎng)景。這個(gè)例程必定是處理器專用的,因此是用匯編語(yǔ)言寫的。
            contextInit()有四個(gè)參數(shù)。第一個(gè)是一個(gè)指向待初始比的設(shè)備場(chǎng)景數(shù)據(jù)結(jié)構(gòu)指針。第二個(gè)是一個(gè)指向啟動(dòng)函數(shù)的指針。這是一個(gè)特殊的ADEOS 函數(shù),叫作run(),它被用來(lái)啟動(dòng)一個(gè)任務(wù),并且如果以后相關(guān)的函數(shù)退出了,它被用來(lái)做其后的清理工作。第三個(gè)參數(shù)是一個(gè)指向新任務(wù)對(duì)象的指針。這個(gè)參數(shù)被傳遞給run(),因此相關(guān)的任務(wù)就能夠被啟動(dòng)。第四個(gè)和最后一個(gè)參數(shù)是指向新任務(wù)棧的指針。
            另一個(gè)函數(shù)調(diào)用是 os.readyList.insert()。這個(gè)函數(shù)把新任務(wù)加入到操作系統(tǒng)內(nèi)部的就緒任務(wù)列表中。readyList 是一個(gè)TaskList 類型的對(duì)象。這個(gè)類是那些具有insert()和remove()兩個(gè)方法的任務(wù)(按照優(yōu)先級(jí)排序)的鏈表。感興趣的讀者如果想知道這些函數(shù)是如何實(shí)現(xiàn)的就應(yīng)該下載和研究其ADEOS 的源代碼。你將在下面的討論中了解到更多有關(guān)就緒列表的問題。
            posted @ 2013-09-05 16:58 tbwshc 閱讀(833) | 評(píng)論 (0)編輯 收藏

            在早期的計(jì)算機(jī)中,沒有操作系統(tǒng)一說(shuō),應(yīng)用程序開發(fā)人員都要對(duì)處理器(CPU)和硬件進(jìn)行徹頭徹尾的控制。實(shí)際上,第一個(gè)操作系統(tǒng)的誕生,就是為了提供一個(gè)虛擬的硬件平臺(tái),以方便程序員開發(fā)。為了實(shí)現(xiàn)這個(gè)目標(biāo),操作系統(tǒng)只需要提供一些較為松散的函數(shù)、例程——就好像現(xiàn)在的軟件庫(kù)一樣——以便于對(duì)硬件設(shè)備進(jìn)行重置、讀取狀態(tài)、寫入指令之類的操作?,F(xiàn)代的操作系統(tǒng)則在單處理器上加入了多任務(wù)機(jī)制,每個(gè)任務(wù)都是一個(gè)軟件模塊,可以是相互獨(dú)立的。嵌入式的軟件經(jīng)常是可以劃分成小的互相獨(dú)立的模塊。例如,第五章“接觸硬件”講到的打印tb共享設(shè)備就包含三個(gè)不同的軟件任務(wù):
            ?? 任務(wù) 1:從計(jì)算機(jī)的串行口A 接收數(shù)據(jù)
            ?? 任務(wù) 2:從計(jì)算機(jī)的串行口B 接收數(shù)據(jù)
            ?? 任務(wù) 3:格式化數(shù)據(jù)并輸送到計(jì)算機(jī)的并行口(打印機(jī)就連接在并行口)
            這些任務(wù)的劃分提供了一個(gè)很關(guān)鍵的軟件抽象概念,這使得嵌入式操作系統(tǒng)的設(shè)計(jì)和實(shí)現(xiàn)更加容易,源程序也更易于理解和維護(hù)。通過(guò)把大的程序進(jìn)行模塊化劃分,程序員可以集中精力克服系統(tǒng)開發(fā)過(guò)程中的關(guān)鍵問題。

            坦言之,一個(gè)操作系統(tǒng)并不是嵌入式或其它計(jì)算機(jī)系統(tǒng)的必需的組件,它所能做的,也是像時(shí)用程序要實(shí)現(xiàn)的功能一樣。本書中的所有例子都說(shuō)明了這一點(diǎn)。應(yīng)用程序執(zhí)行起來(lái),都是從main 開始,然后進(jìn)入系統(tǒng)調(diào)用、運(yùn)行、結(jié)束。這與系統(tǒng)中只有一個(gè)任務(wù)是一樣的。對(duì)于應(yīng)用程序來(lái)說(shuō),僅僅是實(shí)現(xiàn)使LED 進(jìn)行閃爍,這就是操作系統(tǒng)的主要功用(屏蔽了很多復(fù)雜的操作)。

            如果你以前沒作過(guò)對(duì)操作系統(tǒng)的研究,那么,在這里得提醒一下,操作系統(tǒng)是非常復(fù)雜的。tb操作系統(tǒng)的廠商肯定是想使你相信,他們是唯一能生產(chǎn)出功能強(qiáng)大又易用的操作系統(tǒng)的科學(xué)家。但是,我也要告訴你:這并不是根困難的。實(shí)際上嵌入式操作系統(tǒng)要比桌面操作系統(tǒng)更容易編寫,所需的模塊和功能更為小巧、更易于實(shí)現(xiàn)。一旦明確了要實(shí)現(xiàn)了功能,并有一定的實(shí)現(xiàn)技能,你將會(huì)發(fā)現(xiàn),開發(fā)一個(gè)操作系統(tǒng)并不比開發(fā)嵌入式軟件艱難多少。

            嵌入式操作系統(tǒng)很小,因?yàn)樗梢匀鄙俸芏嘧烂娌僮飨到y(tǒng)的功能。例如,嵌入式操什系統(tǒng)很少有硬盤或圖形界面,因此,嵌入式操作系統(tǒng)可以下需要文件系統(tǒng)和圖形用戶接口。而且,一般來(lái)說(shuō),是單用戶系統(tǒng),所以多用戶操作系統(tǒng)的安全特性也可以省去了。上面所說(shuō)的各種性能,都可以作為嵌入式操作系統(tǒng)的一部分,但不是必須的。

            posted @ 2013-09-05 16:46 tbwshc 閱讀(222) | 評(píng)論 (0)編輯 收藏

            2013年8月19日 #

            在這一章里,我試圖把到目前為止所有我們討論過(guò)的單元合在一起,使之成為一個(gè)完整的嵌入式的應(yīng)用程序。在這里我沒有把很多新的素材加入到討論中,因此本章主要是描述其中給出的代碼。我的目的是描述這個(gè)應(yīng)用程序的結(jié)構(gòu)和它的源代碼,通過(guò)這種方式使你對(duì)它不再感到神奇。完成這一章以后,你應(yīng)該對(duì)于示例程序有一個(gè)完整的理解,并且有能力開發(fā)自己的嵌入式應(yīng)用程序。應(yīng)用程序的概述

            我們將要討論的這個(gè)應(yīng)用程序不比其他大部分tb編程書籍中找到的“Hello,World”例子更復(fù)雜。它是對(duì)于嵌入式軟件開發(fā)的一個(gè)實(shí)證,因此這個(gè)例子是出現(xiàn)在書的結(jié)尾而不是開始。我們不得不逐漸地建立我們的道路通向大部分書籍甚至是高級(jí)語(yǔ)言編譯器認(rèn)為是理所當(dāng)然的計(jì)算平臺(tái)。

            一旦你能寫“Hello, World”程序,你的嵌入式平臺(tái)就開始著上去很像任何其他編程環(huán)境。但是,嵌入式軟件開發(fā)過(guò)程中最困難的部分——使自己熟悉硬件,為它建立一個(gè)軟件開發(fā)的過(guò)程,連接到具體的硬件設(shè)備——還在后面呢。最后,你能夠把你的力量集中于算法和用戶界面,這是由你要開發(fā)的產(chǎn)品來(lái)確定的。很多情況下,這些程序的高級(jí)方面可以在其他的計(jì)算機(jī)平臺(tái)上開發(fā),和我們一直在討論的低級(jí)的嵌入式軟件開發(fā)同時(shí)進(jìn)行,并且只要把高級(jí)部分導(dǎo)入嵌入式系統(tǒng)一次,兩者就都完成了。

            圖 9-1 包含了一個(gè)“Hello, World!”應(yīng)用程序的高級(jí)的示意圖。這個(gè)應(yīng)用程序包括三個(gè)設(shè)備驅(qū)動(dòng)程序,ADEOS 操作系統(tǒng)和兩個(gè)ADEOS 任務(wù)。第一個(gè)任務(wù)以每秒10Hz 的速度切換Arcom 板上的紅色指示燈。第二個(gè)每隔10 秒鐘向主機(jī)或是連接到位子串口上的啞終端發(fā)送字符串“Hello,WOrld!”。這兩個(gè)任務(wù)之外,圖中還有三個(gè)設(shè)備的驅(qū)動(dòng)程序。這些驅(qū)動(dòng)程序分別控制著Arcom 板子的指示燈、時(shí)鐘以及串行端口。雖然通常把設(shè)備驅(qū)動(dòng)畫在操作系統(tǒng)的下面,但是我把它們?nèi)齻€(gè)和操作系統(tǒng)放在同一個(gè)級(jí)別,是為了著重說(shuō)明它們事實(shí)上依賴于ADEOS 比ADEOS 依賴于它們更多。實(shí)際上,ADEOS 嵌入式操作系統(tǒng)甚至不知道(或者說(shuō)下關(guān)心)這些設(shè)備驅(qū)動(dòng)是否存在于系統(tǒng)之中。這是嵌入式操作系統(tǒng)中設(shè)備驅(qū)動(dòng)程序和其他硬件專用軟件的共性。

            程序 main()的實(shí)現(xiàn)如下所示。這段代碼簡(jiǎn)單地創(chuàng)造廠兩個(gè)任務(wù),tb啟動(dòng)了操作系統(tǒng)的日程表。在這樣一個(gè)高的級(jí)別上,代碼的含義是不言而喻的。事實(shí)上、我們已經(jīng)在上一章中討論了類似的代碼。

            #include "adeos.h"
            void flashRed(void);
            void helloWorld(void);
            /*
            * Create the two tasks.
            */
            Task taskA(flashRed, 150, 512);
            Task taskB(helloWorld, 200, 512);
            /****************************************************
            *
            * Function : main()
            *
            * Description : This function is responsible for starting the ADEOS scheduler
            only.
            *
            * Notes :
            *
            * Returns : This function will never return!
            *
            ****************************************************/
            void
            main(void)
            {
            os.start();
            // This point will never be reached.
            } /* main() */

            posted @ 2013-08-19 11:49 tbwshc 閱讀(265) | 評(píng)論 (0)編輯 收藏

            2013年7月23日 #

            正如我早先說(shuō)的那樣,當(dāng)問題歸結(jié)于減小代碼的大小的時(shí)候,你最好讓編譯器為你做這件事。然而,如果處理后的程序代碼對(duì)于你可得的只讀存貯器仍然太大了,還有幾種技術(shù)你可以用來(lái)進(jìn)一步減少體程序的大小。在本節(jié)中,自動(dòng)的和人工的代碼優(yōu)化我們都要討論。
            當(dāng)然,墨菲法則指出,第一次你啟用編譯器的優(yōu)化特性后,你先前的工作程序會(huì)突然失效,也許自動(dòng)優(yōu)化最臭名昭著的是“死碼刪除”。這種優(yōu)化會(huì)刪除那些編譯器相信是多余的或者是不相關(guān)的代碼,比如,把零和一個(gè)變量相加不需要任何的計(jì)算時(shí)間。但是你可能還是希望如果程序代碼執(zhí)行了tb編譯器不了解的函數(shù),編譯器能夠產(chǎn)生那些“不相關(guān)”的指示。
            比如,下面這段給出的代碼,大部分優(yōu)化編譯器會(huì)去除第一條語(yǔ)句,因?yàn)?pControl 在重寫(第三行)之前沒有使用過(guò):
            *pControl = DISABLE;
            *pData = 'a';
            *pCotrol = ENABLE;
            但是如果 pControl 和pData 實(shí)際上是指向內(nèi)存映像設(shè)備寄存器的指針怎么辦?這種情況下,外設(shè)在這個(gè)字節(jié)的數(shù)據(jù)寫入之前將接收不到DISABLE 的命令。這可能會(huì)潛在地毀壞處理器和這個(gè)外設(shè)之間的所有未來(lái)的交互作用。為了使你避免這種問題,你必須用關(guān)鍵字“volatile”聲明所有指向內(nèi)存映像設(shè)備寄存器的指針和線程之間(或者是一個(gè)線程和一個(gè)中斷服務(wù)程序之間)共享的全局變量。你只要漏掉了它們中的一個(gè),墨菲法則就會(huì)在你的工程的最后幾天里回來(lái),攪得你心神不寧。我保證。
            ——————————————————————————————————
            警告:千萬(wàn)不要誤以為程序優(yōu)化后的行為會(huì)和未優(yōu)化時(shí)的一樣。你必須在每一次新的優(yōu)化后完全重新測(cè)試你的軟件,以確保它的行為沒有發(fā)生改變。
            ——————————————————————————————————
            更糟糕的是,或者退一步說(shuō),調(diào)試一個(gè)優(yōu)化過(guò)的程序是富有挑戰(zhàn)性的。啟用了編譯器的優(yōu)化后,在源代碼中的一行和實(shí)現(xiàn)這行代碼的那組處理器指令之間的關(guān)聯(lián)關(guān)系變得更加微弱了。那些特定的指令可能被移動(dòng)或者拆分開來(lái),或者兩個(gè)類似的代碼可能現(xiàn)在共用一個(gè)共同的實(shí)現(xiàn)。實(shí)際上,tb高級(jí)語(yǔ)言程序的有些行可能完全從程序中去除了(正如在前面例子里那樣)。結(jié)果,你可能無(wú)法在程序特定的一行上設(shè)置一個(gè)斷點(diǎn)或者無(wú)法研究一個(gè)感興趣變量的值。

            一旦你使用了自動(dòng)優(yōu)化,這里有一些關(guān)于用手工的辦法進(jìn)一步減少代碼大小的技巧。

            避免使用標(biāo)準(zhǔn)庫(kù)例程
            為了減少你的程序的大小,你所能做的最好的一件事情就是避免使用大的標(biāo)準(zhǔn)庫(kù)例程。很多最大的庫(kù)例程代價(jià)昂貴,只是因?yàn)樗鼈冊(cè)O(shè)法處理所有可能的情況。你自己有可能用更少的代碼實(shí)現(xiàn)一個(gè)子功能。比如,標(biāo)準(zhǔn)C 的庫(kù)中的spintf例程是出了名的大。這個(gè)龐大代碼中有相當(dāng)一部分是位于它所依賴的浮點(diǎn)數(shù)處理例程。但是如果你不需要格式化顯示浮點(diǎn)數(shù)值(%f 或者%d),那么你可以寫你自己的sprintf 的整數(shù)專用版本,并且可以節(jié)省幾千字節(jié)的代碼空間。實(shí)際上,一些標(biāo)準(zhǔn)C 的庫(kù)(這讓我想起Cygnus 的newlib)里恰好包含了這樣一個(gè)函數(shù),叫作sprintf。

            本地字長(zhǎng)
            每一個(gè)處理器都有一個(gè)本地字長(zhǎng),并且ANSI C 和C++標(biāo)準(zhǔn)規(guī)定數(shù)據(jù)類型int必須總是對(duì)應(yīng)到那個(gè)字長(zhǎng)。處理更小或者更大的數(shù)據(jù)類型有時(shí)需要使用附加的機(jī)器語(yǔ)言指令。在你的程序中通過(guò)盡可能的一致使用int 類型,你也許能夠從你的程序中削減寶貴的幾百字節(jié)。

            goto 語(yǔ)句
            就像對(duì)待全局變量一樣,好的軟件工程實(shí)踐規(guī)定反對(duì)使用這項(xiàng)技術(shù)。但是危急的時(shí)候,goto 語(yǔ)句可以用來(lái)去除復(fù)雜的控制結(jié)構(gòu)或者共享一塊經(jīng)常重復(fù)的代碼。
            除了這些技術(shù)以外,在前一部分介紹的幾種方法可能也會(huì)有幫助,特別是查詢表、手工編寫匯編、寄存器變最以及全局變量。在這些技術(shù)之中,利用手工編寫匯編通常可以得到代碼最大幅度的減少量。

            posted @ 2013-07-23 17:21 tbwshc 閱讀(584) | 評(píng)論 (0)編輯 收藏

            事情應(yīng)該盡可能簡(jiǎn)化,而不只是簡(jiǎn)單一點(diǎn)點(diǎn),一愛因斯坦

            雖然使軟件正確的工作好像應(yīng)該是一個(gè)工程合乎邏輯的最后一個(gè)步驟,但是在嵌入式的系統(tǒng)的開發(fā)中,情況并不總是這樣的。出于對(duì)低價(jià)系列產(chǎn)品的需要,硬件的設(shè)計(jì)者需要提供剛好足夠的存儲(chǔ)器和完成工作的處理能力。當(dāng)然,在工程的軟件開發(fā)階段,使程序正確的工作是很重要的。為此,通常需要一個(gè)或者更多的開發(fā)電路板,有的有附加的存貯器,有的有更快的處理器,有的兩者都有。這些電路板就是用來(lái)使軟件正確工作的。而工程的最后階段則變成了對(duì)代碼進(jìn)行優(yōu)化。最后一步的目標(biāo)是使得工作程序在一個(gè)廉價(jià)的硬件平臺(tái)上運(yùn)行。

            提高代碼的效率
            所有現(xiàn)代的 C 和C++編譯器都提供了一定程度上的代碼優(yōu)化。然而,大部分由編譯器執(zhí)行的優(yōu)化技術(shù)僅涉及執(zhí)行速度和代碼大小的一個(gè)平衡。你的程序能夠變得更快或者更小,但是不可能又變快又變小。事實(shí)上,在其中一個(gè)方面的提高就會(huì)對(duì)另一方面產(chǎn)生負(fù)面的影響。哪一方面的提高對(duì)于程序更加的重要是由程序員來(lái)決定。知道這一點(diǎn)后,無(wú)論什么時(shí)候遇到速度與大小的矛盾,編譯器的優(yōu)化階段就會(huì)作出合適的選擇。

            因?yàn)槟悴豢赡茏尵幾g器為你同時(shí)做兩種類型的優(yōu)化,我建議你讓它盡其所能的減少程序的大小。執(zhí)行的速度通常只對(duì)于某些有時(shí)間限制或者是頻繁執(zhí)行的代碼段是重要的。而且你可以通過(guò)手工的辦法做很多事以提高這些代碼段的效率。然而,手工改變代碼大小是一件很難的事情,而且編譯器處于一個(gè)更有利的位置,使得它可以在你所有的軟件模塊之間進(jìn)行這種改變。

            直到你的程序工作起來(lái),你可能已經(jīng)知道或者是非常的清楚,哪一個(gè)子程序或者模塊對(duì)于整體代碼效率是最關(guān)鍵的。中斷服務(wù)例程、高優(yōu)先級(jí)的任務(wù)、有實(shí)時(shí)限制的計(jì)算、計(jì)算密集型或者頻繁調(diào)用的函數(shù)都是候選對(duì)象。有一個(gè)叫作profiler 的工具,它包括在一些軟件開發(fā)工具組中,這個(gè)工具可以用來(lái)把你的視線集中到那些程序花費(fèi)大部分時(shí)間(或者很多時(shí)間)的例程上去。
            一旦你確定了需要更高代碼效率的例程,可以運(yùn)用下面的一種或者多種技術(shù)來(lái)減少它們的執(zhí)行時(shí)間。

            inline 函數(shù)
            在 c++中,關(guān)鍵字inline 可以被加入到任何函數(shù)的聲明。這個(gè)關(guān)鍵字請(qǐng)求編譯器用函數(shù)內(nèi)部的代碼替換所有對(duì)于指出的函數(shù)的調(diào)用。這樣做刪去了和實(shí)際函數(shù)調(diào)用相關(guān)的時(shí)間開銷,這種做法在inline 函數(shù)頻繁調(diào)用并且只包含幾行代碼的時(shí)候是最有效的。

            inline 函數(shù)提供了一個(gè)很好的例子,它說(shuō)明了有時(shí)執(zhí)行的速度和代碼的太小是如何反向關(guān)聯(lián)的。重復(fù)的加入內(nèi)聯(lián)代碼會(huì)增加你的程序的大小,增加的大小和函數(shù)調(diào)用的次數(shù)成正比。而且,很明顯,如果函數(shù)越大,程序大小增加得越明顯。優(yōu)化后的程序運(yùn)行的更快了,但是現(xiàn)在需要更多的ROM。

            查詢表
            switch 語(yǔ)句是一個(gè)普通的編程技術(shù),使用時(shí)需要注意。每一個(gè)由tb機(jī)器語(yǔ)言實(shí)現(xiàn)的測(cè)試和跳轉(zhuǎn)僅僅是為了決定下一步要做什么工作,就把寶貴的處理器時(shí)間耗盡了。為了提高速度,設(shè)法把具體的情況按照它們發(fā)生的相對(duì)頻率排序。換句話說(shuō),把最可能發(fā)生的情況放在第一,最不可能的情況放在最后。這樣會(huì)減少平均的執(zhí)行時(shí)間,但是在最差情況下根本沒有改善。

            如果每一個(gè)情況下都有許多的工作要做,那么也許把整個(gè)switch 語(yǔ)句用一個(gè)指向函數(shù)指針的表替換含更加有效。比如,下面的程序段是一個(gè)待改善的候選對(duì)象:

            enum NodeType {NodeA, NodeB, NodeC}
            switch(getNodeType())
            {
            case NodeA:
            ...
            case NodeB:
            ...
            case NodeC:
            ...
            }
            為了提高速度,我們要用下面的代碼替換這個(gè)switch 語(yǔ)句。這段代碼的第一部分是準(zhǔn)備工作:一個(gè)函數(shù)指針數(shù)組的創(chuàng)建。第二部分是用更有效的一行語(yǔ)句替換switch 語(yǔ)句。

            int processNodeA(void);
            int processNodeB(void);
            int processNodeC(void);
            /*
            * Establishment of a table of pointers to functions.
            */
            int (* nodeFunctions[])() = { processNodeA, processNodeB, processNodeC };
            ...
            /*
            * The entire switch statement is replaced by the next line.
            */
            status = nodeFunctions[getNodeType()]();

            手工編寫匯編
            一些軟件模塊最好是用匯編語(yǔ)言來(lái)寫。這使得程序員有機(jī)會(huì)把程序盡可能變得有效率。盡管大部分的C/C++編譯器產(chǎn)生的機(jī)器代碼比一個(gè)一般水平的程序員編寫的機(jī)器代碼要好的多,但是對(duì)于一個(gè)給定的函數(shù),一個(gè)好的程序員仍然可能做得比一般水平的編譯器要好。比如,在我職業(yè)生涯的早期,我用C 實(shí)現(xiàn)了一個(gè)數(shù)字濾波器,把它作為TI TMS320C30 數(shù)字信號(hào)處理器的輸出目標(biāo)。當(dāng)時(shí)我們有的tb編譯器也許是不知道,也許是不能利用一個(gè)特殊的指令,該指令準(zhǔn)確地執(zhí)行了我需要的那個(gè)數(shù)學(xué)操作。我用功能相同的內(nèi)聯(lián)匯編指令手工地替換了一段C 語(yǔ)言的循環(huán),這樣我就能夠把整個(gè)計(jì)算時(shí)間降低了十分之一以上。

            寄存器變量
            在聲明局部變量的時(shí)候可以使用 register 關(guān)鍵字。這就使得編譯器把變量放入一個(gè)多用選的寄存器,而不是堆棧里。合適地使用這種方琺,它會(huì)為編譯器提供關(guān)于最經(jīng)常訪問變量的提示,會(huì)稍微提高函數(shù)的執(zhí)行速度。函數(shù)調(diào)用得越是頻繁,這樣的改變就越是可能提高代碼的速度。

            全局變量
            使用全局變量比向函數(shù)傳遞參數(shù)更加有效率。這樣做去除了函數(shù)調(diào)用前參數(shù)入棧和函數(shù)完成后參數(shù)出棧的需要。實(shí)際上,任何子程序最有效率的實(shí)現(xiàn)是根本沒有參數(shù)。然而,決定使用全局變量對(duì)程序也可能有一些負(fù)作用。軟件工程人士通常不鼓勵(lì)使用全局變量,努力促進(jìn)模塊化和重入目標(biāo),這些也是重要的考慮。

            輪詢
            中斷服務(wù)例程經(jīng)常用來(lái)提高程序的效率。然而,也有少數(shù)例子由于過(guò)度和中斷關(guān)聯(lián)而造成實(shí)際上效率低下。在這些情況中,中斷間的平均時(shí)間和中斷的等待時(shí)間具有相同量級(jí)。這種情況下,利用輪詢與硬件設(shè)備通信可能會(huì)更好。當(dāng)然,這也會(huì)使軟件的模塊更少。

            定點(diǎn)運(yùn)算
            除非你的目標(biāo)平臺(tái)包含一個(gè)浮點(diǎn)運(yùn)算的協(xié)處理器,否則你會(huì)費(fèi)很大的勁去操縱你程序中的浮點(diǎn)數(shù)據(jù)。編譯器提供的浮點(diǎn)庫(kù)包含了一組模仿浮點(diǎn)運(yùn)算協(xié)處理器指令組的子程序。很多這種函數(shù)要花費(fèi)比它們的整數(shù)運(yùn)算函數(shù)更長(zhǎng)的執(zhí)行時(shí)間,并且也可能是不可重入的。

            如果你只是利用浮點(diǎn)數(shù)進(jìn)行少量的運(yùn)算,那么可能只利用定點(diǎn)運(yùn)算來(lái)實(shí)現(xiàn)它更好。雖然只是明白如何做到這一點(diǎn)就夠困難的了,但是理論上用定點(diǎn)運(yùn)算實(shí)現(xiàn)任何浮點(diǎn)計(jì)算都是可能的。(那就是所謂的浮點(diǎn)軟件庫(kù)。)你最大的有利條件是,你可能不必只是為了實(shí)現(xiàn)一個(gè)或者兩個(gè)計(jì)算而實(shí)現(xiàn)整個(gè)IEEE 754 標(biāo)準(zhǔn)。如果真的需要那種類型的完整功能,別離開編譯器的浮點(diǎn)庫(kù),去尋找其他加速你程序的方法吧。


             

            posted @ 2013-07-23 17:19 tbwshc 閱讀(295) | 評(píng)論 (0)編輯 收藏

            你可能想知道為什么C++語(yǔ)言的創(chuàng)造者加入了如此多的昂貴的——就執(zhí)行時(shí)間和代碼大小來(lái)說(shuō)——特性。你并不是少數(shù),全世界的人都在對(duì)同樣的一件事情困惑——特別是用C++做嵌入式編程的用戶們。很多這些昂貴的特性是最近添加的,它們既不是絕對(duì)的必要也不是原來(lái)C++規(guī)范的一部分。這些特性一個(gè)接著一個(gè)的被添加到正在進(jìn)行著的“標(biāo)準(zhǔn)化”進(jìn)程中來(lái)。在1996 年,一群日本的芯片廠商聯(lián)臺(tái)起來(lái)定義了一個(gè)C++語(yǔ)言和庫(kù)的子集,它更加適合嵌入式軟件開發(fā)。他們把他們新的工業(yè)標(biāo)準(zhǔn)叫作嵌入式C++。令人驚奇的是,在它的初期,它就在C++用戶群中產(chǎn)生了很大的影響。
            作為一個(gè)C++標(biāo)準(zhǔn)草案的合適子集,嵌入式C++省略了很多不限制下層語(yǔ)言可表達(dá)性的任何可以省略的東西。這些被省略的特性不僅包括像多重繼承性、虛擬基類、運(yùn)行時(shí)類型識(shí)別和異常處理等昂貴的特性,而且還包括了一些最新的添加特性,比如:模板、命名tb空問、新的類型轉(zhuǎn)換等。所剩下的是一個(gè)C++的簡(jiǎn)單版本,它仍然是面向?qū)ο蟮牟⑶沂荂 的一個(gè)超集,但是它具有明顯更少的運(yùn)行開銷和更小的運(yùn)行庫(kù)。
            很多商業(yè)的C++編譯器已經(jīng)專門地支持嵌入式C++標(biāo)準(zhǔn)。個(gè)別其他的編譯器允許手工的禁用具體的語(yǔ)言特性,這樣就使你能夠模仿嵌入式C++或者創(chuàng)建你的很個(gè)性化的C++語(yǔ)言。
            posted @ 2013-07-23 17:11 tbwshc 閱讀(359) | 評(píng)論 (0)編輯 收藏

            2013年7月10日 #

            最近在調(diào)試中遇到點(diǎn)內(nèi)存對(duì)齊的問題,別人問我是怎么回事,我趕緊偷偷查了一下,記錄下來(lái)。

            不論是C、C++對(duì)于內(nèi)存對(duì)齊的問題在原理上是一致的,對(duì)齊的原因和表現(xiàn),簡(jiǎn)單總結(jié)一下,以便朋友們共享。

            一、內(nèi)存對(duì)齊的原因
            大部分的參考資料都是如是說(shuō)的:
            1、平臺(tái)原因(移植原因):不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
            2、性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。原因在于,為了訪問未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對(duì)齊的內(nèi)存訪問僅需要一次訪問。

               也有的朋友說(shuō),內(nèi)存對(duì)齊出于對(duì)讀取的效率和數(shù)據(jù)的安全的考慮,我覺得也有一定的道理。

            二、對(duì)齊規(guī)則
                每個(gè)特定平臺(tái)上的編譯器都有自己的默認(rèn)“對(duì)齊系數(shù)”(也叫對(duì)齊模數(shù))。比如32位windows平臺(tái)下,VC默認(rèn)是按照8bytes對(duì)齊的(VC->Project->settings->c/c++->Code Generation中的truct member alignment 值默認(rèn)是8),程序員可以通過(guò)預(yù)編譯命令#pragma pack(n),n=1,2,4,8,16來(lái)改變這一系數(shù),其中的n就是你要指定的“對(duì)齊系數(shù)”。

                在嵌入式環(huán)境下,對(duì)齊往往與數(shù)據(jù)類型有關(guān),特別是C編譯器對(duì)缺省的結(jié)構(gòu)成員自然對(duì)屆條件為“N字節(jié)對(duì) 齊”,N即該成員數(shù)據(jù)類型的長(zhǎng)度。如int型成員的自然對(duì)界條件為4字節(jié)對(duì)齊,而double類型的結(jié)構(gòu)成員的自然對(duì)界條件為8字節(jié)對(duì)齊。若該成員的起始 偏移不位于該成員的“默認(rèn)自然對(duì)界條件”上,則在前一個(gè)節(jié)面后面添加適當(dāng)個(gè)數(shù)的空字節(jié)。C編譯器缺省的結(jié)構(gòu)整體的自然對(duì)界條件為:該結(jié)構(gòu)所有成員中要求的 最大自然對(duì)界條件。若結(jié)構(gòu)體各成員長(zhǎng)度之和不為“結(jié)構(gòu)整體自然對(duì)界條件的整數(shù)倍,則在最后一個(gè)成員后填充空字節(jié)。

                那么可以得到如下的小結(jié):

            類型 對(duì)齊方式(變量存放的起始地址相對(duì)于結(jié)構(gòu)的起始地址的偏移量)
            Char    偏移量必須為sizeof(char)即1的倍數(shù)
            Short   偏移量必須為sizeof(short)即2的倍數(shù)
            int     偏移量必須為sizeof(int)即4的倍數(shù)
            float   偏移量必須為sizeof(float)即4的倍數(shù)
            double  偏移量必須為sizeof(double)即8的倍數(shù)

               各成員變量在存放的時(shí)候根據(jù)在結(jié)構(gòu)中出現(xiàn)的順序依次申請(qǐng)空間,同時(shí)按照上面的對(duì)齊方式調(diào)整位置,空缺的字節(jié)編譯器會(huì)自動(dòng)填充。同時(shí)為了確保結(jié)構(gòu)的大小為結(jié) 構(gòu)的字節(jié)邊界數(shù)(即該結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù))的倍數(shù),所以在為最后一個(gè)成員變量申請(qǐng)空間后,還會(huì)根據(jù)需要自動(dòng)填充空缺的字節(jié),也就是 說(shuō):結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要編譯器會(huì)在最末一個(gè)成員之后加上填充字節(jié)。對(duì)于char數(shù)組,字節(jié)寬度仍然認(rèn)為為1。

               對(duì)于下述的一個(gè)結(jié)構(gòu)體,其對(duì)齊方式為:

            struct Node1{

                double m1;
                char m2;
                int m3;
            };

              對(duì)于第一個(gè)變量m1,sizeof(double)=8個(gè)字節(jié);接下來(lái)為第二個(gè)成員m2分配空間,這時(shí)下一個(gè)可以分配的地址對(duì)于結(jié)構(gòu)的起始地址的偏移量為8,是sizeof(char)的倍數(shù),所以把m2存放在偏移量為8的地方滿足對(duì)齊方式,該成員變量占用 sizeof(char)=1個(gè)字節(jié);接下來(lái)為第三個(gè)成員m3分配空間,這時(shí)下一個(gè)可以分配的地址對(duì)于結(jié)構(gòu)的起始地址的偏移量為9,不是sizeof (int)=4的倍數(shù),為了滿足對(duì)齊方式對(duì)偏移量的約束問題,自動(dòng)填充3個(gè)字節(jié)(這三個(gè)字節(jié)沒有放什么東西),這時(shí)下一個(gè)可以分配的地址對(duì)于結(jié)構(gòu)的起始地址的偏移量為12,剛好是sizeof(int), 由于8+4+4 = 16恰好是結(jié)構(gòu)體中最大空間類型double(8)的倍數(shù),所以sizeof(Node1) =16.

             

            typedef struct{

                char a;

                int b;

                char c;

            }Node2;

                成員a占一個(gè)字節(jié),所以a放在了第1位的位置;由于第二個(gè)變量b占4個(gè)字節(jié),為保證起始位置是4(sizeof(b))的倍數(shù),所以需要在a后面填充3個(gè) 字節(jié),也就是b放在了從第5位到第8位的位置,然后就是c放在了9的位置,此時(shí)4+4+1=9。接下來(lái)考慮字節(jié)邊界數(shù),9并不是最大空間類型int(4) 的倍數(shù),應(yīng)該取大于9且是4的的最小整數(shù)12,所以sizeof(Node2) = 12.
            typedef struct{

                char a;

                char b;

                int c;

            }Node3;

               明顯地:sizeof(Node3) = 8

               對(duì)于結(jié)構(gòu)體A中包含結(jié)構(gòu)體B的情況,將結(jié)構(gòu)體A中的結(jié)構(gòu)體成員B中的最寬的數(shù)據(jù)類型作為該結(jié)構(gòu)體成員B的數(shù)據(jù)寬度,同時(shí)結(jié)構(gòu)體成員B必須滿足上述對(duì)齊的規(guī)定。

               要注意在VC中有一個(gè)對(duì)齊系數(shù)的概念,若設(shè)置了對(duì)齊系數(shù),那么上述描述的對(duì)齊方式,則不適合。

               例如:

            1字節(jié)對(duì)齊(#pragma pack(1))
            輸出結(jié)果:sizeof(struct test_t) = 8 [兩個(gè)編譯器輸出一致]
            分析過(guò)程:
            成員數(shù)據(jù)對(duì)齊
            #pragma pack(1)
            struct test_t {
                int a;
                char b;
                short c;
                char d;
            };
            #pragma pack()
            成員總大小=8;

             

            2字節(jié)對(duì)齊(#pragma pack(2))
            輸出結(jié)果:sizeof(struct test_t) = 10 [兩個(gè)編譯器輸出一致]
            分析過(guò)程:
            成員數(shù)據(jù)對(duì)齊
            #pragma pack(2)
            struct test_t {
                int a;
                char b;
                short c;
                char d;
            };
            #pragma pack()
            成員總大小=9;

             

            4字節(jié)對(duì)齊(#pragma pack(4))
            輸出結(jié)果:sizeof(struct test_t) = 12 [兩個(gè)編譯器輸出一致]
            分析過(guò)程:
            1) 成員數(shù)據(jù)對(duì)齊
            #pragma pack(4)
            struct test_t { //按幾對(duì)齊, 偏移量為后邊第一個(gè)取模為零的。
            int a;
            char b;
            short c;
            char d;
            };
            #pragma pack()
            成員總大小=9;

             

            8字節(jié)對(duì)齊(#pragma pack(8))
            輸出結(jié)果:sizeof(struct test_t) = 12 [兩個(gè)編譯器輸出一致]
            分析過(guò)程:
            成員數(shù)據(jù)對(duì)齊
            #pragma pack(8)
            struct test_t {
            int a;
            char b;
            short c;
            char d;
            };
            #pragma pack()
            成員總大小=9;

             

            16字節(jié)對(duì)齊(#pragma pack(16))
            輸出結(jié)果:sizeof(struct test_t) = 12 [兩個(gè)編譯器輸出一致]
            分析過(guò)程:
            1) 成員數(shù)據(jù)對(duì)齊
            #pragma pack(16)
            struct test_t {
            int a;
            char b;
            short c;
            char d;
            };
            #pragma pack()
            成員總大小=9;

             

            至于8字節(jié)對(duì)齊和16字節(jié)對(duì)齊,我覺得這兩個(gè)例子取得不好,沒有太大的參考意義。

            (x666f)

            posted @ 2013-07-10 17:09 tbwshc 閱讀(250) | 評(píng)論 (0)編輯 收藏

            2013年6月25日 #

            任何接口設(shè)計(jì)的一個(gè)準(zhǔn)則:讓接口容易被正確使用,不容易被誤用。

            理想上:如何客戶企圖使用某個(gè)接口缺沒有獲得他所預(yù)期的行為,這個(gè)代碼不該通過(guò)編譯;如果代碼通過(guò)了編譯,他的行為就該是客戶所想要的。

            1. 導(dǎo)入外覆類型(wrapper types)

            2. 讓types容易被正確使用,不容易被誤用。盡量領(lǐng)你的types行為與內(nèi)置types一致。

            3. 設(shè)計(jì)class猶如設(shè)計(jì)type

            新type的對(duì)象應(yīng)該如何被創(chuàng)建和銷毀?(自己設(shè)計(jì)operatornew,operatornew[],operator delete和operator delete[])

            對(duì)象的初始化和對(duì)象的復(fù)制該有什么樣的差別?對(duì)應(yīng)于不同的函數(shù)調(diào)用

            新type的對(duì)象如果被passed by value ,意味著什么

            什么是新type的“合法值”?(??)

            你的新type需要配合某個(gè)繼承圖系嗎?

            你的心type需要什么樣的轉(zhuǎn)換

            什么樣的操作符合函數(shù)對(duì)此新type而言是合理的

            什么樣的標(biāo)準(zhǔn)函數(shù)應(yīng)該駁回

            誰(shuí)該取用新type的成員

            什么是新type的未聲明接口

            你的新type有多么一般化

            你真的需要一個(gè)新type嗎

             

            一、寧以pass-by-reference-to-const 替換 pass-by-value

            1.tbw效率高,沒有任何構(gòu)造函數(shù)或析構(gòu)函數(shù)被調(diào)用,因?yàn)闆]有任何新對(duì)象被創(chuàng)建。

            2. by refrenece方式傳遞參數(shù)還可以避免對(duì)象slicing 問題

            二、必須返回對(duì)象時(shí),別妄想返回其reference

            所有用上static對(duì)象的設(shè)計(jì),會(huì)造成多線程安全性的懷疑。

            三、將成員變量聲明為private:

            1. 可以實(shí)現(xiàn)出“不準(zhǔn)訪問”、“只讀訪問”、“讀寫訪問”、“惟寫訪問”

            2. 封裝:它使我們能夠改變事物而只影響有限客戶。

            將成員變量隱藏在函數(shù)接口的背后,可以為“所有可能的實(shí)現(xiàn)”提供彈性。如:tb變量被讀或被寫時(shí)通知其他對(duì)象、可以驗(yàn)證class的約束條件以及函數(shù)的前提和事后轉(zhuǎn)帖;以及在多線程環(huán)境執(zhí)行同步控制。。。。等。

            四、寧以non-member、non-friend替換member函數(shù)

            1. namespace WebBrowserStuff{ 
            2.         class WebBrowser{...}; 
            3.         void clearBrowser(WebBrowser& wb); 

            五、若所有參數(shù)皆需要類型轉(zhuǎn)換,請(qǐng)為此采用non-member函數(shù)

            1. class Rational { 
            2. ... 
            3.  
            4. const Rational operator*(const Rational& lhs,const Rational& rhs) 
            5. return Rational( lhs.numerator()* rhs.numerator()
            6. ,lhs.denominator()*rhs.denominator() ); 
            7.  
            8. Rational oneFourth(1,4); 
            9. Rational result; 
            10. result = oneFourth * 2; 
            11. result = 2*oneFourth; 

             六、考慮寫出一個(gè)不拋異常的swap函數(shù)

            posted @ 2013-06-25 17:17 tbwshc 閱讀(545) | 評(píng)論 (0)編輯 收藏

            好像所有講述編程的書都用同一個(gè)例子來(lái)開始,就是在用戶的屏幕上顯示出“Hello,World!”??偸鞘褂眠@個(gè)例子可能有一點(diǎn)叫人厭煩,可是它確實(shí)可以幫助讀者迅速地接觸到在編程環(huán)境中書寫簡(jiǎn)單程序時(shí)的簡(jiǎn)便方法和可能的困難。就這個(gè)意義來(lái)說(shuō),“Hello,World!”可以作為檢驗(yàn)編程語(yǔ)言和計(jì)算機(jī)平臺(tái)的一個(gè)基準(zhǔn)。

            不幸的是,如果按照這個(gè)標(biāo)準(zhǔn)來(lái)說(shuō),嵌入式系統(tǒng)可能是程序員工作中碰到的最難的計(jì)算機(jī)平臺(tái)了。甚至在某些嵌入式系統(tǒng)中,根本無(wú)法實(shí)現(xiàn)“Hello,World!”程序。即使在那些可以實(shí)現(xiàn)這個(gè)程序的嵌入式系統(tǒng)里面,文本字符串的輸出也更像是目標(biāo)的一部分而不是開始的一部分。

            你看,“Hello,World!”示例隱含的假設(shè),就是有一個(gè)可以打印字符串的輸出設(shè)備。通常使用的是用戶顯示器上的一個(gè)窗口來(lái)完成這個(gè)功能。但是大多數(shù)的嵌入式系統(tǒng)并沒有一個(gè)顯示器或者類似的輸出設(shè)備。即使是對(duì)那些有顯示器的系統(tǒng),通常也需要用一小段嵌入式程序,通過(guò)調(diào)用顯示驅(qū)動(dòng)程序來(lái)實(shí)現(xiàn)這個(gè)功能。這對(duì)一個(gè)嵌入式編程者來(lái)說(shuō)絕對(duì)是一個(gè)相當(dāng)具有挑戰(zhàn)性的開端。

            看起來(lái)我們還是最好以一個(gè)小的,容易實(shí)現(xiàn)并且高度可移植的聯(lián)人式程序來(lái)開始,這樣的tb程序也不太會(huì)有編程錯(cuò)誤。歸根到底,我這本書繼續(xù)選用“Hello,World!”。這個(gè)例子的原因是,實(shí)現(xiàn)這個(gè)程序?qū)嵲谔?jiǎn)單了。這起碼在讀者的程序第一次就運(yùn)行不起來(lái)的時(shí)候,會(huì)去掉一個(gè)可能的原因,即:錯(cuò)誤不是因?yàn)榇a里的缺陷:相反,問題出在開發(fā)工具或者創(chuàng)建可執(zhí)行程序的過(guò)程里面。

            嵌人式程序員在很大程度上必須要依靠自己的力量來(lái)工作。在開始一個(gè)新項(xiàng)目的時(shí)候,除了他所熟悉的編程語(yǔ)言的語(yǔ)法,他必須首先假定什么東西都沒有運(yùn)轉(zhuǎn)起來(lái),甚至連標(biāo)準(zhǔn)庫(kù)都沒有,就是類似printf()和scanf()的那些程序員常常依賴的輔助函數(shù)。實(shí)際上,庫(kù)例程常常作為編程語(yǔ)言的基本語(yǔ)法出現(xiàn)??墒沁@部分標(biāo)準(zhǔn)很難支持所有可能的計(jì)算平臺(tái),并且常常被嵌入式系統(tǒng)編譯器的制造商們所忽略。

            所以在這一章里你實(shí)際上將找不到一個(gè)真正的”Hello,World!”程序,相反,我們假定在第一個(gè)例子中只可以使用最基本的C 語(yǔ)言語(yǔ)法。隨著本書的進(jìn)一步深人,我們會(huì)逐步向我們的指令系統(tǒng)里添加C++的語(yǔ)法、標(biāo)準(zhǔn)庫(kù)例程和一個(gè)等效的字符輸出設(shè)備。然后,在第九章“綜合所學(xué)的知識(shí)”里面。我們才最終實(shí)現(xiàn)一個(gè)“Hello,World!”程序。到那時(shí)候你將順利地走上成為一個(gè)嵌入式系統(tǒng)編程專家的道路。

            posted @ 2013-06-25 17:12 tbwshc 閱讀(303) | 評(píng)論 (0)編輯 收藏

            国产精品丝袜久久久久久不卡| 99精品久久久久久久婷婷| yellow中文字幕久久网| 久久无码精品一区二区三区| 国内精品久久国产| 精品久久一区二区| 99久久香蕉国产线看观香| 99精品国产在热久久无毒不卡| 国产高清美女一级a毛片久久w| 狠狠色丁香久久婷婷综合_中| 国内精品伊人久久久久| 国产精品久久久久久五月尺| 91精品国产色综久久| 99精品久久久久久久婷婷| 成人国内精品久久久久影院VR| 综合久久一区二区三区 | 中文字幕精品无码久久久久久3D日动漫 | 久久久久亚洲AV成人网人人网站| 日本久久久久亚洲中字幕| 久久久久亚洲精品天堂久久久久久| 99久久国产热无码精品免费| 国产成人综合久久精品红| 精品久久久久久国产三级| WWW婷婷AV久久久影片| 色播久久人人爽人人爽人人片AV| 无码乱码观看精品久久| 久久人人爽人人爽人人AV东京热| 国产69精品久久久久9999| 久久久精品午夜免费不卡| 亚洲AV日韩精品久久久久久久 | 性做久久久久久久| 欧美日韩久久中文字幕| 亚洲国产日韩综合久久精品| 久久久久国产精品嫩草影院| 国产成人久久精品二区三区| 狠狠色丁香久久综合婷婷| 69久久夜色精品国产69| 99久久精品费精品国产一区二区| 性做久久久久久久| 91精品国产91久久综合| 久久精品国产影库免费看|