新建一個物聯網行業交流學習QQ群,感興趣可加:928840648
=====CUT=====
變數
可執行程式存盤區
當一個C/C++原碼檔案被編譯鏈(比如gcc/g++)編譯及鏈接成為可執行程式后,由4個段組成,分別是:代碼段,資料段,堆疊,堆,
代碼段(.text)包含代碼邏輯(函式),以及宏定義(#define)常量,
資料段包含3部分:.bss,.rodata,.data,
.bss: Block Started by Symbol,存放程式中未初始化的全域變數,
.rodata:read only data,用于存放不可變修改的常量資料,
.data:靜態變數和已初始化的全域變數存盤區,
堆疊(.stack)主要用來存放區域變數, 傳遞的引數, 存放函式的回傳地址;程式運行程序中動態生成及回收,不需要用戶回收存盤空間,
堆(.heap)由malloc等API動態分配的記憶體區域,其生命周期由free決定;程式運行程序中動態生成,需要由使用者自行回收,
了解程式的組成存盤區有利于開發程序中對程式的精簡,比如我們可以選擇變數內容及大小是直接編譯進可執行程式(ROM)中,還是程式運行程序中才被實體化(RAM);如果代碼量10W+行基本能很明顯的出現差異,同樣功能有的代碼編譯出來占用空間非常大,有的很精簡,其中一個原因就是對底層存盤磁區的理解不同,
在我們Ubuntu Server目錄:~/workspace/basics/c/3_2_variables,存放著本章節我們會用到的源代碼檔案;其中main_1.c的內容是針對變數/函式的磁區存盤結構做了描述:

我們嘗試保留及注釋掉.data里面的一個存盤空間,對比兩者編譯后程式的大小,
![]()
差別巨大:

動態型別
本節內容原始碼在:~/workspace/basics/c/3_2_variables/main_2.c中,主要講解C語言中的動態型別變數定義的方法,需要使用到的關鍵字是:typeof(),該關鍵字是GNU C提供的一種特性,可以用來取得變數/函式的型別,或者運算式的型別,常用的方式如下:

取得變數型別,
定義一個變數,可以是普通變數也可以是指標變數,然后typeof取得該變數型別并用于定義另外同型別的變數;比如圖中所示的value,
取得函式型別做函式指標,
主要用來取得函式的型別,并定義函式指標使用,圖中所示的指標func就是取著函式add型別定義的,
取得運算式型別做處理,
取得運算式相對較為復雜,圖中所示,我們將函式add的運算結果匯出來用于判斷;該技巧同樣可以用于函式呼叫失敗后的多次重試,
編譯運行如下:

???????型別轉換
在C語言中,進行型別之間的轉換有兩種轉換方式:隱式型別轉換 和 強制型別轉換,其中強制型別轉換是由開發人員完成的,比如float val = (float)u8;
一般不會出現問題,所以我們重點關心隱式型別轉換,
隱式型別轉換是由編譯器主動完成的,如果由低型別到高型別的隱式型別轉換是安全的,不會發生截斷;相反由高型別到低型別的隱式型別轉換是不安全的,會發生截斷產生不正確的結果:

四種情況下會發生隱式型別轉換:賦值,算術運算,函式傳參,函式回傳值,
在原始碼檔案:main_3.c中,我們列出了四種情況的例子:

賦值,
圖中我們定義的型別uint8_t u8,并賦值為250;同時定義int8_t i8,然后把u8賦值給i8,顯然這個程序出現型別不匹配的轉換,由于250已經超過i8的最大范圍,因此i8不在是數值250了,
算術運算,
兩個uint8_t型別相加,賦值給uint16_t,實際上編譯器在執行該條指令時,會把兩個uint8_t先轉換為uint16_t,所以圖中:
uint16_t both = cal_1 + cal_2; 等價于:
uint16_t both = (uint16_t)cal_1 + (uint16_t)cal_2;
隱式型別轉換后資料正確,
函式傳參,
函式add的引數型別都是int8_t,而我們傳入的200已經超過最大范圍,因此傳入的資料發生大型別到小型別的轉換;同時函式回傳值是int8_t,兩個超過范圍的int8_t相加得不到200+200=400的數值,如果相加也出現溢位,那么回傳值更加不可測了,
函式回傳值,
函式add2的引數和回傳值都是uint16_t,我們傳入的兩個uint8_t被轉換為uint16_t,運算結果數值也是uint16_t,因此回傳數值正確,
編譯運行:

在撰寫程式的程序中,我們需要留意可能存在隱式型別轉換的地方,避免由于資料型別轉換導致的結果不可預測,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/168624.html
標籤:其他
