day 9c:指针的高级应用

动态内存分配

为什么要在堆上分配空间?

1.栈空间受限的比较小,栈帧的大小是在编译期间确定的

2.在c中栈无法存放动态大小的数据

3.栈空间最好不要存放多线程共享数据:栈空间独立性即每个线程都有自己的栈地址,栈空间是私有的

如何申请堆空间

malloc不对内存块进行清零,而calloc对内存块进行清零

 

c中可以直接写int* p=malloc(sizeof(int)*100) ,

但c++中要自己将void*转化成int*---->int* p=(int*)malloc(sizeof(int)*100)

动态数组 

为什么上图int arr1[n]会报错?

因为上面的内容是存在栈中的,应该在编译期间就确定大小,而int arr1[n]中的n是变的,在运行时确定大小,所以会报错

模型

realloc(和上两个一样,失败返回空指针)

创建销毁动态数组 及添加元素

 上图结构体和数组都是用malloc在堆中实现

//创建动态数组

Vector* vector_create(void){

        Vector* v=malloc(sizeof(Vector));

        if(v==MULL){

                exit(1);

        }

        v->elements=malloc(sizeof(E)*8);   //E* p=malloc(sizeof(E)*8);

        if(v->elements==NULL){                  //p==NULL

                free(v);

                exit(1);

        }                                                       //v->elements=p;

        v->capacity=8;

        v->size=0;

          

//析构函数

void vector_destroy(Vector* v){

        //释放是按声明的相反顺序释放,先申请v,在申请p(v->elements),所以先释放p

        free(v->elements);

        free(v);  

}

void grow_capacity(Vector* v){

        int new_capacity=v->capacity<1024 ? v->capacity<<1 : v->capacity+1024;

        E* p=realloc(v->elements,new->capacity*sizeof(E));

        if(p!=NULL){

               exit(1);

        }

        v->elements=p; 

        v->capacity=new_capacity;

}

//往数组添加元素

void push_back(Vector* v,E val){

        //判断是否需要扩容

        if(v->size==v->capacity){

                grow_capacity(v);

        }

        v->elements[v->size]==val;

        v->size++;

}

//单元测试

int main(void){

        Vector* v=vector_create();

        for(int i=0;i<200;i++){

                push_back(v,i);

        }

        vector_destory(v);

        return 0;       

}

 free(释放内存空间)

垃圾回收器       

                  减轻程序员负担

                 不确定因素--->stop the world--->不适合实时系统(必须在某个确定时间内完成某个业务) 

没有垃圾回收器

                  c:      free

                c++:    析构函数,RALL,智能指针

                Rust:   所有权机制(生命周期)

int main(void){

        int* p=malloc(sizeof(int)*100);    //malloc不会初始化,失败返回空指针

        free(p);    //不会改变p的值(存的地址),只是将p指向的内存空间释放,这时p是悬空指针

}

调用 free() 释放内存后,原内存区域的数据不会被主动清除或重置,而是可能继续保留原有内容,直到被后续分配操作覆盖。这意味着从程序逻辑层面,访问已释放的内存属于“悬空指针”行为,可能导致不可预知的错误。

问题

1.double free        

int main(void){

        int* p=malloc(sizeof(int)*100); 

        free(p);  

        free(p);  //double free

}

2.use after free

int main(void){

        int* p=malloc(sizeof(int)*100);   

        free(p);   

        p[0]=10;   //use after free

结论:当堆中空间不再使用时,应当有且仅有一次free(),这很难做到,如上图因为栈是每个线程都有,故栈中指针指向同一个堆中空间,巨容易发生冲突

 动态分配的结构体(链表)

typedef struct node{

        int data;

        struct node* next;

} Node;

Node* addNode(Node* head,int data){

        Node* s=malloc(sizeof(Node));

        if(s==NULL){

                exit(1);

        }   //注意申请堆空间记得判一下空   即不成功

        s->next=head;

        s->data=data;

        head=s;

        return head;

}

//不带头结点的头插法

int main(void){

        Node* head = NULL;

        head=addNode(head,1);

        head=addNode(head,2);

        head=addNode(head,3);

        head=addNode(head,4);  //4-->3-->2-->1

        return 0;

}

//上述代码是错误的,因为head在main函数中,要想修改head的值要传递head地址而不是head值

typedef struct node{

        int data;

        struct node* next;

} Node;

Node* addNode(Node** phead,int data){

        Node* s=malloc(sizeof(Node));

        if(s==NULL){

                exit(1);

        }   //注意申请堆空间记得判一下空   即不成功

        s->next=*head;

        s->data=data;

        *phead=s;

        return *phead;

}

//不带头结点的头插法

int main(void){

        Node* head = NULL;

        head=addNode(&head,1);

        head=addNode(&head,2);

        head=addNode(&head,3);

        head=addNode(&head,4);  //4-->3-->2-->1

        return 0;

}

  二级指针(指向指针的指针)

传一级指针还是传二级指针?

你想修改哪个变量就传哪个变量的地址,如上例修改head的值就传head的地址(二级指针 )

函数指针

 

 语法(how)

        变量声明:int (*p) (int , int);

        值        :foo , &foo

        通过函数指针调用函数:p(a,b); (*p)(a,b);

int add(int a,int b){

        return a+b;

}

int mul(int a,int b){

        return a*b;

}

int main(void){

        int (*p1) (int,int)=add; //声明指针变量并赋值,函数和数组相似,用函数名代替函数地址

        int (*p2) (int , int)=&mul;

        int m=p1(3,4);                                //调用也是,可以直接不解引用直接用指针变量名

        int n=(*p2)(3,4);

        return 0;

}

 注意:int* p2(int,int)不是函数指针而是指针函数(返回值为int*的函数)

作用(why)

        1.函数式编程(传递函数,返回函数),函数指针支持函数式编程

            分解任务,解耦合

        2.编写非常通用的函数(功能非常强大的函数)

qsort (非常通用的排序函数)

数组名作为参数传递会退化成指向它第一个元素的指针 !!!!!!!

qsort底层原理是快速排序,比较规则是通过用户传递的函数指针cmp进行!!!!

qsort案例

typedef struct{

        int id;

        char name[20];

        char gender;

        int chinese;

        int math;

        int english

} Student

int cmp (const void* p1,const void* p2){

        Student* s1=p1;

        Student* s2=p2;

        int total1=s1->chinese+s1->math+s1->english;

        int total2=s2->chinese+s2->math+s2->english;

        if(total1 != total2){

                return total2 - total1;

        }

        if(s1->chinese != s2->chinese){

                return total2 - total1;

        }

        if(s1->math != s2->math){

                return total2 - total1;

        }

        return s1->id - s2->id;

}

void print_stu(Student* s){

        printf("%d %s %c %d %d %d",s->id......);

}

int main(void){

        Student students[5];

        for(int i=0;i<5;i++){

                scanf("%d%s %c%d%d%d",

                        &students[i].id,

                        students[i].name,

                        &students[i].gender,

                        &students[i].chinese,

                        &students[i].math,

                        &students[i].english,);

        }

        qsort(students,5,sizeof(Student),cmp);  //默认按从小到大排序

        for(int i=0;i<5;i++){

                print_stu(&student[i]);  //print_stu(student+i);

        }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值