1. 程式人生 > >C語言知識點

C語言知識點

1.C語言之父是丹尼斯·裡奇,他不僅是C語言之父還是UNIX之父;

  2.C語言的三個特點(為什麼嵌入式的開發選擇C語言為開發語言)

(1)執行的效率高:C語言的目的碼效率一般只比彙編程式低10%~20%,因此在同類的語言中,C語言具有生成目的碼質量高、程式執行的效率高的優點。

(2)程式碼的移植性好:C語言有一個突出的優點就是適用的範圍大,適合於DOS,UNIX、Linux、Windows等多種作業系統也適合與多種的機型。只要將一個硬體型號下的程式稍加修改,甚至不加修改,就可移植到另一個不同的硬體型號開發環境使用,這樣,方便了程式的移植和程式碼重用。

(3)可以直接對硬體進行操作:由於C語言允許直接訪問實體地址,可以直接對硬體進行操作。這是它既有高階語言的功能,又具有低階語言的許多功能的重要表現。C能夠像組合語言一樣對位,位元組和地址進行操作,而這三者是計算機最基本的工作的單元,可用來寫系統的檔案。

另外,C語言具有強大的圖形的功能,支援多種顯示器和驅動器,且計算功能、邏輯判斷功能強

3.

1.編譯

編譯型語言寫的程式執行之前,需要一個專門的編譯過程,把程式編譯成為機器語言的檔案,比如exe檔案,以後要執行的話就不用重新翻譯了,直接使用編譯的結果就行了(exe檔案),因為翻譯只做了一次,執行時不需要翻譯,所以編譯型語言的程式執行效率高,但也不能一概而論,部分解釋型語言的直譯器通過在執行時動態優化程式碼,甚至能夠使解釋型語言的效能超過編譯型語言。

最典型的例子就是C語言。

2.解釋

解釋則不同,解釋性語言的程式不需要編譯,省了道工序,解釋性語言在執行程式的時候才翻譯,比如解釋性basic語言,專門有一個直譯器能夠直接執行basic程式,每個語句都是執行的時候才翻譯。這樣解釋性語言每執行一次就要翻譯一次,效率比較低。解釋是一句一句的翻譯。

此外,隨著Java等基於虛擬機器的語言的興起,我們又不能把語言純粹地分成解釋型和編譯型這兩種。

用Java來舉例,Java首先是通過編譯器編譯成位元組碼檔案,然後在執行時通過直譯器給解釋成機器檔案。所以我們說Java是一種先編譯後解釋的語言。

再換成C#,C#首先是通過編譯器將C#檔案編譯成IL檔案,然後在通過CLR將IL檔案編譯成機器檔案。所以我們說C#是一門純編譯語言,但是C#是一門需要二次編譯的語言。同理也可等效運用到基於.NET平臺上的其他語言。

4.

	面向過程一種以事件為中心的程式設計思想,以功能(行為)為導向,按模組化的設計,就是分析出解決問題所需要的步驟,然後用函式把這些步驟一步一步實現,使用的時候一個一個依次呼叫就可以了。
    	面向物件一種以事物為中心的程式設計思想,以資料(屬性)為導向,將具有相同一個或者多個屬性的物體抽象為“類”,將他們包裝起來;而有了這些資料(屬性)之後,我們再考慮他們的行為(對這些屬性進行什麼樣的操作),是把構成問題事務分解成各個物件,建立物件的目的不是為了完成一個步驟,而是為了描敘某個事物在整個解決問題的步驟中的行為。

面向過程和麵向物件的區別 面向過程適合處理簡單的事情,直接關注流程,簡單有效。 面向物件適合處理複雜的事情,先使用面向物件的方式對整體關係作出分類,然後,根據不同的類深入細節的處理。 面向過程和麵向物件的聯絡 面向物件無法取代面向過程,他們是相輔相成的。面向物件關注於從巨集觀上把握事物之間的關係,在具體到如何實現某個細節時,仍然採用面向過程的思維方式。 面向物件如何離開了面向過程,就無法實現真正的落地,成為無源之水。 面向物件技術的優點 面向物件技術具有程式結構清晰,自動生成程式框架,實現簡單,維護簡單,程式碼重用率高,軟體開發效率高等優點。

5.

在c語言中,可以用結構+函式指標來模擬類的實現,而用這種結構定義的變數就是物件。我們可以在標頭檔案中宣告,在c檔案中真正定義,這樣可以隱藏內部資訊,因為外部不知道物件所佔記憶體的大小,所以不能靜態的建立該類的物件,只能呼叫類提供的建立函式才能建立。這種方法的缺陷是不支援繼承,因為子類中得不到任何關於父類的資訊。

在c語言中,可以利用“結構在記憶體中的佈局與結構的宣告具有一致的順序”這一事實實現繼承。

在c語言中我們可以用萬能指標void*來實現多型。

6.

sizeof和strlen的區別:

1.sizeof是個關鍵字,因此,sizeof後面是變數名時可以不加括號,而strlen是個函式,必須加括號

2.sizeof是判斷引數所佔的記憶體大小,引數可以是型別,函式,而strlen有點像計數器,從某個記憶體地址開始計數,碰到"\0"時結束計數,引數只能是char *

3.sizeof編譯時確定,strlen時執行時確定

4.在呼叫函式中,陣列名作為形參的,會被編譯器當成指向陣列首地址的指標,因此作為sizeof的引數時等價於計算指標的大小,作為strlen()的引數時意義未改變

陣列和指標的區別:

1.定義時,當等號右邊為字串時,陣列是是分配在非常量區,指標指向的字串被分配在在常量區(只讀),因此陣列可通過下標更改其值,指標不可更改!

2.佔用的空間大小不同,陣列佔用所申請的記憶體大小,指標為4個位元組(假定計算機的定址大小為32位)

7.

gets()函式總結:

(1)gets() 從標準輸入裝置讀取字串,以回車結束讀取,使用'\0'結尾,回車符'\n'被捨棄沒有遺留在緩衝區。

(2)可以用來輸入帶空格的字串。

(3)可以無限讀取,不會判斷上限,因此使用gets不安全,可能會造成溢位(列印時候出線段錯誤)

scanf()函式總結:

(1)scanf() 以 空格 或 回車符 結束讀取,空格 或 回車符 會遺留在緩衝區。

(2)不能直接輸入帶空格的字串。

注意:scanf()函式如何才能輸入帶空格的字串,應使用:【具體程式碼和輸出內容如下:】

scanf("%[^\n]", a);  //%[]輸入字符集, [^\n] 表示除了'\n'之外的字元都接收,即可以接收空格,這個可以用來輸入帶空格的字串

9.宣告:告訴編譯器變數的型別 在哪裡 或者函式的特徵(返回值 引數型別 個數)
定義:告訴編譯器 在此處分配儲存空間 建立變數和函式

10.

在C語言中,對變數的儲存型別說明有以下四種:

        auto          自動變數

        register     暫存器變數

        extern       外部變數

        static         靜態變數

         所謂儲存型別是指變數佔用記憶體空間的方式,也稱為儲存方式。變數的儲存方式可分為“靜態儲存”和“動態儲存”兩種。

         靜態儲存變數通常是在變數定義時就在儲存單元並一直保持不變,直至整個程式結束。動態儲存變數是在程式執行過程中,使用它時才分配儲存單元,使用完畢立即釋放。典型的例子是函式的形式引數,在函式定義時並不給形參分配儲存單元,只是在函式被呼叫時,才予以分配,呼叫函式完畢立即釋放。如果一個函式被多次呼叫,則反覆地分配、釋放形參變數的儲存單元。從以上分析可知,靜態儲存變數是一直存在的,而動態儲存變數則時而存在時而消失。

       我們又把這種由於變數儲存方式不同而產生的特性稱變數的生存期。生存期表示了變數存在的時間。生存期和作用域是從時間和空間這兩個不同的角度來描述變數的特性,這兩者既有聯絡,又有區別。一個變數究竟屬於哪一種儲存方式,並不能僅從其作用域來判斷,還應有明確的儲存型別說明。

       自動變數和暫存器變數屬於動態儲存方式,外部變數和靜態變數屬於靜態儲存方式。在介紹了變數的儲存型別之後,可以知道對一個變數的說明不僅應說明其資料型別,還應說明其儲存型別。因此變數說明的完整形式應為:儲存型別說明符 資料型別說明符 變數名,變數名....;

       總結與區別:變數根據定義的位置的不同的生命週期,具有不同的作用域,作用域可分為6種:全域性作用域,區域性作用域,語句作用域,類作用域,名稱空間作用域和檔案作用域。

      從作用域看:

       全域性變數具有全域性作用域。全域性變數只需在一個原始檔中定義,就可以作用於所有的原始檔。當然,其他不包含全域性變數的定義的原始檔需要用extern關鍵字再次宣告這個全域性變數。

       靜態區域性變數具有區域性作用域,它只被初始化一次,自從第一次被初始化直到程式執行結束一直存在,它和全域性變數的區別在於全域性變數對所有函式都是可見的,而靜態區域性變數只對定義自己的函式體始終可見。

       區域性變數也只有區域性作用域,它是自動物件(auto),它在程式執行期間不是一直存在,而是隻在函式執行期間存在,函式的一次呼叫執行結束後,變數被撤銷,其所佔用的記憶體也被收回。

       靜態全域性變數也具有全域性作用域,它與全域性變數的區別在於如果程式包含多個檔案的話,它作用於定義它檔案裡,不能作用到其他檔案裡,即被static關鍵字修飾過的變數具有檔案作用域。這樣即使兩個不同的原始檔都定義了相同名字的靜態全域性變數,它們也是不同的變數。

       從分配空間看:

       全域性變數,靜態區域性變數,靜態全域性變數都在靜態儲存區分配空間,而區域性變數在棧裡分配空間。

       全域性變數本身就是靜態儲存方式,靜態全域性變數當然也是靜態儲存方式。這兩者在儲存方式上並無不同。這兩者的區別雖在於非靜態全域性變數的作用域是整個源程式,當一個源程式由多個原始檔組成時,非靜態的全域性變數在各個原始檔中都是有效的。而靜態全域性變數則限制了其作用域,即只在定義該變數的原始檔內有效,在同一個源程式的其他原始檔中不能使用它。由於靜態全域性變數的作用域侷限於一個原始檔內,只能為該原始檔內的函式公用,因此可以避免在其他原始檔中引起錯誤。

       (1)靜態變數會放在程式的靜態資料儲存區(全域性可見)中,這樣可以在下一次呼叫的時候還可以保持原來的賦值。這一點是它與堆疊變數和堆變數的區別。

       (2)變數用static告知編譯器,自己僅僅在變數的作用範圍內可見。這一點是它與全域性變數的區別。

對於register變數的注意點:

1.用register修飾的變數只能是區域性變數,不能是全域性變數。因為全域性變數的生命週期是從執行程式開始,一直到程式結束才會終止,而register變數可能會存放在cpu的暫存器中,如果在程式的整個生命週期內都佔用著暫存器的話,這是個相當不好的舉措,CPU的暫存器資源有限,因此不可能讓一個變數一直佔著CPU暫存器。(編譯時候報錯)

2.register變數一定要是CPU可以接受的值。

4.不可以用&運算子對register變數進行取址。

5.register只是請求暫存器變數,不一定能夠成功。

11.

1.儲存位置不同:全域性變數儲存在方法區,區域性變數儲存在棧區

2.生命週期不同:全域性變數的生命期和主程式一樣,隨著程式的銷燬而銷燬,區域性變數在函式內部,函式退出了就不存在

3.使用方式不同:全域性變數在聲明後程式的各個部分都可以用到,但是區域性變數只能在區域性使用。函式內部會優先使用區域性變數再使用全域性變數

4.未初始化的全域性變數為0,未初始化的區域性變數為垃圾值

12.全域性變數可分為靜態全域性變數和普通全域性變數,兩者生命週期都是從定義開始到程式執行結束,都是儲存在資料段

靜態全域性變數只在本原始檔可見,其他檔案不可訪問,普通全域性變數在其他原程式中宣告為extern變數後就可以使用

13.變數:儲存位置, 生命週期, 初始化(未初始化的靜態區域性變數,未初始化的靜態全域性變數和未初始化的全域性變數,都為0,未初始化的區域性變數為垃圾值)

函式: 靜態函式
在函式的返回型別前加上關鍵字static,函式就被定義成為靜態函式。
函式的定義和宣告預設情況下是extern的,但靜態函式只是在宣告他的檔案當中可見,不能被其他檔案所用。
定義靜態函式的好處:
<1> 其他檔案中可以定義相同名字的函式,不會發生衝突
<2> 靜態函式不能被其他檔案所用。 儲存說明符auto,register,extern,static,對應兩種儲存期:自動儲存期和靜態儲存期。 auto和register對應自動儲存期。具有自動儲存期的變數在進入宣告該變數的程式塊時被建立,它在該程式塊活動時存在,退出該程式塊時撤銷。
關鍵字extern和static用來說明具有靜態儲存期的變數和函式。用static宣告的區域性變數具有靜態儲存持續期(static storage duration),或靜態範圍(static extent)。雖然他的值在函式呼叫之間保持有效,但是其名字的可視性仍限制在其區域性域內。靜態區域性物件在程式執行到該物件的宣告處時被首次初始化。

14.

動態儲存方式
所謂動態儲存方式是指在程式執行期間根據需要進行動態的分配儲存空間的方式。
動態儲存變數是在程式執行過程中,使用它時才分配儲存單元, 使用完畢立即釋放。

靜態儲存方式
所謂靜態儲存方式是指在程式編譯期間分配固定的儲存空間的方式。
該儲存方式通常是在變數定義時就分定儲存單元並一直保持不變, 

直至整個程式結束。全域性變數,靜態變數等就屬於此類儲存方式

分為靜態全域性,全域性,靜態區域性,區域性四個來闡述區別

若在記憶體中,則資料可以”隨機存取”,但記憶體資料被讀取或寫入時,所需要的時間與這段資訊所在的位置無關.但是在讀取和寫入磁碟時,其所需要的時間與位置就會有關係.因為在BASIC,PASCAL和C/C++語言中,陣列的存放是按照行優先來存放的,按行號第一行第二行…以此類推.本體關鍵是考察記憶體抖動的問題,如果按列訪問則需要跳過一大串記憶體地址,這樣可能需求的記憶體地址不在當前頁中則需要進行頁置換,這樣便需要硬碟IO,減低速度.