C語言的各類操做都是基於內存的。變量、數組都是內存的別名。 拿數組舉例,程序所須要的內存在編譯期間就已經被決定,因此定義數組時須要定義長度,以便於編譯器在編譯期給程序分配足夠的內存。但並非每次都能肯定數組的長度到底要定爲多少,或者前期定好了,後期有須要一些額外的空間時,定長數組就會帶來問題。因此就須要用到動態內存分配的支持。編程
想要使用動態內存分配須要<stdlib.h>標準庫的支持數組
一、malloc()函數
該函數會向操做系統請求內存塊,並返回內存塊的首地址。能夠用一個指針變量保存這個地址。函數
代碼及結果操作系統
int main() { int i; int *testArray1 = (int *)malloc(5 * sizeof(int)); //用malloc()函數分配內存 if (testArray1 == NULL) { exit(-1); //內存分配失敗,退出程序 } for(i=0; i<5; i++) { printf("Array1[%d] = %d\n", i, testArray1[i]); } free(testArray1);//釋放內存 return 0; }
二、calloc()函數
該函數與malloc函數大致相同,calloc函數接受兩個參數,單元數量和單元大小,而且內存塊中的值爲0;
指針
代碼及結果code
int main() { int i; int *testArray2 = (int *)calloc(5, sizeof(int)); //用calloc()函數分配內存 if (testArray2 == NULL) { exit(-1); //內存分配失敗,退出程序 } for(i=0; i<5; i++) { printf("Array2[%d] = %d\n", i, testArray2[i]); } free(testArray2);//釋放內存 return 0; }
三、realloc()函數
用於修改一個原先已經分配的內存塊大小blog
代碼及結果內存
int main() { int i; int *testArray1 = (int *)malloc(5 * sizeof(int)); //用malloc()函數分配內存 if (testArray1 == NULL) { exit(-1); //內存分配失敗,退出程序 } int *arrayNew = (int *)realloc(testArray1, 5 * sizeof(int));//用realloc()函數給arrayNew增長內存空間 if (arrayNew == NULL) { free(testArray1); //先釋放內存 exit(-1); //內存分配失敗,退出程序 } else { testArray1 = arrayNew; //將增添了內存的數組返回給testArray1 } for(i=0; i<10; i++) { printf("Array1[%d] = %d\n", i, testArray1[i]); } free(testArray1);//釋放內存 return 0; }
四、free()函數
用於釋放已分配的內存,將動態內存歸還系統字符串
一、malloc實際分配的內存可能會比請求的稍多一些,可是不能依賴此行爲來分配內存。必須按需求分配須要的內存。編譯器
二、當請求分配內存時,若是內存沒法知足須要時,會返回NULL。因此在動態分配內存時須要加以判斷。
三、使用動態分配內存函數分配的內存在程序結束以前必須釋放(調用free()函數)。
四、用realloc函數增添數組內存地址時,要將從新分配的內存地址返回新指針,而後再將該地址賦給須要增添內存的數組的地址
由於calloc雖然對內存進行了初始化(所有初始化爲0),會要下降效率
calloc至關於
p = malloc();
memset(p, 0,size);
多了對內存的寫零操做,而寫零這個操做咱們有時候須要,而大部分時間不須要。
內存泄漏(Memory Leak)是指程序中己動態分配的堆內存因爲某種緣由程序未釋放或沒法釋放,形成系統內存的浪費,致使程序運行速度減慢甚至系統崩潰等嚴重後果。
一、動態分配的內存在使用後沒有即時釋放(沒有被free)
二、爲指針變量分配了一個內存,而後又讓指針變量指向其餘的值,致使泄漏
三、用realloc函數給已有數組直接增添內存地址,但內存不足時,realloc函數返回值爲NULL,致使該數組指向丟失,內存泄漏
在實際的編程過程當中,咱們每每還須要一組類型不一樣的數據,例如對於學生信息登記表,姓名爲字符串,年齡爲整數,性別爲字符。由於數據類型不一樣,顯然不能用一個數組來存放。這時就須要要用到結構體了。
struct 結構體名{ 結構體所包含的變量或數組 };
結構體是一種集合,他能夠位於塊的內部,也能夠位於塊的外部,區別是他們的做用區間不一樣。它裏面包含了多個變量或數組,它們的類型能夠相同,也能夠不一樣,每一個這樣的變量或數組都稱爲結構體的成員。請看下面的一個例子:
struct Student { char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }; //不要忘記分號
此處便定義了一個能夠表示學生信息的結構體,該結構體中能夠保存學生的姓名、年齡還有性別信息。
第一種:在建立結構體時就定義該結構體的變量
struct Student { char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }student1, student2; //此處便有了Student類型的兩個變量,student1和student2
此種方法等同於:
struct Student { char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }; struct Student student1; struct Student student2;
第二種:使用typedef關鍵字
typedef struct Student { char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }STU; //此處STU就至關於struct Student,STU是其別名,二者等價
爲何要使用typedef:
在定義結構體變量時,能夠省去struct關鍵字,直接使用別名即可以定義變量。
struct Student student1; STU student2; //student1和student2的類型是同樣的
第三種:直接定義結構體變量
struct { char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }student1, student2; //該結構體只有student1和student兩個變量
採用此方法,該結構體只有student1和student2兩個變量,沒法在其餘地方再定義該結構體的新變量。
int main() { STU student; //定義一個學生變量 student.name = "小明"; //給該學生的姓名賦值 student.age = 10; //給該學生的年齡賦值 student.sex = 'm'; //給該學生的性別賦值 //打印學生信息,在變量名後面加上 .號 即可取出結構體變量中的屬性 printf("該學生的的姓名是:%s 年齡是:%d 性別是:%c", student.name, student.age, student.sex); }
打印結果
顧名思義,結構體數組就是相同類型的結構體變量組成的集合,和數組相似。
結構體數組的定義方式能夠看做是結構體定義和數組定義的結合,在此簡單列舉一些。
第一種:先定義結構體,再定義結構體數組
struct Student{ char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }STU; int main() { struct Stu students[5]; //定義了一個學生類型的數組 }
第二種:定義結構體的同時定義結構體數組
struct Student{ char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }students[5];//和第一種定義結果相同
此方法下,還能夠在定義結構體數組時對其進行初始化
struct Student{ char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }students[] = { //定義的同時初始化數組 {"小明", 10, 'm'}, {"小紅", 11, 'f'} }; int main() { for(int i=0; i<2; i++) { printf("該學生的的姓名是:%s 年齡是:%d 性別是:%c\n", students[i].name, students[i].age, students[i].sex); } }
打印結果
借用上面的打印能夠看出,結構體數組的使用方法是:結構體數組名[下標].屬性
當一個指針變量指向結構體時,咱們就稱它爲結構體指針。
第一種:定義結構體之後再定義結構體指針
結構體也是一種數據類型,只是它是由你本身定義的,因此結構體指針的定義類型和通常數據類型的指針定義方式相同。
struct Student{ char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }; int main() { struct Student student = {"小明", 10, 'm'}; //定義一個結構體變量 struct Student *pStudent = &student; //定義一個結構體指針並傳入student的地址 }
第二種:使用typedef關鍵字定義(經常使用)
typedef struct Student{ char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }STU, *PSTU; // PSTU至關於struct student * 或者 STU * int main() { STU student = {"小明", 10, 'm'}; //定義一個結構體變量 PSTU pStudent = &student; //PStu即爲Stu的指針類型 }
第一種寫法:
typedef struct Student{ char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }STU, *PSTU; // PSTU至關於struct student * 或者STU * int main() { STU student; //此處不初始化 PSTU pStudent = &student; //PSTU即爲STU的指針類型 //用指針給結構體中的屬性賦值 (*pStudent).name = "小明"; (*pStudent).age = 10; (*pStudent).sex = 'm'; //注意加括號 }
注意事項:由於 . 號的優先級高於 * 號,因此(*pStudent)的括號不能少!
第二種寫法:使用 -> 運算符(經常使用)
typedef struct Student{ char name[10]; //學生姓名 int age; //學生年齡 char sex; //學生性別 }STU, *PSTU; // PSTU至關於struct student * 或者 STU * int main() { STU student; //此處不初始化 PSTU pStudent = &student; //PStu即爲Stu的指針類型 //用指針給結構體中的屬性賦值 pStudent->name = "小明"; pStudent->age = 10; pStudent->sex = 'm'; //使用 -> 運算符 }
這也是 -> 運算符在C語言中惟一的用途