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

 

运行结果:

 

posted @ 2020-07-22 14:18  DNoSay  阅读(1537)  评论(0编辑  收藏  举报