内存管理

stdlib库中有几个内存管理相关的函数

序号 函数和描述
1 void *calloc(int num, int size);
在内存中动态地分配 num 个长度为size 个字节 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。
2 void free(void *address);
该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
3 void *malloc(int size);
在堆区分配一块size个字节的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
4 void *realloc(void *address, int newsize);
该函数重新分配内存,把内存扩展到 newsize,newsize的单位还是字节

总之三个alloc分配的都是字节,calloc分配num个size字节的空间,malloc分配size个字节的空间,realloc重新分配size个字节的空间,三个都返回分配的空间的基址,然后用一个指针存这个基址,free()用来释放掉这个指针.

如:

#include<stdio.h>
#include<stdlib.h> int main(){
int *a=calloc(10,4);
realloc(a,80);
for(int i=0;i<20;i++){
*(a+i)=2*i;
}
for(int i=0;i<20;i++){
printf("%d ",*(a+i));
}
free(a);
}

分配10个4字节空间,一个4字节的空间可以存一个int或long,之后重新分配80个字节,也就是之前空间的两倍

两次for循环存数,读数,最后释放掉指针

问题1:

重新分配内存会不会把之前的存储释放?

实验:

#include<stdio.h>
#include<stdlib.h> int main(){
int *a=malloc(4);
*a=78;
printf("%d\n",*a);
realloc(a,8);
*(a+1)=99;
printf("%d %d",*a,*(a+1));
}

结果:

所以重新分配内存不会释放掉之前的内存,而是在原内存的基础上添加新的空间,而且如果重新分配的内存比之前的空间小,并不会截断之间后面的内存,还是可以访问的

#include<stdio.h>
#include<stdlib.h> int main(){
int *a=malloc(8);
*a=78;
*(a+1)=99;
realloc(a,4);
printf("%d %d",*a,*(a+1));
}

问题2:

如果只分配4字节的空间,但是强制使用大于4字节的空间,能正常存取吗?

#include<stdlib.h>
#include<stdio.h> int main(){
int *a=malloc(4);
for(int i=0;i<4;i++){
*(a+i)=i+4;
}
for(int i=0;i<4;i++){
printf("%d ",*a);
a++;
}
}

输出

存取成功!也就是说,实际分配的空间不管多大,当指针移动时,都是按数据的类型移动的,并按数据类型对齐,所以能正确的存取数据

看调试过程,a的地址是每次增加4的

如果不用alloc分配内存,直接用一个指针呢?

#include<stdlib.h>
#include<stdio.h> int main(){
int *a;
a++;
a++;
*a=1;
printf("%d ",*a);
}

断点调试时可以看到a的地址是0x10,每次a++,地址+4

每次a++的反汇编代码是add指令

在对*a赋值时出现Segmentation fault异常,也就是不能对*a赋值

segmentation fault:随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump.

查看反汇编的内存地址

可以看到0x3fffff以前的内存是无法访问的,从0x400000才是程序能用的地址

猜测:因为内存的分段:

0x3fffff之前的内存空间都是属于kernal的,所以不能用

那如果给指针初始化一个可使用的内存空间呢?

#include<stdlib.h>
#include<stdio.h> int main(){
int z;
int *a=&z;
*a=1;
printf("%d ",*a);
}

成功,这也是正常的调用

断点调试看内存地址:

问题3:

如果给指针初始化一个int型的地址,它会自动对齐后续的空间吗?

若继续后移指针,并存数取数:

#include<stdlib.h>
#include<stdio.h> int main(){
int z;
int *a=&z;
*a=1;
printf("%d ",*a);
a++;
*a=2;
printf("%d ",*a);
}

结果只输出了一个,但也没有报错,再断点调试看看:

可以看到在后移指针后,在给*a赋值,a的地址发生了改变,也就说*a=a,a指向本身?

验证:

#include<stdlib.h>
#include<stdio.h> int main(){
int z;
int *a=&z;
*a=1;
printf("%d ",*a);
a++;
printf("%d %d",a,*a);
}

尝试*a=&x

正常情况下,如

int *a;
int x;
*a=&x;

这样是错误的,会报异常:assignment to 'int' from 'int *' makes integer from pointer without a cast,即从“int *”赋值到“int”会使指针中的整数没有强制转换,因为a是一个int型指针,*a得到的应该是一个int型,&x则是一个int *,这个操作相当于把一个int *赋值给一个int

但是由上个问题实验得知,初始化后的指针后移会指向本身,所以int是可以存int*的

实践:

#include<stdio.h>
int main(){
int z=1;
int *a=&z;
a++;
int x;
*a=&x;
*a=4;
printf("%d",*a); }

虽然有warning,但是成功输出了,证明了此时a就是指向本身,同时也证明了c语言中的数据类型是互用存储的,只要字节长度相同就能存,int ,int *只是一个规定的标签罢了

运行完*a=&x后,a的值变成了x的地址

问题4:

如果只是在int *前有int型变量初始化,那么指针会自动对齐

如:

#include<stdio.h>
int main(){
int a=1;
int *p;
*p=3;
p++;
*p=5;
}

调试过程:

而且可以看到,指针后移之后,*p被初始化成了0

问题总结

alloc分配内存时会自动对齐,即使分配的空间很小,也能继续使用空间

int a;如果不赋值的化,就会被编译器优化掉

直接对指针操作,无法赋值,因为初始它指向的是不能访问的内核空间

如果给指针赋值&a,并不会自动对齐后续的空间,p++得到的是&a++,实际上,p++后指针指向本身

但是如果给变量赋了初值,int a=1;之后对指针直接操作,是自动对齐的,且,p++后的*p会初始化成0

以上探究只是为了搞清楚内存分配的实质,但在项目中不要随便用,这些可能属于未定义行为,很容易导致异常

最新文章

  1. 嵌入式Linux驱动学习之路(七)Linux内核启动流程
  2. mysql 的 find_in_set函数使用方法
  3. [LintCode] Perfect Squares 完全平方数
  4. Dynamic Animator 、 CollectionViewLayout
  5. Linux服务器使用命令操作MySQL插入数据乱码问题
  6. ASPX页面包含inc文件、用户控件、普通html文件
  7. 使用JavaScript实现简单的输入校验
  8. Django 1.10中文文档—第一个Django应用Part1
  9. CF Zepto Code Rush 2014 B. Om Nom and Spiders
  10. HAProxy+apache实现web服务动静分离
  11. android开发进阶学习博客资源
  12. GitHub 系列之「怎样使用 GitHub?」
  13. 使用mpvue开发小程序教程(二)
  14. 备份恢复工具xtrabackup安装和使用的记录
  15. luogu P4385 [COCI2009]Dvapravca
  16. 处理器 趣事 CPU/GPU/TPU/DPU/BPU
  17. Community宣言
  18. 『计算机视觉』Mask-RCNN_从服装关键点检测看KeyPoints分支
  19. 开源项目几点心得,Java架构必会几大技术点
  20. spark-submit提交参数设置

热门文章

  1. 操作服务器的神奇工具Tmux
  2. python 作用域与命名空间
  3. 在Windows Server 2019上安装edge浏览器
  4. 《Java基础——制表符》
  5. 云原生之旅 - 2)Docker 容器化你的应用
  6. kvm安装windows使用virtio驱动
  7. 不停机为虚拟机添加主机磁盘(以VMware Workstation为例)
  8. Elasticsearch准实时索引实现(数据写入到es分片并存储到文件中的过程)
  9. 1_Html
  10. [题解] Codeforces 1349 D Slime and Biscuits 概率,推式子,DP,解方程