指针进阶
动态内存分配
1.概论
对于普通的数组,其一大缺点就一旦定义以后就无法改变其大小容量。这其实本质上是静态内存的缺陷。
静态内存:对于系统分配的内存就是静态内存也叫栈内存,比如定义的变量,函数等等都是由系统进行内存的分配。因而程序员自己无法灵活对其进行更改。
值得注意的是静态变量和静态内存不是想似的概念,静态变量是全局变量,在编译前阶段就已经存在了,所以不要混淆。
动态内存:动态内存是由吃程序员自己生成管理的内存,也叫堆内存。我们可以控制其产生和销毁。
malloc函数和free函数
malloc函数:
相当于C++语言的new关键字,直接创建一个指定内存大小的空间。
定义:void *malloc(unsigned long size)
此函数的类型是void*类型,即其返回值是一个可以指向void的指针变量,也就是一个可以指向任何变量的指针。
例如int *p=malloc(4)
向指针变量p直接分配了4个字节内容。
但是这种程序编写方式却不好,因为不同机器对于int大小并没有一个统一的共识,所以4个字节有可能在不同编译器的编译下出现歧义。下面的编程习惯更好。
int *p=malloc(siazeof(int))或者int *p=malloc(sizeof*p)
free函数:
因为堆内存完全依赖程序员自己的管理,所以既然有创建新内存,就得有释放老内存,否则越来越多内存消耗将会吃掉全部机器内存空间,导致内存泄露。
因而我们就需要free函数帮助我们释放掉不需要的内存。而释放的本质就是将这块内存重现生命为可用内存,但是内存上内容不一定清空,以及指针的指向也不会改变,为了防止后续指针干扰我们回收的内存的重新使用,所以伴随free的使用必须将指针的指向置为null。而内存的上内容的处置则因不同编译器而不同。
free(p);p=NULL;
2.动态数组
动态数组就是利用动态分配指针大小来表示数组长度的可变性。
2.1动态数组的建立
#include<stdio.h>
#include<stdlib.h>
int main()
{
int cnt,i;
int *p;
printf("请输入存放元素个数:");
scanf("%d",&cnt);
p=(int*)malloc(sizeof(*p)*cnt);
printf("请输入元素内容:");
for(i=0;i<cnt;i++){
scanf("%d",&p[i]);
}
printf("创立的一维数组是:");
for(i=0;i<cnt;i++){
printf("%d ",p[i]);
}
free(p);
p=NULL;
return 0;
}
2.2动态数组长度的扩充和缩小
对于动态扩增或者缩减动态数组大小,可以使用realloc函数。
void *realloc(void *p,unsinged long size);
但是需要特别注意的是,如果是缩减长度,尽量使得缩减后长度满足大于原有数据的长度,否则就会造成数据的丢失。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int cnt,i,cnt2;
int *p;
printf("请输入存放元素个数:");
scanf("%d",&cnt);
p=(int*)malloc(sizeof(*p)*cnt);
printf("请输入新的元素个数");
scanf("%d",&cnt2);
realloc(p,sizeof(int)*cnt2);
printf("请输入元素内容:");
for(i=0;i<cnt2;i++){
scanf("%d",&p[i]);
}
printf("创立的一维数组是:");
for(i=0;i<cnt2;i++){
printf("%d ",p[i]);
}
free(p);
p=NULL;
return 0;
}
3.多级指针
所谓多级指针就是指针的指针。也就是这个地图上指示的是另一个地图的存放地点。
int a;
int *p=&a;
int **q=&p;
意义:多级指针的意义所在就是可以帮助我们跨函数的使用动态内存。
3.1跨函数使用动态内存
本质:就是在主调函数中使用背调函数中分配的内存空间。
多级指针的使用
#include<stdlib.h>
void DynamicArray(int **q);
int main()
{
int i=2;
int *p=i;
DynamicArray(&p);
printf("*p=%d",*p);
return 0;
}
void DynamicArray(int **q){
*q=malloc(sizeof*q);
**q=5;
return;
}
如果上述代码传入的的不是&p,而是p的话,自然*q=malloc(sizeof*q);就会变成q=malloc(sizeof*q);
所以p指针的指向改变,后续再进行p内容改变也不会影响到i的值变化了。
直接返回被调函数的指针
#include<stdio.h>
#include<stdlib.h>
int* DynamicArray(void);
int main()
{
int *p=DynamicArray();
printf("*p=%d",*p);
return 0;
}
int* DynamicArray(void){
int *q=malloc(sizeof*q);
*q=5;
return q;
}
这种直接返回被调函数指针的方式也可以实现跨函数的动态内存管理。
3.2通过指针引用二维数组
所谓的二维数组本质就是一堆数组的组合,在内存依然是一个个数据连续存放的顺序表形式。
*(a+i)+j==&a[i][j]
二维数组的地址就是其首元素,但是要注意的是首元素不是a[0][0]而是a[0]。
a==&a[0];
a[0]==&a[0][0];
a==&(&a[0][0]);
上面三个式子等价
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int i,j;
int (*p)[4]=a;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("%-2d\x20",*(*(p+i)+j));
}
printf("\n");
}
return 0;
}
4.函数指针
函数指针不同于指针变量,它指向的是一个函数的地址,而不是变量。
int(*p)(int a,int b);
其中()不可以省略,否则意思就变成了返回值为int*指针的函数了。
举例:
int func(int x);
int (*p)(int x);
p=func;
这样p代表这个函数的地址,而*p则完全等价于整个函数