1. 程式人生 > >C語言需要注意的基礎知識點(自己總結的)

C語言需要注意的基礎知識點(自己總結的)

1、     C99標準以前的C要求在一個程式碼塊的開始處集中宣告變數,遵循這條規則的好處是把所有的變數宣告放在一起,會更易於瞭解程式多要做的事情。C99標準則允許把變數宣告分散放置,這樣的好處是可以在準備為變數賦值之前宣告變數,這樣就不會忘記給變數賦值。

2、     編譯器將C語言原始碼編譯成機器語言程式碼,放在一個目的碼檔案中,而後連結器將啟動程式碼(不同擦作業系統的啟動程式碼不同)、庫程式碼(程式中所使用到的,在標頭檔案中所宣告的變數及函式在庫檔案中的原始碼)和目的碼連結起來,生成可執行程式碼,即exe檔案。

3、     printf()中的說明符(佔位符)決定資料的顯示方式,例如:在int為32位的系統中,無符號數3000000000和有符號數-129496296在記憶體中的表示方法是一樣的,但是在printf()中用%u說明符會打印出3000000000,二用%d說明符則會打印出-129496296。

4、    對於float型別的變數,printf()中的說明符可以用%f或%lf,而scanf()中的說明符則只能用%f;

對於double型別的變數,printf()中的說明符可以用%f或%lf,而scanf()中的說明符則只能用%lf;

對於long double型別的變數,printf()中的說明符可以用%f或%lf,而scanf()中的說明符則只能用%lf。

5、     C語言中,把char型別的長度定義為一個位元組,所以,在char型別長為16(一般為8),double型別長為64的系統中,sizeof將報告double型別有4位元組長。

6、     printf()函式什麼時候真正把輸出傳送給螢幕?首先,printf語句將輸出傳遞給一個被稱為緩衝區(buffer)的中介儲存區。緩衝區中的內容再不斷地被傳遞給螢幕。標準C規定在以下幾種情況下將緩衝區內容傳給螢幕:緩衝區滿的時候、遇到換行符的時候以及需要輸入的時候。將緩衝區內容傳送給螢幕或檔案成為重新整理緩衝區。

7、     sizeof()函式以位元組為單位給出資料的大小,strlen()函式以字元為單位給出字串的長度。而一個字元只佔用一個位元組,那是不是這兩個函式應用到同一個字串時可以得到相同的結果呢?事實並非如此,例如,對於一個數組長度為40的char型陣列,假設其中實際儲存的字元個數為6,則用sizeof讀取出的長度為40,而用strlen()函式讀出的長度為6(陣列的第7個單元放置空字元,即‘\0’,strlen()函式在這裡停止計數)。再比如,對於字串,strlen()函式讀取的是雙引號中最後一個非空白字元前的字元的個數(包含該非空白字元),包括其中的標點、空白字元等字元,而sizeof()函式讀取的是雙引號中的所有字元的個數,包括最後的空白字元。一般用strlen()函式獲取一個字串的長度(不包含標示終止的空字元)。

8、     C語言中規定,sizeof()返回size_t型別的值,這是一個無符整數型別,但它不是一個新型別,而是在C的標頭檔案中通過typedef將其建立為unsigned int 或unsigned long的別名。

9、     空白字元與空字元的區別:空白字元是指在螢幕上不會顯示出來的字元(如:空格、製表符tab、回車換行等),而空字元是指‘\0’,或者是字元的編碼值為0的字元,它是非列印字元。C的字串儲存時通常以空字元結束,該字元的存在意味著陣列的單元數必須至少比要儲存的字元數多1。

10、    printf()的返回值為所列印的字元的數目,如果有輸出錯誤,它會返回一個負數。scanf()函式返回成功讀入的專案的個數,如果它沒有讀取任何專案(當它期望一個數字而你卻鍵入一個非數字的字串時就會發生這種情況),它會返回0,而當它檢測到“文字結尾”,它會返回EOF(在stdio.h中,它被定義為-1)。

11、    printf()語句過長,要在多行放置一個列印語句的三種方法:

方法1、使用多個printf()語句。

方法2、用反斜線符號(/)和回車鍵的組合結束第一行,這樣下一行必須從行的最左邊開始。

方法3、採用字串連線的方法,它是ANSI C的新方法。如果在一個用雙引號引起來的字串後面跟有另一個用雙引號引起來的字串,而且二者之間僅用空白字元隔開,那麼C會把該組合當做一個字串處理。

12、    printf()和scanf()中的*修飾符:

一、在printf()中,假設你不想實現指定欄位寬度,而是希望由程式來制定該值,那麼可以在欄位寬度部分使用*代替數字,但是這樣的話,你也必須使用一個引數告訴函式欄位寬度應該是什麼,也就是說,如果轉換說明符是%*d,那麼引數列表中應該包含一個*的值和一個d的值。

二、在scanf()中,*則提供截然不同的服務,當把它放在%和說明符字母之間時,它會是函式跳過相應的輸入專案。

13、  深入理解scanf()的輸入:首先應明白空白字元對於sacnf()如何處理輸入起著至關重要的作用,除了在%c模式下外,在讀取輸入時,scanf()會跳過空白字元直到第一個非空白的字元處,然後會一直讀取字元,直到遇到空白字元,或遇到一個不符合正在讀取的型別的字元,或達到指定的欄位長度(由三者中最先滿足的那一個終止輸入)而停止讀取,並將停止處的字元作為下一個輸入字元如果是空白字元,下次讀取時也會跳過空白字元,但下次讀取如果是在%c模式下,就不會跳過去了。scanf()在%c模式下讀取字元的時候,則不會跳過空白字元,而是直接讀取第一個輸入的字元。scanf()在%s模式下讀取字串的時候,由於遇到單詞間的空格鍵而停止讀取,故只能讀取到第一個單詞,而不是整個字串語句。

14、  C99標準為整數及浮點數除法規定了“趨零截尾”的規則,因此“/”計算的結果應該遵循趨近0並去掉小數點的原則,尤其在有負數的時候。也因此,對於負數的取模運算(%),得到的模的正負性與第一個運算元的正負性相同。

15、  字元陣列與字串:一般來說,字元陣列就是元素都被賦予字元值的陣列,如果字元陣列包含了“\0”,那麼字元陣列的內容就構成一個字串,其中空字元標誌著字串的結尾。

16、  C99標準為邏輯運算子增加了可選擇的拼寫法,and可以代替&&,or可以代替||,not可以代替!

17、  在使用getchar()函式讀取字元時,由於最終要輸入回車符來發送輸入,因此往往要用到fflush(stdin)來重新整理輸入緩衝區,或者使用如下語句來跳過輸入行的剩餘部分(包括最後輸入的回車)。

while(getchar()!=’\n’)

continue;                                              

      同樣,由於scanf()函式在讀取非法字元時會停在那裡,並把該字元放回輸入,下次讀取的時候依然從該字元讀取,這樣便永遠不會超過這個非法字元,那麼往往也需要上述兩種方法來解決這個問題。

18、  對於非緩衝輸入,一般在按下要求的鍵之後,會立即傳送輸入,而對於緩衝輸入,一般當按下回車時,才會傳送輸入。

19、  緩衝分為兩種:完全緩衝I/O和行緩衝I/O。對於完全緩衝輸入來說,緩衝區滿時被清空(內容被髮送至目的地),這種型別的緩衝通常出現在文字輸入中,緩衝區的大小取決於系統,512位元組和4096位元組是常見的值;對於行緩衝輸入來說,遇到一個換行字元時將清空緩衝區。鍵盤輸入是標準的行緩衝,因此按下回車鍵時將清空緩衝區。

20、  讀取字元時scanf()與getchar():scanf()在遇到換行符時,會將其停留在輸入佇列中,下次讀取時不會跳過該換行符,getchar()也不會跳過換行符。如果混合使用scanf()和getchar(),那麼當呼叫getchar()之前scanf()恰好在輸入中留下了一個換行符時,將會產生問題,運用重新整理輸入緩衝區或跳過輸入行剩餘部分等技巧可以解決這類問題。

21、  在大多數系統內部,指標由一個無符號整數表示,但這並不表示可以把指標看做是整數型別。ANSI C專門為指標提供了%p輸出格式。

22、  C保證為陣列分配記憶體空間時,指向陣列之後的第一個位置的指標是也合法的,但是對該位置上的值不做任何保證。,它可能被系統的其他程式佔用,也可能是垃圾值。

23、  無論在何種情況下,int *ar都表示ar是指向int的指標。形式int ar[]也可以表示ar是執行int的指標,但只有在宣告形式參量時才可以這樣使用。另外在C語言中,兩個表示式ar[i]和*ar(i+1)的意義是等價的,而且不管ar是一個數組名還是一個指標變數,這兩個表示式都可以工作。然而只有當ar是一個指標變數時,才可以使用ar++這樣的表示式。

24、  使用指標時一定要注意,不能對未初始化的指標取值。

25、  const int *p 與int * const p的不同是,前者定義指標p所指向的int值為常量,不可改變,而p的值是可以改變的,後者定義指標p的值為常量,即其指向不可改變,而*p的值是可以被改變的。

26、  const指標(這種形式:const int *p)不能賦給非const指標,但是非const指標賦給const指標是允許的,這樣的賦值有一個前提:只能進行一層間接運算。

27、  函式是通過指標來處理多維陣列的,在編譯時,編譯器會把陣列符號轉換為指標符號,編譯器在轉換時需要知道指標所指向的物件資料的大小。

28、  由於在處理二維陣列函式時,陣列的列只能被預置在函式內部,因此C99標準引入了變長陣列(VLA)。這裡的“變”不是指建立陣列後,可以修改其大小,而是指其維數大小可以由變數指定。例如如下宣告:

Int sum(int rows,int cols,int ar[rows][cols]),這裡前兩個參量是ar陣列的行數和列數。注意:引數列表中,rows和cols的宣告需要早於對陣列ar的宣告。

29、  字串常量屬於靜態儲存類。靜態儲存是指如果在一個函式中使用字串常量,即使多次呼叫了這個函式,該字串在程式的整個執行中只儲存一份。字串的整個引號中的內容作為指向該字串儲存位置的指標。

30、  char *arr[4]與char arr[4][10]的區別:前者表示的是一個指向char的指標的陣列,其中存放4個地址,而後者是一個char陣列的陣列,其中存放4個完整的字串陣列。

31、  空指標(NULL)與空字元(\0)的區別:空指標是一個地址,而空字元是一個char型別的資料物件。數值上二者都可以用0表示,但是它們的概念不同。

32、  gets()、puts()和fgets()、fputs():在讀取字串時,gets()會把最後的換行符捨去,併為未字串加上空字元,但是不檢查預留儲存區是否能容納實際輸入的資料,fgets()會指定最大讀入的字元數,但是會讀取換行符。puts()顯示字串時會自動在其後新增一個換行符,另外,puts()在遇到空字元時才會停下來,所以應該保證空字元的存在,fputs()並不為輸出自動新增換行符。gets()和puts()一起使用,它們一般面向鍵盤螢幕輸入輸出,fgets()和fputs()一起使用,它們一般面向檔案輸入輸出。

33、  傳統上,具有程式碼塊作用域的變數都必須在程式碼塊的開始處宣告,C99標準放寬了這一規則,允許在一個程式碼塊的任何位置宣告變數,一個新的可能是變數宣告可以出現在for迴圈的控制部分。

34、  一個C變數具有下列連結之一:外部連結、內部連結、空連線。具有程式碼塊作用域或者函式原型作用域的變數具有空連結,意味著它們是由其定義所在的程式碼塊或者函式原型所私有的,具有檔案作用域的變數可能有內部或外部連結,這要取決於在外部定義中是否使用了static說明符,若使用了,則具有內部連結,若沒有使用,則具有外部連結。一個具有外部連結的變數可以在一個多檔案程式的任何地方使用,一個具有內部連結的變數可以在一個檔案的任何地方使用。

35、  一個C變數有以下兩種儲存時期之一:靜態儲存時期和自動儲存時期。如果一個變數具有靜態儲存時期,它在程式執行期間將一直存在,具有檔案作用域的變數具有靜態儲存時期,具有程式碼塊作用域的變數一般情況下具有自動儲存時期,在程式進入定義這些變數的程式碼塊時,將為這些變數分配記憶體,當退出該程式碼塊時,分配的記憶體將被釋放。另外,static關鍵字表明連結型別,並非儲存時期

36、  C使用作用域、連結和儲存時期等定義了5種儲存類:自動變數、暫存器變數、具有程式碼塊作用域的靜態變數、具有外部連結的靜態變數、具有內部連結的靜態變數。

一、自動變數,它具有自動儲存時期、程式碼塊作用域和空連結,可以用auto關鍵字宣告,也可以不宣告,自動變數需要顯式地初始化,而不能自動初始化為某些預設值。

二、暫存器變數,多是存放在一個暫存器而非記憶體中,所以無法獲得其地址,它具有自動儲存時期、程式碼塊作用域和空連結,通過register說明符宣告,如未經初始化,它的值是不定的。

三、具有程式碼塊作用域的靜態變數,它具有程式碼塊作用域、空連線,卻有靜態儲存時期,也就是說,當包含這些變數的函式完成工作時,他們並不消失,且對它們的值的改變會一直儲存下來,其記憶體空間當程式結束時才會被釋放,在程式碼塊內通過static關鍵字宣告。另外,如果不顯式地對該靜態變數初始化,他們將被初始化為預設值0,僅在編譯時被初始化一次。

四、具有外部連結的靜態變數,它具有文字作用域、外部連結和靜態儲存時期,這一類的變數被稱為外部變數。把變數的定義宣告放在所有函式之外,即建立了一個外部變數。可以在使用外部變數的函式中通過使用extern關鍵字再次宣告它,如果變數是在別的檔案中定義的,使用extern來宣告該變數就是必須的。如果不對外部變數顯式地初始化,它將自動被賦初值0,這一原則也適用於外部定義的陣列元素,另外,只可以用常量表達式來初始化檔案作用域變數,且一個外部變數只僅在編譯時進行一次初始化,而且一定是在變數被定義時進行。

五、具有內部連結的靜態變數,它具有文字作用域、內部連結和靜態儲存時期,通過使用static說明符在所有函式外部進行定義,只能在本檔案中使用,僅在編譯時初始化一次,若未明確初始化,將被初始化為0。

 37、 alloca向棧申請記憶體,因此無需釋放,malloc、calloc等申請的記憶體均在堆中,區別是,malloc申請的記憶體中的內容沒有被初始化,而calloc申請的記憶體中的內容被初始化為0或空指標(無法保證),浮點型的0(某些系統中)等,realloc對給定的指標所指的空間進行擴大或縮小。

 38、 getc()和putc()在輸入輸出字元時需要傳入一個檔案指標做外引數,從而確定輸入輸出的檔名稱。getc(stdin)和getchar()等價,在原始碼中,一般後者通過前者實現,putc(ch,stdout)和putchar(ch)等價,在原始碼中,一般後者通過前者實現。

 39、 printf()將列印輸入到螢幕,sprintf()將列印輸入到指定的字串中,它的第一個引數為目標字串的首地址,fprintf()則將列印輸入到指定的檔案中,它的第一個引數是一個檔案指標,但該引數為stdout時,與   printf()等價。

 40、和陣列不同,一個結構體的名字不是該結構的地址,必須使用&運算子。另外,現在的C允許把一個結構賦值給另一個結構,即使該結構中有一個成員是陣列也能完成賦值,但是對陣列卻不能這樣做。

 41、 如果必須寫一個與結構有關的函式,應該用結構體指標作為引數還是用結構體作為引數並返回值呢?前者執行效率高,但是缺少對資料的保護,後者對資料有保護,但是執行效率慢。通常,程式設計師為了追求效率而是用結構指標作為函式引數,當需要保護資料、防止意外改變資料時,對指標使用const限定詞。而傳遞結構體值是處理小型結構最常用的方法。

 42、 如果在結構體中要儲存字串,使用字元陣列好,還是用指向字元的指標比較好呢?要使用字元陣列來儲存。因為字元陣列中的字串變數是儲存在結構體中的,而指向字元的指標僅在結構體中儲存字串的地址,字串則儲存在編譯器儲存字串常量的任何地方,如果對該指標未初始化而直接用scanf()輸入的話,該字串將可能被放在任何地方,這是很危險的,當然如果使用malloc()進行動態分配的話,也可以使用指向字元的指標來儲存字串。

 43、 聯合(union)是一個能在同一個儲存空間裡(但不同時)儲存不同型別資料的資料型別。聯合的初始化有3中選擇:可以把一個聯合初始化為同類型的另一個聯合;可以初始化聯合的第一個元素;或者,按照C99標準,可以使用一個指定初始化專案。不能在定義聯合體的時候對它初始化,不能用聯合體變數名作為函式引數。

 44、 列舉型別使用enum關鍵字宣告代表整數常量的符號名稱,enum常量是int型的,因此在使用int型別的任何地方都可以使用它,但不能將一個整數直接賦給一個列舉變數,應先進行強制型別轉化才能賦值。enum中的第一個常量的預設值是0,依次遞增,由於是常量,因此不能對它們賦值,如果要指定值,只能在宣告列舉類時指定列舉元素的值。另外:C的某些列舉屬性不能延至C++中,例如,C允許列舉變數使用運算子++,而C++不允許,要想在C++下也能工作,就要把用enum關鍵字宣告的變數改用int宣告。

 45、 前處理器不會進行計算,只是按照指令進行文字替換操作,在編譯時才會進行計算。在巨集中不要使用增減運算子,如++。

 46、 atexit()和exit():atexit()使用函式指標作為引數,用來註冊函式(應該是不接手機任何引數的void函式),將這些函式放入註冊列表中(ANSI C保證這個列表至少可放置32個函式),最後,呼叫exit()函式時,按照先進後出的順序執行這些函式。exit()函式在執行了atexit()指定的函式後,精做一些自身清理工作,最後終止程式。注意:main()終止時會隱式地呼叫exit(),而且最main()以外的函式中使用exit()也會終止程式。

47、char arr[] = abdedf和char *brr = abdedf,前者的元素可以修改,後者不可修改,這是因為陣列初始化是從靜態儲存區把一個字串複製給陣列,而指標初始化只是複製字串的地址。Sizeof(arr)會得到整個陣列所佔的位元組數,sizeof(brr)得到的則是指標變數所佔的位元組數,因為brr是一個指向字串abdedf的首字元的字元指標。另外,如果在函式中以char crr[]為形參,則crr會被看成是指標,因此在該函式內部,sizeof(crr)得到的是指標變數所佔的位元組數(大多數系統中一般為4)。

48、typedef 和 define:typedef給出的符號名稱僅限於對型別,而不是對值,#define既可以對值,也可以針對型別(只能是一部分型別)。

49、在演算法中可能會經常用到如下函式(math.h庫中)

double floor(double x)—>返回不大於x的最大整數值;

double ceil(double x)—>返回不小於x的最小整數值;

double pow(double x,double y)—>返回x的y次冪;

double exp(double x,)—>返回e的x次冪;

double log10(double x)—>返回x的以10為底的對數;

double log(double x)—>返回x自然對數值(即以e為底);

double sqrt(double x)—>返回x的平方根;

double fabs(double x)—>返回x的絕對值;

50、作業系統中的記憶體分配(結合C):堆:用於存放程序執行中被動態分配的記憶體段,它的大小並不固定,可動態擴張或縮減。當程序呼叫malloc時,新分配的內存就被動態新增到堆上,呼叫free時,記憶體就會從堆中釋放;棧:又稱堆疊,存放程式的區域性變數和形參等(不包括區域性宣告的static變數,它儲存在靜態儲存區中的資料段中),在呼叫函式時,棧可以用來傳遞引數和返回值;BSS段:Block Started by Symbol,是靜態儲存區的一部分,用來儲存未初始化的全域性變數,屬於靜態記憶體分配;
資料段:是靜態儲存區的一部分,用來儲存已初始化的全域性變數和static宣告的變數,屬於靜態記憶體分配;程式碼段:通常是指用來存放程式執行程式碼的一塊記憶體區域,這部分割槽域的大小在程式執行前就已經確定,並且記憶體區域通常屬於讀 , 某些架構也允許程式碼段為可寫,即允許修改程式。程式段為程式程式碼在記憶體中的對映.一個程式可以在記憶體中多有個副本。文字常量區:常量字串就放在這裡,比如char *str = "abc",這裡字串abc就儲存在文字常量區,也因此不可修改,如這樣的操作str[1]='t'是違規的,而指標str則儲存在棧中,它指向該字串。另外需要注意,char str[] = "abc",這裡的字串abc是可以改變的,即操作str[1]='t'是合法的,這是因為它從常量區中複製了一個副本儲存在str陣列中。