當我們在網上搜索比較小的C語言程序時,對于如何編寫這樣一個較小的程序似乎還有很多困惑。有很多帖子想知道為什么即使是很小的程序,如“Hello world”這樣的應用程序為何如此之大,但沒有太多解釋。本文將展示如何使用Embedded Studio寫一個非常小的“Hello world”應用程序。
1 2 3 4 5 6 |
|
Windows
在Windows上,微軟的Visual Studio代表事實標準。微軟不僅設法讓可執行文件變得更大,而且現在還要求一個包能重新分發一個“可再分發的包”。

很難看出他們為什么會這樣做,它們提供操作系統和工具鏈,但是在舊版本的Visual Studio中這是不必要的。現在仍然有可能生成非常小的windows可執行文件,但這將是我們另一篇文章要講的內容。
Linux
在Linux中操作要好很多,沒有所謂的在分發的文件
$ gcc ./hello.c
$ ./a.out在終端窗口,我們得到的結果文本:
Hello world!
a.out的大小是16608字節(在64位系統上)。使用strip命令:
strip ./a.out
我們可以消除未使用的(符號)信息,并將其降低到14408字節。我們可以讓GCC生成一個main.c的ASM版本,并看到main翻譯得很好,所以大部分的大小來自于啟動代碼和庫代碼。實際的應用程序只使用了一些指令:

Embedded Studio
對于嵌入式系統,小代碼很重要。然而,大多數工具鏈在生成小代碼方面并不出色。讓我們來看看Embeded Studio是如何生成程序的,以及它的輸出有多大。
首先,我使用了我的blinky project.。其次是將應用程序更改為上面所示的“Hello world”應用程序。重新編譯后,程序大小為1.7KB。

雖然這看起來還不錯,但是還沒達到這個程序所能達到的更小的程度。如上面的截圖所示,實際上只有29+32=61個字節來自應用程序和啟動代碼。其余的都來自哪里呢?
很簡單,它們來自RTT(SEGGER的實時傳輸技術)和printf格式化器。格式化程序是將占位符(如 %d 和 %s)替換為給定參數的軟件(此處甚至不需要這些參數,因為“Hello world!”不需要任何參數)。
RTT代碼將輸出數據存儲在一個RAM緩沖區中,并由調試器不斷監視和排出。事實上,向RTT寫入數據基本上只是調用memcpy,因此速度很快。CPU不需要停止,實時行為幾乎不受影響。RTT的干擾非常小,在一個典型的Cortex-M微控制器上復制字符串的時間遠低于0.5us,因此即使在要求非常嚴格的環境中也能使用這種類型的通信。這就是我們將 RTT 作為默認 printf() 實現的原因。
主機端 printf 格式化
可以從列表中選擇斷點的實現方式,這是所有處理器都可以選擇的:

這對大多數程序來說都是一個很好的選擇。兩者都可以在使用J-Link的實際硬件和模擬器中無縫運行。這里需要注意的一點是,格式化是在主機上完成的,因此是由調試器完成,所有的格式化代碼現在不需要包含在生成的程序中,從而使程序變得非常小,正如項目資源管理器和輸出窗口報告所示 :

這非常好,運行程序,我們得到了預期的結果:

再按一下 F5鍵,我們在調試終端看到了預期的結果:

“Hello world”在Flash中只有125字節!
但是,讓每個字節都能發揮作用,我們還可以更進一步。Embedded Studio提供了兩套系統庫,一套根據大小優化,另一套根據速度優化。新項目的默認值是速度,讓我們將其更改為大小:

“重新編譯”后我們進一步減小了程序:

117字節,很難再小了!我們還可以深入研究主機端格式是如何工作的,但是這可能還有很多內容。但實際上它很簡單,就是printf()遇到一個breakpoint(ed)斷點指令,然后調試器會處理所有其余的工作。你可以嘗試自己下載Embedded Studio來體驗,用于評估或非商業用途不需要許可證,它可以在很多支持的平臺上使用。
elf文件的大小
對于嵌入式系統來說,Flash中的字節數才是最重要的,而不是ELF文件的大小。但是,我們也可以查看ELF文件,它是7470字節,這包括了不必需的所有符號信息。我們可以通過連接器來刪除符號信息(這通常沒有多大意義,只是在這里做一個比較):

這使得ELF文件的大小減少到1076個字節,大約比Linux可執行文件小14倍。然而,重要的數字是所使用的117字節的ROM(通常是閃存)內存。
在SEGGER的工具中,我們讓每個字節都有意義。你可以嘗試在嵌入式系統的其他工具鏈上使用同樣的方法,如果你能接近這個數字,我們會感到很驚訝的!