首页 > 系统相关 >C温故补缺(十四):内存管理

C温故补缺(十四):内存管理

时间:2022-11-15 10:13:09浏览次数:40  
标签:字节 int ++ 内存 补缺 include 温故 指针

内存管理

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

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

标签:字节,int,++,内存,补缺,include,温故,指针
From: https://www.cnblogs.com/Tenerome/p/Creview14.html

相关文章

  • C# c#中如何获取本机用户名、MAC地址、IP地址、硬盘ID、CPU序列号、系统名称、物理内
    CIMWin32WMIProvidersWMI是什么Windows管理规范(WindowsManagementInstrumentation)是一项核心的Windows管理技术;用户可以使用WMI管理本地和远程计算机。WMI通过......
  • Linux内存管理 (2)页表的映射过程【转】
    转自:https://www.cnblogs.com/arnoldlu/p/8087022.html 关键词:swapper_pd_dir、ARMPGD/PTE、LinuxPGD/PTE、pgd_offset_k。 Linux下的页表映射分为两种,一是Linux自......
  • Memcached内存缓存服务
       内存缓存(Memcached)是一个开源的、高性能的分布式内存对象缓存系统。通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高网站访问速度,加速动态WEB应用、......
  • JMM内存模型学习笔记
    java内存模型1.到底什么叫“底层原理”1.1从java到cpu指令最开始,我们编写的Java代码,是.java文件在编译(javac命令)后,从刚才的.java文件会变出一个新的Java字节......
  • 内存管理
    访存次数\(\begin{array}{|c|c|}\hline&访存次数\\\hline基本分页存储管理&2\\\hline基本分段存储管理&2\\\hline段页式管理&3\\\hline\end{arr......
  • C温故补缺(十三):可变参数
    可变参数stdarg.h 头文件提供了实现可变参数功能的函数和宏。具体步骤如下:定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数,一般传入参数的个数。int......
  • .net 温故知新:【9】.NET日志记录 ILogger使用和原理
    .net温故知新:【9】.NET日志记录ILogger使用和原理 日志日志作为我们程序记录的“黑匣子”不论什么系统都应该使用到的,比如我们经常使用的log4net就是第三方日志记......
  • 查看电脑支持最大内存
    1. win+r输入cmd打开黑窗口2.输入命令wmicmemphysicalgetmaxcapacity得到数据,单位是KB3.数据转换67108864/1024/1024=64(GB) ......
  • JUC学习笔记——共享模型之内存
    JUC学习笔记——共享模型之内存在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的内存部分我们会分为以下几部分进行介绍:Java内存模型可见性模式之两阶段......
  • 容器内存相关知识
    这篇文章是我研究容器内存整理出的相关内容.前后内容并没有上下文关系,每个知识点都可以单独查看.内存控制使用这样的命令启动一个容器dockerrun-d-m300Mxxx.可......