1、使用為初始化的指針或者使用NULL指針
#include<stdio.h> /** * C語言使用指針常犯的錯誤---指針未初始化 * 使用未初始化的指針或者指向NULL等系統內存地址的指針 * 錯誤原因: * 1、定義一個指針未初始化時,系統將為該指針隨機分配一個地址,此時若該地址指向系統內存,在該內存寫入數據將會導致程序甚至系統崩潰-->使用野指針 * 2、一般來所NULL(系統地址為0x0(表示十六進制的零)所指向的地址是不允許寫入數據的,強行寫入會導致1一樣的錯誤,也可以把NULL視為系統內存 * @return */ int main() { //案例1 // int * p; //案例2 int *p = NULL; *p = 10; printf("%d\n", *p); return 0; }
2、使用指針變量開辟空間后未釋放內存
#include<stdio.h> #include <cstdlib> /** * C語言使用指針常犯的錯誤---忘記釋放內存 * 1、使用指針開辟了內存后,不釋放內存-->內存泄漏 * 2、釋放內存后仍然使用指針(未將指針指向NULL)-->使用野指針 * 3、令指針指向NULL后繼續使用該指針-->訪問系統內存,系統或程序崩潰 * @return */ int main() { int *p = (int *) malloc(sizeof(int) * 100); for (int i = 0; i < 100; ++i) { p[i] = i; } for (int j = 0; j < 100; ++j) { printf("%d\n", p[j]); } //輸出 //1 //.. //99 free(p); printf("%d\n", p[0]); //輸出:釋放了內存后,p指向的存儲單元被收回,存儲數據未知 //10187872 p = NULL; printf("%d\n", p[0]); //報錯:使用空指針 //Process finished with exit code -1073741819 (0xC0000005) return 0; }
3、使用指針變量開辟內存后,在操作指針變量時改變了指針變量的指向,然后釋放內存
#include<stdio.h> #include <cstdlib> #include <cstring> /** * C語言使用指針常犯的錯誤---不斷修改指針變量的指向,然后進行釋放內存操作 * 使用指針開辟內存后,進行操作,操作完成過程中改變了指針的指向,然后釋放內存,會釋放不應該釋放的內存, * 而程序對部分本該釋放而沒有釋放的內存單元失去控制,造成內存泄漏 * 解決辦法:在開辟內存后,定義額外一個指針指向開辟內存的基址,該指針僅用於釋放操作,而開辟內存的指針變量僅用於釋放內存 * @return */ int main() { //開辟內存 char *p = (char *)malloc(sizeof(char)*100); //使用指針 strcpy(p,"abcdef"); printf("%s\n",p); //輸出:abcdef //修改指針指向 p +=1; //此時p指向存儲了b的存儲單元 printf("%s\n",p); //輸出:bcdef //釋放內存 //程序不正常結束:報錯 //SIGTRAP (Trace/breakpoint trap) free(p); return 0; }
4、使用野指針
#include<stdio.h> #include <cstdlib> /** * C語言使用指針常犯的錯誤--使用野指針 * 1、定義指針變量時未初始化,該指針不會自動指向NULL,其缺省值是隨機的,該指針即變為野指針 * 2、釋放內存后未將指針指向NULL,該指針變為野指針 * 野指針特點: * 用戶一般很少使用空指針,應為可以使用if判斷來防止使用了控制,但是野指針無法通過if判斷來判別,很容易使程序崩潰 * @return */ int main() { //定義指向double變量的指針,未初始化,p變為野指針 double *p; //開辟內存 char *s = (char *)malloc(sizeof(char)*100); //...一系列操作 //釋放內存 free(s); //未使s指向NULL,s變為野指針 //使用野指 if(p)printf("%lf\n", *p); //輸出 //0.000000 if(s)printf("%s\n",s); //輸出 //`t //指向NULL s = nullptr; if(s)printf("%s\n",s); //空指針,不執行語句 return 0; }
5、錯誤使用指針參數申請動態內存
/** * C語言使用指針常犯的錯誤---使用指針參數開辟內存 * 不要指望使用指針參數去申請動態內存, * @return */ int main() { char *p = nullptr; //p:NULL(0x0) getMemory(p, 200); //p:NULL(0x0) if (!p)printf("this is a flag\n"); //輸出: //this is a flag strcpy(p, "use dynomic memory"); //SIGSEGV (Segmentation fault) printf("%s\n", p); free(p); p = NULL; //程序異常結束 //Process finished with exit code -1073741819 (0xC0000005) return 0; } /** * 使用指針參數申請內存 * 結果:在getMemory函數內,編譯器為每一個參數制作一個副本,指針p的副本為_p, 編譯器自動執行效果如下的語句: * _p = p; * 這里_p是一個指向char類型的指針變量,其地址指向的內存單元可供用戶使用,該內存單元用來存儲一個指向char類型的地址 * 而_p = p;則是意味着使_p的地址指向的內存單元存儲指針p指向的char類型的地址 * 在函數內使用指針_p使用new或malloc()函數開辟內存后,變量_p的地址指向的內存單元存儲的地址不在是指針變量p地址指向的存儲單元存儲的地址 * 在main案例中指針變量p存儲的地址是NULL,_p在使用malloc()函數前存儲的地址是NULL,使用malloc()函數后存儲的地址由系統分配,不再是NULL * 在getMemory函數執行完畢后,_p變量銷毀,但是其開辟的內存未釋放,造成內存泄漏,而p變量仍然存儲着NULL,其未開辟內存。 * @param p * @param size * */ void getMemory(char *p, int size) { p = (char *) malloc(sizeof(char) * size); //p:0x9515a0 "\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\253\253\253\253\253\253\253\253\253\253\253\253\253\253\253\253\356\376\356\376\356\376", <incomplete sequence \356\376> }
附1:使用指針參數申請動態內存的常見做法
#include<stdio.h> #include <cstdlib> #include <cstring> char *getMemory1(char *p, int i); void getMemory2(char **p,int i); /** * 使用指針參數開辟內存的兩種方式 * 1、使用函數返回值傳遞動態內存 * 2、使用指向指針的指針作為參數來傳遞動態內存 */ int main() { char *p = nullptr; //使用函數返回值傳遞動態內存 p = getMemory1(p, 100); //p:0xad15a0 "\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\253\253\253\253\253\253\253\253\253\253\253\253\253\253\253\253\356\376\356\376\356\376\356\376\356\376", <incomplete sequence \356\376> strcpy(p, "use dynomic memory\n"); printf("%s", p); free(p); p = nullptr; getMemory2(&p,100); strcpy(p, "use dynomic memory again\n"); printf("%s", p); free(p); p = nullptr; return 0; } char *getMemory1(char *p, int size) { //開辟內存 p = (char *) malloc(sizeof(char) * size); //p:0xad15a0 "\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\r\360\255\272\253\253\253\253\253\253\253\253\253\253\253\253\253\253\253\253\356\376\356\376\356\376\356\376\356\376", <incomplete sequence \356\376> //使用函數值傳遞指向堆中的內存,不能返回指向指向棧的內存? return p; } /** * 使用指向指針的指針來傳遞動態內存 * 首先getMemory2()函數制作一個p的拷貝_p,p變量和_p變量的地址存儲的是main函數中指針變量p(以下稱為p',便於區分)的地址 * 函數內的p即_p,而不是p * *p = (char *)malloc(sizeof(char)*size); * 上述語句的作用是,為指針變量p的地址指向的內存存儲的地址即指針p'開辟一塊內存,將p'指向這塊內存單元的基址 * 在函數執行完成后,_p銷毀,但是p'的指向被改變了,故動態內存得以傳遞 * @param p (指向(指向(char變量)的指針)的指針) * @param size */ void getMemory2(char **p, int size) { *p = (char *) malloc(sizeof(char) * size); }
6、C語言在函數內定義結構體變量,將該變量地址進行賦值
// // Created by ljw07 on 2020/7/22. // #include<stdio.h> #include<stdlib.h> /** * 定義結構體 */ typedef struct Node{ int data; struct Node * next; }Node,*LinkList; void test(LinkList *L , int j, int y){ Node *p = *L; int i = 0; for (i = 0; i < j-1; ++i) { p = p->next; } Node s; s.data = y; s.next = NULL; printf("Node s' address is %d\n", &s); p->next = &s; } /** * C使用指針常犯的錯誤---函數內定義結構體變量,然后將該變量的地址賦值給指針變量 * 對於函數內定義的結構體變量,每次調用該函數所使用的內存塊是一致的,即使用的是同一個結構體變量 * 因此每次獲取該結構體地址得到的值是相同的,當我們想把該結構體變量的地址賦值給指向結構體的指針變量時, * 會導致多次操作的不同指針結構體的指針變量指向同一塊內存,常常導致函數的實際功能與預期功能不一致, * 如同上述的test(LinkList *L,int i,int y)函數實現的在單鏈表L的第i個位置上插入數據域為y的結點 * 最終會發現調用test()函數在L上第1,2個位置插入分別插入數據域為3,4的結點,最終只在第1位置插入數據域為4的結點, * 而該結點的指針域指向其本身,如同形成一個循環鏈表,經過調試發現,在test函數中p指針始終指向結構體變量s, * 而每次調用test()函數,結構體變量s的地址都是一致的,故從第二次調用test()函數開始,test函數每次僅改變結構體變量s的數據域, * 並讓其的指針域指向結構體變量s本身 * @return */ int main() { LinkList L = (LinkList *)malloc(sizeof(Node)); L->next = NULL; L->data = 0; test(&L,1,3); test(&L,2,4); test(&L,3,5); printf("%d\n", L->next); printf("%d\n", L->next->next); printf("%d\n", L->next->next->next); return 0; }
運行結果:
附2:解決在函數中定義局部結構體的方法---使用結構體指針申請動態內存來定義結構體變量,即可使每次調用函數時,得到新的內存空間
// // Created by ljw07 on 2020/7/22. // // // Created by ljw07 on 2020/7/22. // #include<stdio.h> #include<stdlib.h> /** * 定義結構體 */ typedef struct Node{ int data; struct Node * next; }Node,*LinkList; void test(LinkList *L , int j, int y){ Node *p = *L; int i = 0; for (i = 0; i < j-1; ++i) { p = p->next; } Node *s = (Node *)malloc(sizeof(Node)); s->data = y; s->next = NULL; printf("Node s' address is %d\n", s); p->next = s; }
運行結果: