成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

全棧必備 :C語言基礎(chǔ)

開發(fā) 開發(fā)工具
C語言簡潔,使用方便靈活,能直接訪問物理地址,并進行高效的位運算。生成的目標(biāo)文件質(zhì)量高,執(zhí)行效率高,但這是相對而言的,比匯編語言的效率還是低了15%左右。數(shù)據(jù)處理尤其是圖像處理能力強,可移植性也好。

[[402858]]

【引子】溫故而知新,“三日不彈,手生荊棘”,代碼也是如此。另一方面,自己挖的坑要自己填。在《全棧的技術(shù)棧設(shè)想》中埋下了4種編程語言的伏筆,已經(jīng)兌現(xiàn)了Javacript,Python和Java, 本想將C/C++一并整理,但涉及面向?qū)ο蟮仍O(shè)計技術(shù),最終還是C 梳理一下,從0到1吧。

C語言簡潔,使用方便靈活,能直接訪問物理地址,并進行高效的位運算。生成的目標(biāo)文件質(zhì)量高,執(zhí)行效率高,但這是相對而言的,比匯編語言的效率還是低了15%左右。數(shù)據(jù)處理尤其是圖像處理能力強,可移植性也好。

關(guān)鍵字

ANSI C 共有32個關(guān)鍵字和9種控制語句,按照慣例編一首打油詩。

while signed for return,unsigned case continue default.

register goto auto union, do short long struct.

void typedef switch extern, volatile char double const.

if break static int, enum sizeof else float.

在C99中,又增加了5個關(guān)鍵字inline restrict _Bool _Complex _Imaginary, 后來的C11中又增加了7個關(guān)鍵字_Alignas _Alignof _Atomic _Static_assert _Noreturn _Thread_local _Generic, 所有這些關(guān)鍵字,不但要有所了解,還要知道其典型的應(yīng)用場景。

數(shù)據(jù)結(jié)構(gòu)

C語言為用戶提供了豐富的數(shù)據(jù)結(jié)構(gòu),還允許用戶自定義復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。C語言提供的數(shù)據(jù)結(jié)構(gòu)是以數(shù)據(jù)類型的形式給出的,C的數(shù)據(jù)類型劃分如下:

  • 基本類型
    • 數(shù)值類型
    • 字符類型
    • 枚舉類型
  • 構(gòu)造類型
    • 數(shù)組類型
    • 結(jié)構(gòu)類型
    • 聯(lián)合類型
  • 指針類型

數(shù)據(jù)有常量與變量之分,習(xí)慣上用大寫字母代表常量,用小寫字母代表變量。數(shù)值類型要注意數(shù)的范圍不同。字符常量是用單引號括起來的一個字符,還允許以一個“\”開頭的特殊字符常量。枚舉類型是一種基本數(shù)據(jù)類型,而不是一種構(gòu)造類型,因為它不能再分解為任何基本類型。在編譯中,對枚舉元素按常量處理,故稱枚舉常量,它們不是變量,不能對它們賦值。

數(shù)組是有序數(shù)據(jù)的集合,數(shù)組中每一元素都屬于同一數(shù)據(jù)類型,用一個統(tǒng)一的數(shù)組名和下標(biāo)來唯一的確定數(shù)組中的元素。結(jié)構(gòu)體是C語言提供的一種數(shù)據(jù)結(jié)構(gòu),一般形式如下:

  1. struct 結(jié)構(gòu)體名字 
  2.        { 
  3.           成員列表 
  4.        } 變量名列表; 

一般地,可以利用宏取得結(jié)構(gòu)內(nèi)的偏移量:

#undef offsetofstruct #define offsetofstruct(TYPE, ELEMENT) ((size_t) &((TYPE *)0)->ELEMENT) #endif

聯(lián)合也是一種派生類型,語法和結(jié)構(gòu)體相同,不同是它的成員共享存儲空間。聯(lián)合定義了一組可供選擇的值,它們共享一塊內(nèi)存。

一個變量在內(nèi)存中的地址就稱為該變量的指針,這是C語言中的精華,下面單獨描述。

C語言還提供了十分豐富的運算符,主要有如下34種:

  1. 算術(shù):+、-、*、/、++等 
  2. 關(guān)系:>、<、==、!=等 
  3. 邏輯:&&、||、!等 
  4. 位:>>、<<、~等 
  5. 賦值:等號(=)及其擴展賦值運算符(+=、-=、*=、/=等) 
  6. 指針:*、& 

用各種運算符將運算對象連接起來形成了表達式。

指針

C 語言的核心是指針,其靈活性和超長之處源自于指針。指針提供了動態(tài)操控內(nèi)存的機制,強化了對數(shù)據(jù)結(jié)構(gòu)的支持,且實現(xiàn)了訪問硬件的功能。

指針是一個存放內(nèi)存地址的變量。定義一個指針時,必須規(guī)定它指向的變量類型。任何指針都是指向某種類型的變量。當(dāng)通過指針來訪問指針?biāo)赶虻膬?nèi)存區(qū)時,指針?biāo)赶虻念愋蜎Q定了編譯器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來看。要注意區(qū)分指針的類型(即指針本身的類型)和指針?biāo)赶虻念愋褪莾蓚€概念。

void指針類型,即不指定它是指向哪一種類型數(shù)據(jù)的指針變量。void指針?biāo)梢灾赶蛉魏晤愋蛿?shù)據(jù),可以用任何類型的指針直接給void指針賦值。但是,如果需要將指針的值賦給其它類型的指針,則需要進行強制類型轉(zhuǎn)換。在指針定義語句的類型前加const,表示指向的對象是常量。

指針變量可以指向另一個指針,指針的指針。程序中的函數(shù)代碼同樣也占有內(nèi)存空間,每個函數(shù)都有地址,因此指針同樣可以指向函數(shù),指向函數(shù)地址的指針稱為函數(shù)指針??傊羔樋梢灾赶蚴裁词菦]有限制的,可以是變量、數(shù)組元素、動態(tài)分配的內(nèi)存塊以及函數(shù)。

正確理解指針變量和函數(shù)指針的聲明,例如:(*(void(*)())0)(); 注意*p()和(*p)()的區(qū)別,前者含義是函數(shù)返回值為一個指針類型,后者含義p是一個指向函數(shù)的指針。

指針的典型用法:

  • 直接訪問系統(tǒng)內(nèi)存
  • 引用函數(shù)
  • 構(gòu)造鏈?zhǔn)綌?shù)據(jù)結(jié)構(gòu)
  • 引用動態(tài)分配的數(shù)據(jù)結(jié)構(gòu)
  • 實現(xiàn)引用調(diào)用
  • 傳遞數(shù)組參數(shù)
  • 訪問和迭代數(shù)據(jù)元素
  • 代表字符串
  • 作為其他值的別名

函數(shù)

一個大程序可分為若干個小程序模塊,每一個模塊用來實現(xiàn)一個特定的功能,這個模塊稱為函數(shù)。一個C程序可由一個主函數(shù)和若干子函數(shù)構(gòu)成。由主函數(shù)調(diào)用其他函數(shù),其他函數(shù)也可以互相調(diào)用。同一個函數(shù)可以被一個或多個函數(shù)調(diào)用任何多次。

從用戶來看,可以將函數(shù)分為庫函數(shù)和自定義函數(shù)。從函數(shù)自身看,可以分為有參數(shù)和無參兩種。傳參過程中要根據(jù)需要進行值傳遞和地址傳遞,也就是形參和實參。只有在發(fā)生函數(shù)調(diào)用時,函數(shù)中的形參才被分配內(nèi)存單元。在調(diào)用結(jié)束后,形參所占的內(nèi)存單元也被釋放。

函數(shù)應(yīng)當(dāng)在同一文件中它被調(diào)用的位置之前定義,否則就會默認(rèn)返回值是整型。如果調(diào)用函數(shù)處和被調(diào)用函數(shù)不在同一文件,且返回值類型不同,連接時會報錯。如果被調(diào)用函數(shù)參數(shù)包括char、short、float等類型,則在調(diào)用該函數(shù)的文件中必須聲明該函數(shù),且括號內(nèi)帶上參數(shù)類型。

本質(zhì)上,函數(shù)表示法就是指針表示法,函數(shù)名稱經(jīng)過求值會變成函數(shù)的地址,然后函數(shù)參數(shù)會被傳遞給函數(shù)。

程序棧是支持函數(shù)執(zhí)行的內(nèi)存區(qū)域,通常和堆共享,包括返回地址,局部數(shù)據(jù)存儲,參數(shù)存儲,棧指針和基指針(運行時管理棧的指針)。系統(tǒng)在創(chuàng)建棧幀時,將參數(shù)以跟聲明相反的順序推到幀上,最后推入局部變量。

從函數(shù)返回指針時可能存在的潛在問題:

  • 返回未初始化的指針
  • 返回指向無效地址的指針
  • 返回局部變量的指針
  • 返回指針但是沒有釋放內(nèi)存

函數(shù)指針可以 以編譯時未確定的順序來執(zhí)行函數(shù)。

  1. void (*foo)() 

使用函數(shù)指針時一定要小心,因為c 不會檢查參數(shù)傳遞是否正確,建議使用fptr作為前綴。函數(shù)指針數(shù)組可以基于某些條件選擇要執(zhí)行的函數(shù)。傳遞指針的指針可以讓參數(shù)指針指向不同的內(nèi)存地址。

內(nèi)存存儲

C中主要有4種存儲類型:

  1. auto只能用來標(biāo)識局部變量的存儲類型,對于局部變量,auto是默認(rèn)的存儲類型,不需要顯示的聲明。因此,auto標(biāo)識的變量存儲在棧區(qū)中。
  2. extern用來聲明全局變量。如果全局變量未被初始化,那么將被存在BBS區(qū)中,且在編譯時,自動將其值賦值為0,如果已經(jīng)被初始化,那么就被存在數(shù)據(jù)區(qū)中。全局變量,不管是否被初始化,其生命周期都是整個程序運行過程中,為了節(jié)省內(nèi)存空間,在當(dāng)前文件中使用extern來聲明其它文件中定義的全局變量時,就不會再為其分配內(nèi)存空間。
  3. register的變量在由內(nèi)存調(diào)入到CPU寄存器后,則常駐在CPU的寄存器中,因此register將在很大程度上提高效率,因為省去了變量由內(nèi)存調(diào)入到寄存器過程中的多個指令周期。
  4. static無論是全局的還是局部的,都存儲在數(shù)據(jù)區(qū)中,其生命周期為整個程序,如果是靜態(tài)局部變量,其作用域為一對{}內(nèi),如果是靜態(tài)全局變量,其作用域為當(dāng)前文件。靜態(tài)變量如果沒有被初始化,則自動初始化為0。靜態(tài)變量只能夠初始化一次。

在使用內(nèi)存時,申請與釋放要配對,本著誰申請,誰釋放的原則,釋放后,要把指針置空。常見的內(nèi)存使用問題有3種:

  • 野指針:Free后,沒有置空,后續(xù)繼續(xù)使用該指針;
  • 內(nèi)存泄漏:申請后沒有釋放
  • 內(nèi)存越界:數(shù)組索引和內(nèi)存訪問溢出

避免內(nèi)存越界,必須對數(shù)組的索引進行有效值檢查,字符串操作API最好要帶n 例如strncpy,strncat等,內(nèi)存拷貝的size要做檢測,避免野指針。

在條件允許的情況下,可以自己實現(xiàn)內(nèi)存池管理,按字節(jié)切割內(nèi)存池(例如 8字節(jié)的整數(shù)倍)。每次分配的內(nèi)存地址空間,在啟止位置進行初始化特殊值,然后用單獨線程每隔一小段時間,對內(nèi)存池中每個有效塊進行掃描,做好內(nèi)存碎片整理。

動態(tài)分配存儲字符串的空間(malloc方式)時,注意不要忘記字符串需要多分配一個字節(jié)保存字符串結(jié)尾'\0'。

編譯

C語言的編譯過程有預(yù)編譯——>語法分析——>代碼生成——>優(yōu)化——>匯編——>連接。預(yù)編譯器完成宏替換,詞法分析,并創(chuàng)建符號表。語法分析包含了語義分析,創(chuàng)建語法樹。代碼生成器來生成中間代碼,優(yōu)化器負(fù)責(zé)指令優(yōu)化,匯編程序生成匯編代碼,最后由連接器生成目標(biāo)文件和可執(zhí)行文件。連接器對目標(biāo)模塊中的外部對象做同名檢查,如果沒有命名沖突就加入到載入模塊。

函數(shù)和初始化的全局變量(包括初始化為0)是強符號,未初始化的全局變量是弱符號。符號的意義就是將對一個對同一個名字的讀寫操作都指向同一塊內(nèi)存,即使這些操作分散在不同的.o中。

對于它們,下列三條規(guī)則使用:

  • 同名的強符號只能有一個,否則編譯器報"重復(fù)定義"錯誤。
  • 允許一個強符號和多個弱符號,但定義會選擇強符號的。
  • 當(dāng)有多個弱符號相同時,鏈接器選擇占用內(nèi)存空間最大的那個。

切記比較運算符==不要錯寫為賦值符號=,反之亦然,二者大為不同.詞法分析采用的是從左至右的貪心法,例如a---b等價于a-- -b,而不等價于a- --b;

預(yù)編譯

通常在C編譯系統(tǒng)對程序進行編譯前,先對程序中一些特殊的命令進行“預(yù)處理”,然后將預(yù)處理的結(jié)果和源程序一起進行編譯處理,得到目標(biāo)代碼, 以“#”開始的行成為預(yù)處理指令。

帶參數(shù)的宏與函數(shù)非常類似,在引用函數(shù)時也是在函數(shù)名后的括號 內(nèi)寫實參,且要求實參的數(shù)目等于形參的數(shù)目,但它們還是有區(qū)別的:

對參數(shù)的使用方式不一樣。函數(shù)調(diào)用時,先求出實參表達式的值,然后帶入形參;宏只進行簡單的字符替換。

處理機制不一樣。函數(shù)調(diào)用在程序運行時處理,且要分配內(nèi)存;宏展開在編譯時進行,不分配內(nèi)存單元,不發(fā)生值的傳遞處理,也不存在返回值

定義時的要求不一樣。函數(shù)定義時,實參和形參都要定義類型;宏定義時不存

預(yù)處理程序提供了條件編譯的功能??梢园床煌臈l件去編譯不同的程序部分,因而產(chǎn)生不同的目標(biāo)代碼文件,這對于程序的移植和調(diào)試是很有用的。條件編譯有三種形式:

  1. #ifdef 標(biāo)識符 
  2.     codes1 
  3. #else 
  4.     codes2 
  5. #endif 
  6.  
  7. #ifdef 標(biāo)識符 
  8.     codes3 
  9. #endif  
  10.  
  11. #ifndef 標(biāo)識符 
  12.     codes4 
  13. #else 
  14.     codes5  
  15. #endif 

頭文件

一般的,通過頭文件來調(diào)用庫功能。在很多場合,源代碼不便(或不準(zhǔn))向用戶公布,只要向用戶提供頭文件和二進制的庫即可。用戶只需要按照頭文件中的接口聲明來調(diào)用庫功能,而不必關(guān)心接口怎么實現(xiàn)的。編譯器會從庫中提取相應(yīng)的代碼。頭文件還能加強類型安全檢查。如果某個接口被實現(xiàn)或被使用時,其方式與頭文件中的聲明不一致,編譯器就會指出錯誤,這一簡單的規(guī)則能大大減輕程序員調(diào)試、改錯的負(fù)擔(dān)。

使用尖括號引入的頭文件在包含文件目錄中去查找(包含目錄是由用 戶在設(shè)置環(huán)境時設(shè)置的),而不在源文件目錄去查找。使用雙引號則表示首先在當(dāng)前的源文件目錄中查找,若未找到才到所包含目錄中去查找。用戶編程時可根據(jù)自己的文件所在的目錄來選擇某一種命令形式。

程序框架與庫

C語言中的程序框架是由頭文件,變量聲明,main函數(shù)和子函數(shù)組成。無處不在的helloword 在C中是這樣的:

  1. #include <stdio.h>  
  2. int main() 
  3.     printf("Hello, World! \n");  
  4.     return 0;  

里面沒有變量聲明和子函數(shù)。那沒有main 函數(shù)是否可以呢?或者說,不寫成main函數(shù),換個其他的名字是否可以呢?這涉及到編譯的指定,main 是c中默認(rèn)的調(diào)用入口。

C中的那些庫就大都沒有main函數(shù)。C語言中的庫分為靜態(tài)庫(.a)和動態(tài)庫(.so)。

靜態(tài)庫實際上是一些目標(biāo)文件的集合,用于連接器生成可執(zhí)行文件階段。連接器會將程序中使用到函數(shù)的代碼從庫文件中拷貝到應(yīng)用程序中,一旦連接完成生成可執(zhí)行文件之后,在執(zhí)行程序的時候就不需要靜態(tài)庫了。動態(tài)庫也叫共享庫,在程序鏈接的時候只是作些標(biāo)記,然后在程序開始啟動運行的時候,動態(tài)地加載所需庫(模塊)。

C標(biāo)準(zhǔn)庫有各種不同的實現(xiàn),比如最著名的glibc, 用于嵌入式Linux的uClibc,還有ARM自己的C語言標(biāo)準(zhǔn)庫等。不同標(biāo)準(zhǔn)庫的實現(xiàn)并不相同,提供的函數(shù)也不完全相同,不過有一個它們都支持的最小子集,這也就是最典型的C語言標(biāo)準(zhǔn)庫。

C標(biāo)準(zhǔn)庫由在15個頭文件中聲明的函數(shù)、類型定義和宏組成,每個頭文件都代表了一定范圍的編程功能。有人說,C標(biāo)準(zhǔn)庫可以分為 3 組,如何正確并熟練的使用它們,可區(qū)分出 3 個層次的程序員:

  • 合格程序員:、、
  • 熟練程序員:、
  • 優(yōu)秀程序員:、、、、

運行時

在C語言運行時的數(shù)據(jù)結(jié)構(gòu)中,堆棧為局部變量提供存儲空間,為函數(shù)調(diào)用提供還原信息,其臨時存儲區(qū),用于計算復(fù)雜算術(shù)表達式;調(diào)用記錄支持過程調(diào)用,并記錄調(diào)用結(jié)束后返回調(diào)用點所需要的全部信息;全局變量的數(shù)據(jù)有static變量,常量等。

BSS段(bss segment)

通常是指用來存放程序中未初始化的全局變量的一塊內(nèi)存區(qū)域。BSS段屬于靜態(tài)內(nèi)存分配。

數(shù)據(jù)段(data segment)

通常是指用來存放程序中 已初始化 的 全局變量 的一塊內(nèi)存區(qū)域。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配。

代碼段(code segment/text segment)

通常是指用來存放程序執(zhí)行代碼的一塊內(nèi)存區(qū)域。這部分區(qū)域的大小在程序運行前就已經(jīng)確定,并且內(nèi)存區(qū)域通常屬于 只讀 , 某些架構(gòu)也允許代碼段為可寫,即允許修改程序。在代碼段中,也有可能包含一些 只讀的常數(shù)變量 ,例如字符串常量等。程序段為程序代碼在內(nèi)存中的映射。

堆(heap)

堆是用于存放進程運行中被動態(tài)分配的內(nèi)存段,它的大小并不固定,可動態(tài)擴張或縮減。當(dāng)進程調(diào)用malloc/free等函數(shù)分配內(nèi)存時,新分配的內(nèi)存就被動態(tài)添加到堆上(堆被擴張)/釋放的內(nèi)存從堆中被剔除(堆被縮減)。

棧(stack)

棧又稱堆棧,存放程序的局部變量(但不包括static聲明的變量, static 意味著 在數(shù)據(jù)段中 存放變量)。除此以外,在函數(shù)被調(diào)用時,棧用來傳遞參數(shù)和返回值。由于棧的先進先出特點,所以棧特別方便用來保存/恢復(fù)調(diào)用現(xiàn)場。

程序在進入main函數(shù)之前,已經(jīng)完成數(shù)據(jù)在內(nèi)存中的分配、初始化,包括數(shù)據(jù)區(qū),堆棧區(qū)等。關(guān)于這部分代碼對于開發(fā)者不可見,屬于C標(biāo)準(zhǔn)運行時的一部分。

函數(shù)在調(diào)用和被調(diào)用過程中,都伴隨著入棧和出棧,因此棧發(fā)揮著重要作用。函數(shù)的局部變量、參數(shù)、返回值都存在棧區(qū)中。函數(shù)結(jié)束后,棧區(qū)空間自動釋放,棧區(qū)擔(dān)任著一個臨時存儲的角色,是計算機利用內(nèi)存空間的一種機制。

了解了C 運行時的空間分布是遠遠不夠的,最好了解一下一個編譯后的代碼是如何運行起來的,以及庫中的函數(shù)是如何鏈接到目標(biāo)代碼的,尤其是函數(shù)指針鏈表的維護,之后會有一種對代碼完全掌控的感覺。

不是小結(jié)的小結(jié)

C語言不但能讓我們了解編程的相關(guān)概念,還能讓我們明白程序的運行原理,比如,計算機的各子系統(tǒng)是如何交互,程序在內(nèi)存中是一種怎樣的,操作系統(tǒng)和程序之間的“愛恨情仇”,這些底層知識對程序員的職業(yè)生涯大有裨益。

C語言被一些人譽為“上帝語言”,它幾乎奠定了軟件產(chǎn)業(yè)的基礎(chǔ),還創(chuàng)造了很多其它語言。但是,鑒于水平有限,難以舉重若輕,本文中的基礎(chǔ)描述只是老碼農(nóng)的碎碎念罷了。

 

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2017-04-06 10:27:01

JavaScript基礎(chǔ)Java

2020-07-20 08:23:04

Redis分布式系統(tǒng)

2017-06-13 15:10:02

大數(shù)據(jù)Log日志

2017-06-13 08:55:29

Log日志MySQL

2017-10-12 14:24:24

2017-12-18 15:33:56

Java基礎(chǔ)編程

2017-04-12 14:45:20

數(shù)據(jù)架構(gòu)數(shù)據(jù)源

2017-08-07 13:02:32

全棧必備貝葉斯

2023-12-10 20:30:51

SQL工具數(shù)據(jù)

2018-01-09 15:35:54

Python編程基礎(chǔ)

2023-02-17 08:14:29

C語言C技巧內(nèi)存

2015-08-04 09:40:10

Python大數(shù)據(jù)全棧式

2015-08-17 09:27:51

全棧工程師Devops工具周期表

2023-08-21 09:51:57

全棧軟件開發(fā)

2023-07-03 00:47:23

2017-11-10 19:00:37

華為

2023-12-08 14:32:02

C語言編程文件操作

2013-12-09 09:42:50

JavaScript全棧式

2011-07-25 17:31:49

iPhone Objective-

2013-08-05 15:44:36

C語言基礎(chǔ)
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 在线观看特色大片免费网站 | 亚洲视频在线看 | 精品久久久久久18免费网站 | 二区高清 | 国产精品久久av | 亚洲日韩中文字幕一区 | 国产精品无码永久免费888 | 亚洲在线免费 | 男女免费在线观看视频 | 在线久草 | av看片网站| 91伊人| 成人免费片 | 美女亚洲一区 | 欧美一区二区三区高清视频 | 在线观看特色大片免费网站 | 国产视频1区 | 青青草原精品99久久精品66 | 日韩一区二区三区精品 | 国产精品黄视频 | 国产高清免费视频 | 亚洲综合成人网 | 一色一黄视频 | 亚洲一区二区视频在线观看 | 中国91av| 欧美日韩在线看 | 91高清视频在线 | 日韩三级一区 | 久久成人精品视频 | 日本粉嫩一区二区三区视频 | 99久久婷婷国产综合精品电影 | 欧美激情综合色综合啪啪五月 | 成人一区二区在线 | 成人教育av | 亚洲视频三区 | 欧美色性 | 欧美第一页 | 欧美成人h版在线观看 | 色性av| 自拍偷拍中文字幕 | 日韩三级|