C语言指针和动静态分配内存
1、静态数组的缺陷
在之前的分配数组中有两个缺陷:
1、在定义数组的时候必须指定数组的长度,只能是常量,而不是变量;
2、之前的定义数组中,程序员无法手动的进行释放数组的内存;只能够在本函数中运行完毕时,由操作系统来进行释放。
在该函数运行期间,该函数中数组所占用的空间会一直空间。
3、数组在当前函数运行期间,不能够动态扩充或者缩小,一旦定义,就不能够发生改变。
4、在函数中定义的数组中,可以被其他函数所使用,但是在定义数组的函数中一旦运行结束,那么数组将会被操作系统释放。
传统定义的数组的释放是由操作系统来进行释放,可以跨函数使用,但是数组所在的函数一旦运行结束,其他函数将不能够使用了,因为已经被释放了,没有权限继续去使用了
2、动态分配
动态分配数组可以解决上面的问题。
malloc函数是memory allocate内存分配的缩写。
要使用malloc函数,导入一个头文件:# include <malloc.h>
malloc函数只有一个形参,并且实参是一个整形的数据;
形参表示的是请求操作系统为本程序分配整数个字节;
malloc函数只能够返回第一个字节的地址,但是可以根据数据类型确定占用了几个字节
int i = 100;
int * p = (int *)malloc(4);
拿上面的来举例子,请求操作系统分配4个字节给本段程序,然后返回给p指针,但是因为之前说过了p指针保存的是内存单元的值,那么p中保存的就是64位的01代码,也就是8个字节,保存的就是分配的内存单元的地址,也就是第一个内存单元的地址,根据前面的数据类型可以找到分配的内存单元有几个。
所以这里使用了强制转换类型来进行操作的。
这里也可以这样子来书写:
int i = 100;
int * p = (int *)malloc(sizeof(int));
编写个程序:
# include <stdio.h>
# include <malloc.h>
int main(void){
int i = 100;
int * p = (int *)malloc(sizeof(int));
p = &i;
printf("p中保存的是值:%d\n",p);
printf("p中保存的是值:%d\n",&i);
return 0;
}
查看控制台输出:
p中保存的是值:6487572
p中保存的是值:6487572
--------------------------------
Process exited after 0.2071 seconds with return value 0
请按任意键继续. . .
再对上面描述的一段代码来进行深入理解:
int * p = (int *)malloc(sizeof(int));
这段代码有静态分配,也有动态分配。在定义int * p的时候,这段代码属于静态分配,而等号后面的属于动态分配。
静态分配因为是指针变量,所以占据了8个字节,后面的占据了四个字节。
p是静态的,但是指向的空间是动态分配的。p变量所占据的空间是无法被释放的,只有在函数运行结束之后由操作系统来进行释放,而动态分配的内存地址空间我们程序员是可以来手动释放的。
free(p);
这段代码的意思就是将p指向的内存空间释放掉,因为p指向的内存地址空间是动态分配的。但是p这个变量本身是无法由程序员释放的,而是由OS在当前函数运行结束之后进行释放的。
继续上面所说的,既然是已经向操作系统申请分配内存空间了,那么我们就可以操作这块内存空间了
# include <stdio.h>
# include <malloc.h>
int main(void){
int i = 100;
int * p = (int *)malloc(sizeof(int));
p = &i;
printf("p中保存的是值:%d\n",p);
printf("p中保存的是值:%d\n",&i);
*p = 200;
printf("p变量的值是:%d\n",*p);
printf("i变量的值是:%d\n",*p);
return 0;
}
查看控制台输出:
p中保存的是值:6487572
p中保存的是值:6487572
p变量的值是:200
i变量的值是:200
--------------------------------
Process exited after 0.1563 seconds with return value 0
请按任意键继续. . .
再次重复一句话:操作变量就是相当于操作变量的值。不管是对于指针还是变量,其实都是变量,都是操作变量的值。之前也说过,变量就是一个容器,操作容器就是操作容器中的值;
3、free
释放内存的操作
# include <stdio.h>
# include <malloc.h>
int main(void){
int i = 100;
int * p = (int *)malloc(sizeof(int));
p = &i;
printf("p中保存的是值:%d\n",p);
printf("p中保存的是值:%d\n",&i);
*p = 200;
printf("p变量的值是:%d\n",*p);
printf("i变量的值是:%d\n",*p);
// 释放掉内存
free(p);
*p = 20;
printf("-------------------\n");
printf("i变量的值是:%d\n",*p);
return 0;
}
看控制台输出:
p中保存的是值:6487572
p中保存的是值:6487572
p变量的值是:200
i变量的值是:200
--------------------------------
Process exited after 2.125 seconds with return value 3221226356
请按任意键继续. . .
可以发现,已经被释放掉了的内存现在是无法再次来进行使用了。已经释放了之后,当前程序的环境就无法使用了,被操作系统回收了。
4、动态数组
通过动态分配的方式来创建一个数组。
看了很多的博客和书籍,一直没有人能够把一个知识点解释清楚,我自己总结一下:
int i = 10;
int * p = (int *)malloc(sizeof(int));
这里需要画一个图来进行展示:
简单解释下:右边是内存条中的内存单元显示,表示的是int i 申请的内存空间,操作变量i就是操作变量i的值,那么变量i的值就是四个内存单元中的01代码,而对于指针来说,操作指针就是操作指针的值,也就是上图中的001H,这个值保存的是内存单元的地址值。
上面的(int *)malloc(sizeof(int)):这里首先表示的是分配四个字节,但是不和上面的相同,是额外的四个字节。分配完成之后,将这一块内存单元的地址赋值给p,将给p来进行保存,但是因为p变量保存的是内存单元地址的值,所以只能够保存内存地址单元地址而且只是一个而已。那么也就是说指针指向如下图所示:
指针永远指向的都只是一块内存单元的首个元素的第一个内存单元的地址。对于数组来说,在进行计算*(p+1)的时候,这里的1其实是一个偏移量问题。这个1是携带了数据类型的,比如说当前的是1,数据类型是int,那么在找到第二个的时候,很明显找到的就是+4个字节的地方。
上面图中显示的是一个单元格占据四个字节的,没有进行准确说明。
# include <malloc.h>
# include <stdio.h>
int main(void){
int length;
scanf("%d",&length);
// 动态的构造了一个长度是length的int类型的数组
int * p = (int *)malloc(sizeof(int)*length);
return 0;
}
上面的数组长度是通过控制台来进行输入的。
数组中的每个元素是int类型的,长度是length,p保存的是第一个元素的第一个内存单元地址。
那么紧接着来验证一下:
# include <malloc.h>
# include <stdio.h>
int main(void){
int length;
printf("请输入数组长度:\n");
scanf("%d",&length);
int * p = (int *)malloc( sizeof(int) * length);
int len;
// 开始为每个变量来进行赋值
for(len=0;len<length;len++){
scanf("%d",&p[len]);
}
for(len=0;len<length;len++){
printf("每个元素的内存单元值是:%d ",&p[len]);
printf("每个元素的值是:%d\n",p[len]);
}
// 将动态分配的内存释放掉
free(p);
return 0;
}
控制台输出:
请输入数组长度:
5
1 2 3 4
5
每个元素的内存单元值是:10317344 每个元素的值是:1
每个元素的内存单元值是:10317348 每个元素的值是:2
每个元素的内存单元值是:10317352 每个元素的值是:3
每个元素的内存单元值是:10317356 每个元素的值是:4
每个元素的内存单元值是:10317360 每个元素的值是:5
--------------------------------
Process exited after 15.06 seconds with return value 0
请按任意键继续. . .
方法中的基本变量是在栈中继续拿给你分配的,而使用了malloc函数是在堆中进行分配的。和java中是一样的使用方式。只不过在C语言中和java中,一个是手动释放,一个是通过JVM的GC算法来进行垃圾回收的。
5、动态内存和静态内存比较
静态内存是由操作系统来进行分配的,分配的位置是在栈中的,栈运行完成,操作系统分配的内存将会由操作系统来释放;
动态内存是程序员向操作系统来申请分配的,分配的位置是在堆内存中的,程序运行结束,调用free函数,将原来向操作系统申请的内存给释放掉;
6、多级指针
指针的指针,本质上保存的也是内存单元地址。
这里本质上就是为了函数的使用,但是我觉得这块比较简单,不在进行赘述了。