c/c++0-語言基礎


0,參考鏈接:

C語言中文網

C library:提供了所有C語言標准函數的原型,並給出了詳細的介紹和使用示例,可以作為一部權威的參考手冊。

C++ STL標准手冊

1,基本數據類型,輸入輸出,運算符

基本數據類型

字符型 短整型 整型 長整型 單精度浮點型 雙精度浮點型
數據類型 char short int long float double
長度 1 2 4 4 4 8

(1)獲取某個數據類型的長度可以使用 sizeof 操作符
(2)不希望設置符號位,可以在數據類型前面加上 unsigned 關鍵字
(3)字符串定義/數組和指針: char str1[] = "abc"; char *str2 = "abc";這兩種形式是有區別的,第一種形式的字符串所在的內存既有讀取權限又有寫入權限,第二種形式的字符串所在的內存只有讀取權限,沒有寫入權限。printf()、puts() 等字符串輸出函數只要求字符串有讀取權限,而 scanf()、gets() 等字符串輸入函數要求字符串有寫入權限,所以,第一種形式的字符串既可以用於輸出函數又可以用於輸入函數,而第二種形式的字符串只能用於輸出函數。
(4)當除數和被除數都是整數時,運算結果也是整數;如果不能整除,那么就直接丟掉小數部分,只保留整數部分;一旦除數和被除數中有一個是小數,那么運算結果也是小數,並且是 double 類型的小數。
(5)C語言中的取余運算只能針對整數,也就是說%的兩邊都必須是整數;余數可以是正數也可以是負數,由 % 左邊的整數決定:如果 % 左邊是正數,那么余數也是正數;如果 % 左邊是負數,那么余數也是負數。
(6)++ 在變量前面和后面是有區別的:++ 在前面叫做前自增(例如 ++a)。前自增先進行自增運算,再進行其他操作。++ 在后面叫做后自增(例如 a++)。后自增先進行其他操作,再進行自增運算。自減(--)也一樣,有前自減和后自減之分。
(7)強制類型轉換的格式為: (type_name) expression

輸入

gets():獲取一行數據,並作為字符串處理。gets() 能讀取含有空格的字符串,而 scanf()不能(其實用上正則表達式也可以)。
getchar()、getche()、getch():這三個函數都用於輸入單個字符。

函數 緩沖區 頭文件 回顯 適用平台
getchar() stdio.h Windows、Linux、Mac OS 等所有平台
getche() conio.h Windows
getch() conio.h Windows
scanf()/scanf_s():和 printf() 類似,scanf() 可以輸入多種類型的數據,並且可以使用正則表達式控制輸入的范圍。從本質上講,我們從鍵盤輸入的數據並沒有直接交給 scanf(),而是放入了緩沖區中,直到我們按下回車鍵,scanf() 才到緩沖區中讀取數據。如果緩沖區中的數據符合 scanf() 的要求,那么就讀取結束;如果不符合要求,那么就繼續等待用戶輸入,或者干脆讀取失敗。
輸入類型控制符 說明
%c 讀取一個單一的字符
%hd、%d、%ld 讀取一個十進制整數,並分別賦值給 short、int、long 類型
%ho、%o、%lo 讀取一個八進制整數(可帶前綴也可不帶),並分別賦值給 short、int、long 類型
%hx、%x、%lx 讀取一個十六進制整數(可帶前綴也可不帶),並分別賦值給 short、int、long 類型
%hu、%u、%lu 讀取一個無符號整數,並分別賦值給 unsigned short、unsigned int、unsigned long 類型
%f、%lf 讀取一個十進制形式的小數,並分別賦值給 float、double 類型
%e、%le 讀取一個指數形式的小數,並分別賦值給 float、double 類型
%g、%lg 既可以讀取一個十進制形式的小數,也可以讀取一個指數形式的小數,並分別賦值給 float、double 類型
%s 讀取一個字符串(以空白符為結束)

輸出

puts():只能輸出字符串,並且輸出結束后會自動換行
putchar():只能輸出單個字符
printf():可以輸出各種類型的數據,並借助格式控制符控制輸出格式(在 printf 中,% 是格式控制符的開頭,是一個特殊的字符,不能直接輸出;要想輸出 %,必須在它的前面再加一個 %,這個時候 % 就變成了普通的字符,而不是用來表示格式控制符了。)(printf() 格式控制符的完整形式:%[flag][width][.precision]type)

輸出類型控制符type 說明
%c 輸出一個單一的字符
%hd、%d、%ld 以十進制、有符號的形式輸出 short、int、long 類型的整數
%hu、%u、%lu 以十進制、無符號的形式輸出 short、int、long 類型的整數
%ho、%o、%lo 以八進制、不帶前綴、無符號的形式輸出 short、int、long 類型的整數
%#ho、%#o、%#lo 以八進制、帶前綴、無符號的形式輸出 short、int、long 類型的整數
%hx、%x、%lx、%hX、%X、%lX 以十六進制、不帶前綴、無符號的形式輸出 short、int、long 類型的整數。如果 x 小寫,那么輸出的十六進制數字也小寫;如果 X 大寫,那么輸出的十六進制數字也大寫。
%#hx、%#x、%#lx、%#hX、%#X、%#lX 以十六進制、帶前綴、無符號的形式輸出 short、int、long 類型的整數。如果 x 小寫,那么輸出的十六進制數字和前綴都小寫;如果 X 大寫,那么輸出的十六進制數字和前綴都大寫。
%f、%lf 以十進制的形式輸出 float、double 類型的小數
%e、%le、%E、%lE 以指數的形式輸出 float、double 類型的小數。如果 e 小寫,那么輸出結果中的 e 也小寫;如果 E 大寫,那么輸出結果中的 E 也大寫。
%g、%lg、%G、%lG 以十進制和指數中較短的形式輸出 float、double 類型的小數,並且小數部分的最后不會添加多余的 0。如果 g 小寫,那么當以指數形式輸出時 e 也小寫;如果 G 大寫,那么當以指數形式輸出時 E 也大寫。
%s 輸出一個字符串
%p 表示以十六進制的形式(帶小寫的前綴)輸出數據的地址。如果寫作%P,那么十六進制的前綴也將變成大寫形式。
flag標志字符 含 義
- -表示左對齊。如果沒有,就按照默認的對齊方式,默認一般為右對齊。
+ 用於整數或者小數,表示輸出符號(正負號)。如果沒有,那么只有負數才會輸出符號。
空格 用於整數或者小數,輸出值為正時冠以空格,為負時冠以負號。
# 對於八進制(%o)和十六進制(%x / %X)整數,# 表示在輸出時添加前綴;八進制的前綴是 0,十六進制的前綴是 0x / 0X。對於小數(%f / %e / %g),# 表示強迫輸出小數點。如果沒有小數部分,默認是不輸出小數點的,加上 # 以后,即使沒有小數部分也會帶上小數點。

運算符

http://c.biancheng.net/view/161.html

2,流程控制

選擇

  • if else
    if(判斷條件1){
        語句塊1
    } else  if(判斷條件2){
        語句塊2
    }else  if(判斷條件3){
        語句塊3
    }else  if(判斷條件m){
        語句塊m
    }else{
         語句塊n
    }
    
    所謂語句塊(Statement Block),就是由{}包圍的一個或多個語句的集合。如果語句塊中只有一個語句,也可以省略{}。
    if 語句嵌套時,要注意 if 和 else 的配對問題。C語言規定,else 總是與它前面最近的 if 配對。
  • switch case
    switch(表達式){
      case 整型數值1: 語句 1; break;
      case 整型數值2: 語句 2; break;
      ......
      case 整型數值n: 語句 n; break;
      default: 語句 n+1; break;
    }
    
    case 后面必須是一個整數,或者是結果為整數的表達式,但不能包含任何變量。
    default 不是必須的。當沒有 default 時,如果所有 case 都匹配失敗,那么就什么都不執行。

循環

異常處理

3,函數

無參函數

dataType  functionName(){
    //body
}

有參函數

dataType  functionName( dataType1 param1, dataType2 param2 ... ){
    //body
}

函數調用

functionName(param1, param2, param3 ...);

函數聲明

  • C語言代碼由上到下依次執行,原則上函數定義要出現在函數調用之前,否則就會報錯。但在實際開發中,經常會在函數定義之前使用它們,這個時候就需要提前聲明。所謂聲明(Declaration),就是告訴編譯器我要使用這個函數,你現在沒有找到它的定義不要緊,請不要報錯,稍后我會把定義補上。
  • 函數聲明給出了函數名、返回值類型、參數列表(重點是參數類型)等與該函數有關的信息,稱為函數原型(Function Prototype)。函數原型的作用是告訴編譯器與該函數有關的信息,讓編譯器知道函數的存在,以及存在的形式,即使函數暫時沒有定義,編譯器也知道如何使用它。
  • 有了函數聲明,函數定義就可以出現在任何地方了,甚至是其他文件、靜態鏈接庫、動態鏈接庫等。
  • 對於單個源文件的程序,通常是將函數定義放到 main() 的后面,將函數聲明放到 main() 的前面,這樣就使得代碼結構清晰明了,主次分明。
  • 對於多個文件的程序,通常是將函數定義放到源文件(.c文件)中,將函數的聲明放到頭文件(.h文件)中,使用函數時引入對應的頭文件就可以,編譯器會在鏈接階段找到函數體。
  • 頭文件中包含的都是函數聲明,而不是函數定義,函數定義都放在了其它的源文件中,這些源文件已經提前編譯好了,並以動態鏈接庫或者靜態鏈接庫的形式存在,只有頭文件沒有系統庫的話,在鏈接階段就會報錯,程序根本不能運行。
dataType  functionName( dataType1 param1, dataType2 param2 ... );
dataType  functionName( dataType1, dataType2 ... );

全局變量與局部變量

  • 定義在函數內部的變量稱為局部變量(Local Variable),它的作用域僅限於函數內部, 離開該函數后就是無效的。在 main 函數中定義的變量也是局部變量,只能在 main 函數中使用;同時,main 函數中也不能使用其它函數中定義的變量。main 函數也是一個函數,與其它函數地位平等。
  • 在所有函數外部定義的變量稱為全局變量(Global Variable),它的作用域默認是整個程序,也就是所有的源文件,包括 .c 和 .h 文件。如果給全局變量加上 static 關鍵字,它的作用域就變成了當前文件,在其它文件中就無效了。在一個函數內部修改全局變量的值會影響其它函數,全局變量的值在函數內部被修改后並不會自動恢復,它會一直保留該值,直到下次被修改。
  • C語言規定,在同一個作用域中不能出現兩個名字相同的變量,否則會產生命名沖突;但是在不同的作用域中,允許出現名字相同的變量,它們的作用范圍不同,彼此之間不會產生沖突。
    • 不同函數內部可以出現同名的變量,不同函數是不同的局部作用域;函數內部和外部可以出現同名的變量,函數內部是局部作用域,函數外部是全局作用域。
    • 函數內部的局部變量和函數外部的全局變量同名時,在當前函數這個局部作用域中,全局變量會被“屏蔽”,不再起作用。也就是說,在函數內部使用的是局部變量,而不是全局變量。

代碼塊

  • 所謂代碼塊,就是由{ }包圍起來的代碼。C語言允許在代碼塊內部定義變量,這樣的變量具有塊級作用域;換句話說,在代碼塊內部定義的變量只能在代碼塊內部使用,出了代碼塊就無效了。C語言還允許出現單獨的代碼塊,它也是一個作用域。
  • 遵循 C99 標准的編譯器允許在 for 循環條件里面定義新變量,這樣的變量也是塊級變量,它的作用域僅限於 for 循環內部。
  • 每個C語言程序都包含了多個作用域,不同的作用域中可以出現同名的變量,C語言會按照從小到大的順序、一層一層地去父級作用域中查找變量,如果在最頂層的全局作用域中還未找到這個變量,那么就會報錯。

遞歸函數

  • 一個函數在它的函數體內調用它自身稱為遞歸調用,這種函數稱為遞歸函數。執行遞歸函數將反復調用其自身,每調用一次就進入新的一層,當最內層的函數執行完畢后,再一層一層地由里到外退出。
  • 分類:
    • 尾遞歸:遞歸調用位於函數體的結尾處
    • 中間遞歸:發生遞歸調用的位置在函數體的中間(DFS)
    • 多層遞歸:在一個函數里面多次調用自己

C標准庫

  • 標准C語言(ANSI C)共定義了15 個頭文件,稱為“C標准庫”,所有的編譯器都必須支持,如何正確並熟練的使用這些標准庫,可以反映出一個程序員的水平。
    • 初:<stdio.h>、<ctype.h>、<stdlib.h>、<string.h>
    • 中:<assert.h>、<limits.h>、<stddef.h>、<time.h>
    • 高:<float.h>、<math.h>、<error.h>、<locale.h>、<setjmp.h>、<signal.h>、<stdarg.h>
    • C library:提供了所有C語言標准函數的原型,並給出了詳細的介紹和使用示例,可以作為一部權威的參考手冊。

注意:

  • C語言不允許函數嵌套定義;也就是說,不能在一個函數中定義另外一個函數,必須在所有函數之外定義另外一個函數。
  • 函數不能嵌套定義,但可以嵌套調用,也就是在一個函數的定義或調用過程中允許出現對另外一個函數的調用。
  • 在所有函數之外進行加減乘除運算、使用 if...else 語句、調用一個函數等都是沒有意義的,這些代碼位於整個函數調用鏈條之外,永遠都不會被執行到。C語言也禁止出現這種情況,會報語法錯誤。(全局變量的定義可以)

4,數組及STL(標准模板庫)

數組:

  • 定義
    //一維數組
    dataType  arrayName[length];
    //二維數組
    dataType arrayName[length1][length2];
    
  • 初始化
    //一維數組
    int a[4] = {20, 345, 700, 22};
    int a[10]={12, 19, 22 , 993, 344};  //可以只給部分元素賦值。當{ }中值的個數少於元素個數時,只給前面部分元素賦值。
    int a[] = {1, 2, 3, 4, 5};  //如給全部元素賦值,那么在定義數組時可以不給出數組長度。
    //二維數組
    int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };  //分段賦值
    int a[5][3]={80, 75, 92, 61, 65, 71, 59, 63, 70, 85, 87, 90, 76, 77, 85};  //連續賦值
    int a[3][3] = {{1}, {2}, {3}};  //可以只對部分元素賦值,未賦值的元素自動取“零”值。
    int a[][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};  //如果對全部元素賦值,那么第一維的長度可以不給出。
    
  • 字符數組
    • 字符數組實際上是一系列字符的集合,也就是字符串(String)。在C語言中,沒有專門的字符串變量,沒有string類型,通常就用一個字符數組來存放一個字符串。
    • C語言規定,可以將字符串直接賦值給字符數組,但字符數組只有在定義時才能將整個字符串一次性地賦值給它,一旦定義完了,就只能一個字符一個字符地賦值了。
    • 在C語言中,字符串總是以'\0'作為結尾,所以'\0'也被稱為字符串結束標志,或者字符串結束符。由" "包圍的字符串會自動在末尾添加'\0'。('\0'是 ASCII 碼表中的第 0 個字符,英文稱為 NUL,中文稱為“空字符”。該字符既不能顯示,也沒有控制功能,輸出該字符不會有任何效果,它在C語言中唯一的作用就是作為字符串結束標志。)
    • 需要注意的是,逐個字符地給數組賦值並不會自動添加'\0';當用字符數組存儲字符串時,要特別注意'\0',要為'\0'留個位置;這意味着,字符數組的長度至少要比字符串的長度大 1。
      char str[7] = "abc123";
      
    • 逐個字符地為字符數組賦值時,要在在字符串的最后手動添加'\0';更加專業的做法是將數組的所有元素都初始化為“零”值,這樣才能夠從根本上避免問題。
      char str[30] = {0};  //將所有元素都初始化為 0,或者說 '\0';如果只初始化部分數組元素,那么剩余的數組元素也會自動初始化為“零”值.
      
    • 所謂字符串長度,就是字符串包含了多少個字符(不包括最后的結束符'\0')。例如"abc"的長度是 3,而不是 4。在C語言中,我們使用string.h頭文件中的 strlen() 函數來求字符串的長度,它的用法為:
      length strlen(strname);
      
  • 相關算法:
    • 排序:冒泡、快速、選擇、插入、歸並(合並)
    • 查找:順序、二分
  • 注意:
    • 數組是一個整體,它的內存是連續。
    • int、char、float 等類型的變量用於 scanf() 時都要在前面添加&,而數組或者字符串用於 scanf() 時不用添加&,它們本身就會轉換為地址。
    • 在C語言中,數組一旦被定義后,占用的內存空間就是固定的,容量就是不可改變的,既不能在任何位置插入元素,也不能在任何位置刪除元素,只能讀取和修改元素,這樣的數組稱為靜態數組。
    • C語言數組是靜態的,不能自動擴容,當下標小於零或大於等於數組長度時,就發生了越界(Out Of Bounds),訪問到數組以外的內存。如果下標小於零,就會發生下限越界(Off Normal Lower);如果下標大於等於數組長度,就會發生上限越界(Off Normal Upper)。C語言為了提高效率,保證操作的靈活性,並不會對越界行為進行檢查,即使越界了,也能夠正常編譯,只有在運行期間才可能會發生問題。當賦予數組的元素個數超過數組長度時,就會發生溢出(Overflow)。
    • 在 C89 中,必須使用常量表達式指明數組長度;而在 C99 中,可以使用變量指明數組長度。變量的值在編譯期間並不能確定,只有等到程序運行后,根據計算結果才能知道它的值到底是什么,所以數組長度中一旦包含了變量,那么數組長度在編譯期間就不能確定了,也就不能為數組分配內存了,只有等到程序運行后,得到了變量的值,確定了具體的長度,才能給數組分配內存,我們將這樣的數組稱為變長數組(VLA, Variable Length Array)。
      • 普通數組(固定長度的數組)是在編譯期間分配內存的,而變長數組是在運行期間分配內存的。
      • 變長數組是說數組的長度在定義之前可以改變,一旦定義了,就不能再改變了,所以變長數組的容量也是不能擴大或縮小的,它仍然是靜態數組。

STL(標准模板庫):

泛型
* 泛型程序設計(generic programming)是一種算法在實現時不指定具體要操作的數據的類型的程序設計方法。所謂“泛型”,指的是算法只要實現一遍,就能適用於多種數據類型,該方法可以大規模的減少程序代碼的編寫量,讓程序員可以集中精力用於業務邏輯的實現。其最成功的應用就是 C++ 的標准模板庫(STL)。
* STL,英文全稱 standard template library,中文可譯為標准模板庫或者泛型庫,其包含有大量的模板類和模板函數,是 C++ 提供的一個基礎模板的集合,用於完成諸如輸入/輸出、數學計算等功能。
* 如今 STL 已完全被內置到支持 C++ 的編譯器中,無需額外安裝。STL 就位於各個 C++ 的頭文件中,即它並非以二進制代碼的形式提供,而是以源代碼的形式提供。從根本上說,STL 是一些容器、算法和其他一些組件的集合,這里提到的容器,本質上就是封裝有數據結構的模板類,例如 list、vector、set、map 等。
* 一個例子:
  ```C++
  T maxt(T x, T y) {
      return (x > y) ? x : y;
  }
  ```
  * 代碼中的 T 是什么呢?很明顯,這是一個占位符,更確切的說是一個類型占位符。也就是說,將來在 T 這個位置上的是一個真實、具體的數據類型,至於到底是哪個類型,完全取決於用戶的需求。
  * 當然,如果硬要給 T 這種類型占位符也叫做一種數據類型,提供這種想法的發明者稱它為泛型(generic type),而使用這種類型占位符的編程方式就被稱為泛型編程。
  * 值得一提的是,既然泛型並不是真實的數據類型,那么使用泛型編寫的代碼也就不是真正的程序實體,只能算是一個程序實體的樣板。故此,通常形象的將這種使用了泛型的代碼稱為模板,由模板生成實際代碼的過程稱為模板的具體實現。
  * 注意,類型占位符的替換工作,不需要人為操控,可以完全交由計算機來完成,更准確的說,是交由編譯器在編譯階段來完成模板的具體實現。
  * 總之一句話,泛型也是一種數據類型,只不過它是一種用來代替所有類型的“通用類型”。在 C++ 中,用以支持泛型應用的就是標准模板庫 STL,它提供了 C++ 泛型設計常用的類模板和函數模板。
STL的組成
在 C++ 標准中,STL被組織為 13 個頭文件:<iterator><functional><vector><deque><list><queue><stack><set><map><algorithm><numeric><memory><utility>
STL的組成 含義
容器 一些封裝數據結構的模板類,例如 vector 向量容器、list 列表容器等。簡單的理解容器,它就是一些模板類的集合,但和普通模板類不同的是,容器中封裝的是組織數據的方法(也就是數據結構)。STL 提供有 3 類標准容器,分別是序列容器、排序容器和哈希容器,其中后兩類容器有時也統稱為關聯容器。
算法 STL 提供了非常多(大約 100 個)的數據結構算法,它們都被設計成一個個的模板函數,這些算法在 std 命名空間中定義,其中大部分算法都包含在頭文件algorithm中,少部分位於頭文件numeric中。
迭代器 在 C++ STL 中,對容器中數據的讀和寫,是通過迭代器完成的,扮演着容器和算法之間的膠合劑。它除了具有對容器進行遍歷讀寫數據的能力之外,還能對外隱藏容器的內部差異,從而以統一的界面向算法傳送數據。
函數對象 如果一個類將 () 運算符重載為成員函數,這個類就稱為函數對象類,這個類的對象就是函數對象(又稱仿函數)。
適配器 可以使一個類的接口(模板的參數)適配成用戶指定的形式,從而讓原本不能在一起工作的兩個類工作在一起。值得一提的是,容器、迭代器和函數都有適配器。
內存分配器 為容器類模板提供自定義的內存申請和釋放功能,由於往往只有高級用戶才有改變內存分配策略的需求,因此內存分配器對於一般用戶來說,並不常用。
容器種類 功能
序列容器 主要包括 vector 向量容器、list 列表容器以及 deque 雙端隊列容器。之所以被稱為序列容器,是因為元素在容器中的位置同元素的值無關,即容器不是排序的。將元素插入容器時,指定在什么位置,元素就會位於什么位置。
排序容器 包括 set 集合容器、multiset多重集合容器、map映射容器以及 multimap 多重映射容器。排序容器中的元素默認是由小到大排序好的,即便是插入元素,元素也會插入到適當位置。所以關聯容器在查找時具有非常好的性能。
哈希容器 C++ 11 新加入 4 種關聯式容器,分別是 unordered_set 哈希集合、unordered_multiset 哈希多重集合、unordered_map 哈希映射以及 unordered_multimap 哈希多重映射。和排序容器不同,哈希容器中的元素是未排序的,元素的位置由哈希函數確定。
容器 對應的迭代器類型
array 隨機訪問迭代器
vector 隨機訪問迭代器
deque 隨機訪問迭代器
list 雙向迭代器
set / multiset 雙向迭代器
map / multimap 雙向迭代器
forward_list 前向迭代器
unordered_map / unordered_multimap 前向迭代器
unordered_set / unordered_multiset 前向迭代器
stack 不支持迭代器
queue 不支持迭代器
迭代器分類
  1. 前向迭代器(forward iterator)
    假設 p 是一個前向迭代器,則 p 支持 ++p,p++,*p 操作,還可以被復制或賦值,可以用 == 和 != 運算符進行比較。此外,兩個正向迭代器可以互相賦值。
  2. 雙向迭代器(bidirectional iterator)
    雙向迭代器具有正向迭代器的全部功能,除此之外,假設 p 是一個雙向迭代器,則還可以進行 --p 或者 p-- 操作(即一次向后移動一個位置)。
  3. 隨機訪問迭代器(random access iterator)
    隨機訪問迭代器具有雙向迭代器的全部功能。除此之外,假設 p 是一個隨機訪問迭代器,i 是一個整型變量或常量,則 p 還支持以下操作:
    p+=i:使得 p 往后移動 i 個元素。
    p-=i:使得 p 往前移動 i 個元素。
    p+i:返回 p 后面第 i 個元素的迭代器。
    p-i:返回 p 前面第 i 個元素的迭代器。
    p[i]:返回 p 后面第 i 個元素的引用。
迭代器定義方式 具體格式
正向迭代器 容器類名::iterator 迭代器名;
常量正向迭代器 容器類名::const_iterator 迭代器名;
反向迭代器 容器類名::reverse_iterator 迭代器名;
常量反向迭代器 容器類名::const_reverse_iterator 迭代器名;
  • 通過定義以上幾種迭代器,就可以讀取它指向的元素,*迭代器名就表示迭代器指向的元素。其中,常量迭代器和非常量迭代器的分別在於,通過非常量迭代器還能修改其指向的元素。另外,反向迭代器和正向迭代器的區別在於:
  • 對正向迭代器進行 ++ 操作時,迭代器會指向容器中的后一個元素;
  • 而對反向迭代器進行 ++ 操作時,迭代器會指向容器中的前一個元素。

5,多文件編程

  • 不管是標准頭文件,還是自定義頭文件,都只能包含變量和函數的聲明,不能包含定義,否則在多次引入時會引起重復定義錯誤。

6,文件操作

7,面向對象

8,網絡編程

9,多進程/多線程

10,指針/pointer

  • 所謂指針,也就是內存的地址/address(內存中字節的編號);所謂指針變量,也就是保存了內存地址的變量。

  • CPU 訪問內存時需要的是地址,而不是變量名和函數名。變量名和函數名只是地址的一種助記符,當源文件被編譯和鏈接成可執行程序后,它們都會被替換成地址。編譯和鏈接過程的一項重要任務就是找到這些名稱所對應的地址。

  • &獲取變量的地址,*由地址獲取變量值,datatype *name 定義指針變量(datatype 和 datatype *是完全不同的數據類型)

  • 指針變量加減運算的結果跟數據類型的長度有關,而不是簡單地加 1 或減 1。(如對應int、double、char 數據類型,每次加 1,它們的地址分別增加 4、8、1。)(給進程分配的內存空間是有限的,如果for循環一直增加指針的值並且取對應的值,超過上限會導致讀取訪問權限沖突)

  • 在C語言中,函數的參數不僅可以是整數、小數、字符等具體的數據,還可以是指向它們的指針。用指針變量作函數參數可以將函數外部的地址傳遞到函數內部,使得在函數內部可以操作函數外部的數據,並且這些數據不會隨着函數的結束而被銷毀。

  • C語言允許函數的返回值是一個指針(地址),我們將這樣的函數稱為指針函數。

  • 指針可以指向一份普通類型的數據,例如 int、double、char 等,也可以指向一份指針類型的數據,例如 int *、double *、char * 等。如果一個指針指向的是另外一個指針,我們就稱它為二級指針,或者指向指針的指針。

    int a =100;
    int *p1 = &a;
    int **p2 = &p1;
    
  • 一個指針變量可以指向計算機中的任何一塊內存,不管該內存有沒有被分配,也不管該內存有沒有使用權限,只要把地址給它,它就可以指向,C語言沒有一種機制來保證指向的內存的正確性。建議對沒有初始化的指針賦值為 NULL。void *表示一個有效指針,它確實指向實實在在的數據,只是數據的類型尚未確定,在后續使用過程中一般要進行強制類型轉換。(指向NULL的指針會尋址到物理地址為零的內存單元,訪問該內存單元會引起系統調用,從而直接引發異常;該內存單元不可用)。

  • 一個函數總是占用一段連續的內存區域,函數名在表達式中有時也會被轉換為該函數所在內存區域的首地址,這和數組名非常類似。我們可以把函數的這個首地址(或稱入口地址)賦予一個指針變量,使指針變量指向函數所在的內存區域,然后通過指針變量就可以找到並調用該函數。這種指針就是函數指針。

  • 一些神奇的例子

    #include<stdio.h>
    
    int main() {
    	int a[3] = { 1,2,3 };
    	int *p = a;
    	printf("%d %d\n", a[1], 1[a]);
    	printf("%d %d", *(p + 1), *(a + 1));  //記住這種格式
    }
    

11,結構體

  • C語言結構體(Struct)從本質上講是一種自定義的數據類型,只不過這種數據類型比較復雜,是由 int、char、float 等基本類型組成的。可以認為結構體是一種聚合類型。
  • 結構體是一種數據類型,可以用它來定義變量;如果只需要定有有限個變量,后面不需要再使用結構體名定義其他變量,那么在定義時也可以不給出結構體名;結構體使用點號.獲取單個成員,除了可以對成員進行逐一賦值,也可以在定義時整體賦值。
  • 結構體也有對應的變量、數組、指針
  • ->是一個新的運算符,習慣稱它為“箭頭”,有了它,可以通過結構體指針直接取得結構體成員;這也是->在C語言中的唯一用途。

12,預處理

  • 在編譯之前對源文件進行簡單加工的過程,就稱為預處理(即預先處理、提前處理)。預處理主要是處理以#開頭的命令,例如#include <stdio.h>等。預處理命令要放在所有函數之外,而且一般都放在源文件的前面。預處理是C語言的一個重要功能,由預處理程序完成。當對一個源文件進行編譯時,系統將自動調用預處理程序對源程序中的預處理部分作處理,處理完畢自動進入對源程序的編譯。
  • 預處理階段的工作就是把代碼當成普通文本,根據設定的條件進行一些簡單的文本替換,將替換以后的結果再交給編譯器處理。宏定義可以帶有參數,宏調用時是以實參代換形參,而不是“值傳送”。
  • #include(文件包含命令) #define(宏定義命令) #undef(取消已定義的宏,終止宏定義命令的作用域) #define(帶參宏定義,對於帶參宏定義不僅要在參數兩側加括號,還應該在整個字符串外加括號。) #if、#elif、#else 和 #endif (條件編譯) #ifdef、#ifndef #error(在編譯期間產生錯誤信息,並阻止程序的編譯)
  • 預定義宏:
    • __LINE__:表示當前源代碼的行號(而且是去除空行和預處理命令的行數,代碼審計天然可行,龜龜);
    • __FILE__:表示當前源文件的名稱;
    • __DATE__:表示當前的編譯日期;
    • __TIME__:表示當前的編譯時間;
    • __STDC__:當要求程序嚴格遵循ANSI C標准時該標識被賦值為1;
    • __cplusplus:當編寫C++程序時該標識符被定義。
    • _WIN32
    • __linux__

13,C語言內存


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM