C温故补缺(十四):内存管理
内存管理
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
以上探究只是为了搞清楚内存分配的实质,但在项目中不要随便用,这些可能属于未定义行为,很容易导致异常
C温故补缺(十四):内存管理的更多相关文章
- 使用虚幻引擎中的C++导论(四-内存管理与垃圾回收)(终)
使用虚幻引擎中的C++导论(四)(终) 第一,这篇是我翻译的虚幻4官网的新手编程教程,原文传送门,有的翻译不太好,但大体意思差不多,请支持我O(∩_∩)O谢谢. 第二,某些细节操作,这篇文章省略了,如 ...
- 十.oc内存管理
引用百度百科图 栈(stack)又名堆栈. 栈定义:栈是限定仅在表头进行插入和删除操作的线性表(有序).(又称:后进先出表) (动态)数据展示存储的地方.(举例:升降电梯)特点:先进后出(FILO—F ...
- 《objective-c基础教程》学习笔记(十)—— 内存管理
本篇博文,将给大家介绍下再Objective-C中如何使用内存管理.一个程序运行的时候,如果不及时的释放没有用的空间内存.那么,程序会越来越臃肿,内存占用量会不断升高.我们在使用的时候,就会感觉很卡, ...
- MySQL(十四)管理维护及性能优化
关于MySQL的学习,<MySQL必知必会>这本书呢,看完已经两个月了,一直被工作以及生活的一些琐事拖着,趁着今晚有空闲,就整理完了最后的几章学习笔记,接下来的学习计划呢, 应该是pyth ...
- Linux命令(二十四) 磁盘管理命令(二) mkfs,mount
一.格式化文件系统 mkfs 当完成硬盘分区以后要进行硬盘的格式化,mkfs系列对应的命令用于将硬盘格式化为指定格式的文件系统.mkfs 本身并不执行建立文件系统的工作,而是去调用相关的程序来执行.例 ...
- Oracle学习(十四):管理用户安全性
--用户(user) SQL> --创建一个名为 grace password是password 的用户,新用户没有不论什么权限 SQL> create user grace identi ...
- OC-手动内存管理
一.为什么要进行内存管理 •移动设备的内存极其有限,每个app所能占用的内存是有限制的 • •下列行为都会增加一个app的内存占用 Ø创建一个OC对象 Ø定义一个变量 Ø调用一个函数或者方法 • •当 ...
- 启动期间的内存管理之pagging_init初始化分页机制--Linux内存管理(十四)
1 今日内容(分页机制初始化) 在初始化内存的结点和内存区域之前, 内核先通过pagging_init初始化了内核的分页机制. 在分页机制完成后, 才会开始初始化系统的内存数据结构(包括内存节点数据和 ...
- Spark(四十六):Spark 内存管理之—OFF_HEAP
存储级别简介 Spark中RDD提供了多种存储级别,除去使用内存,磁盘等,还有一种是OFF_HEAP,称之为 使用JVM堆外内存 https://github.com/apache/spark/blo ...
- 【读书笔记】C#高级编程 第十四章 内存管理和指针
(一)后台内存管理 1.值数据类型 Windows使用一个虚拟寻址系统,该系统把程序可用的内存地址映射到硬件内存中的实际地址,该任务由Windows在后台管理(32位每个进程可使用4GB虚拟内存,64 ...
随机推荐
- JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容
大家好,又见面啦. 在前一篇文档<JAVA中自定义扩展Swagger的能力,自动生成参数取值含义说明,提升开发效率>中,我们探讨了如何通过自定义注解的方式扩展swagger的能力让Swag ...
- JS作用域、变量提升和闭包
作用域 作用域可以理解为JS引擎执行代码的时候,查找变量的规则. 从确定变量访问范围的阶段的角度,可以分为2类,词法作用域和动态作用域.js是词法作用域. 从变量查找的范围的角度,可以分为3类,全局作 ...
- Java SE 多态
1.多态 方法的多态 //方法重载体现多态 A a = new A(); //这里我们传入不同的参数,就会调用不同sum方法 System.out.println(a.sum(10,20)); Sys ...
- thinkphp5.1打印SQL语句
最近在写tp框架搭建的小玩具,有时候我们需要查看SQL语句.所以就诞生了这篇随笔,命令如下: $xxx=db('xxx')->where('x',xx)->select(); $sql=D ...
- Compose 命令说明
命令对象与格式 对于 Compose 来说,大部分命令的对象既可以是项目本身,也可以指定为项目中的服务或者容器.如果没有特别的说明,命令对象将是项目,这意味着项目中所有的服务都会受到命令影响. 执行 ...
- 《Go 精进之路》 读书笔记 (第一次更新)
<Go 精进之路> 读书笔记.简要记录自己打五角星的部分,方便复习巩固.目前看到p120 Go 语言遵从的设计哲学为组合 垂直组合:类型嵌入,快速让一个类型复用其他类型已经实现的能力,实现 ...
- C++ 自学笔记 new和delete(动态内存分配)
动态内存分配 Dynamic memoey allocation C++使用new和delete 来申请和释放内存 new:先申请一个空间 int\Stash : 默认构造函数初始化对象 ~:析构函数 ...
- C#-2 C#程序
一 C#程序是一组类型声明 C#程序或DLL的源代码是一组一种或多种类型声明. 对于可执行程序,类型声明中必须有一个包含Main方法的类. 命名空间是一种把相关的类型声明分组并命名的方法.是类在程序集 ...
- jq修改多个css样式
$("#xxx").css({"属性名称": "属性值", "属性名称": "属性值" });
- (数据科学学习手札144)使用管道操作符高效书写Python代码
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,一些比较熟悉pandas的读者 ...