C語言使用指針時常犯的一些錯誤


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;
}

 

運行結果:

 


免責聲明!

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



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