c语言输出11258循环,c/c++内存机制(一)(转)

一:C语言中的内存机制

在C语言中,内存主要分为如下5个存储区:

(1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。

(2)堆(Heap):由程序员用malloc/calloc/realloc分配,free释放。如果程序员忘记free了,则会造成内存泄露,程序结束时该片内存会由OS回收。

(3)全局区/静态区(Global Static Area): 全局变量和静态变量存放区,程序一经编译好,该区域便存在。并且在C语言中初始化的全局变量和静态变量和未初始化的放在相邻的两个区域(在C++中,由于全局变量和静态变量编译器会给这些变量自动初始化赋值,所以没有区分了)。由于全局变量一直占据内存空间且不易维护,推荐少用。程序结束时释放。

(4)C风格字符串常量存储区: 专门存放字符串常量的地方,程序结束时释放。

(5)程序代码区:存放程序二进制代码的区域。

二:C++中的内存机制

在C++语言中,与C类似,不过也有所不同,内存主要分为如下5个存储区:

(1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。

(2)堆(Heap):这里与C不同的是,该堆是由new申请的内存,由delete或delete[]负责释放

(3)自由存储区(Free Storage):由程序员用malloc/calloc/realloc分配,free释放。如果程序员忘记free了,则会造成内存泄露,程序结束时该片内存会由OS回收。

(4)全局区/静态区(Global Static Area): 全局变量和静态变量存放区,程序一经编译好,该区域便存在。在C++中,由于全局变量和静态变量编译器会给这些变量自动初始化赋值,所以没有区分了初始化变量和未初始化变量了。由于全局变量一直占据内存空间且不易维护,推荐少用。程序结束时释放。

(5)常量存储区: 这是一块比较特殊的存储区,专门存储不能修改的常量(如果采用非正常手段更改当然也是可以的了)。

三:堆和栈的区别

3.1 栈(Stack)

具体的讲,现代计算机(冯诺依曼串行执行机制),都直接在代码低层支持栈的数据结构。这体现在有专门的寄存器指向栈所在的地址(SS,堆栈段寄存器,存放堆栈段地址);有专门的机器指令完成数据入栈出栈的操作(汇编中有PUSH和POP指令)。

这种机制的特点是效率高,但支持数据的数据有限,一般是整数、指针、浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构(可以自定义栈结构支持多种数据类型)。因为栈的这种特点,对栈的使用在程序中是非常频繁的 。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址入栈,然后跳转至子程序地址的操作,而子程序的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。

C/C++中的函数自动变量就是直接使用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的原因,因而要避免返回栈内存和栈引用,以免内存泄露。

3.2 堆(Heap)

和栈不同的是,堆得数据结构并不是由系统(无论是机器硬件系统还是操作系统)支持的,而是由函数库提供的。基本的malloc/calloc/realloc/free函数维护了一套内部的堆数据结构(在C++中则增加了new/delete维护)。

当程序用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间(常见内存分配算法有:首次适应算法、循环首次适应算法、最佳适应算法和最差适应算法等。os的基本内容!!)。如果没有可用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。当程序释放分配的内存空间时,这片内存空间被返回到内部堆结构中,可能会被适当的处理(比如空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。 这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下几个原因:

(1)系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配);这样的话对于大量的小内存分配来说会造成浪费。

(2)系统调用申请内存可能是代价昂贵的。 系统调用可能涉及到用户态和核心态的转换。

(3)没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。

3.3 栈和堆的对比

从以上介绍中,它们有如下区别:

(1)栈是系统提供的功能,特点是快速高效,缺点是由限制,数据不灵活;

堆是函数库提供的功能,特点是灵活方便,数据适应面广,但是效率有一定降低。

(2)栈是系统数据结构,对于进程/线程是唯一的;

堆是函数库内部数据结构,不一定唯一,不同堆分配的内存无法互相操作。

(3)栈空间分静态分配和动态分配,一般由编译器完成静态分配,自动释放,栈的动态分配是不被鼓励的;

堆得分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存/释放内存匹配是良好程序的基本要素。

(4)碎片问题

对于堆来讲,频繁的new/delete等操作势必会造成内存空间的不连续,从而造成大量的碎片,使程序的效率降低;对于栈来讲,则不会存在这个问题,因为栈是后进先出(LIFO)的队列。

(5)生长方向

堆的生长方向是向上的,也就是向这内存地址增加的方向;对于栈来讲,生长方向却是向下的,是向着内存地址减少的方向增长。

(6)分配方式

堆都是动态分配的,没有静态分配的堆;

栈有两种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配则由alloca函数进行分配,但是栈的动态分配和堆不同,它的动态分配是由编译器进行释放,无需我们手工实现。

(7)分配效率

栈是机器系统提供的数据结构,计算机在底层提供支持,分配有专门的堆栈段寄存器,入栈出栈有专门的机器指令,这些都决定了栈的高效率执行。

堆是由C/C++函数库提供的,机制比较复杂,有不同的分配算法,易产生内存碎片,需要对内存进行各种管理,效率比栈要低很多。

四:具体实例分析

例子(一)

看下面的一小段C程序,仔细体会各种内存分配机制。

ef5f243b23e0b8ab1dcdb3ab50e771f6.gif

int a = 0; //全局初始化区,a的值为0

char *p1; //全局未初始化区(C++中则初始化为NULL) intmain() { int b; //b分配在栈上,整型 char s[] = "abc"; //s分配在栈上,char *类型;"abc\0"分配在栈上,运行时赋值,函数结束销毁 char *p2; //p2分配在栈上,未初始化 char *p3 = "123456"; //p3指向"123456"分配在字符串常量存储区的地址,编译时确定 static int c = 0; //c在全局(静态)初始化区,可以多次跨函数调用而保持原值 p1 = (char *)malloc(10); //p1在全局未初始化区,指向分配得来得10字节的堆区地址 p2 = (char *)malloc(20); //p2指向分配得来得20字节的堆区地址 strcpy(p1, "123456"); //"123456"放在字符串常量存储区,编译器可能会将它与p3所指向的"123456"优化成一块 return 0; }

4dcc3ce362d35c305ed21d3f2be6a970.gif

例子(二)

看下面的一小段代码,体会堆与栈的区别:

cf1c60e16d0d9c2eec6664a397127b43.gif

intfoo()

{

//其余代码

int *p = new int[5]; //其余代码 return 0; }

95c691ccf12e7c39860f86275d62d8e7.gif

其中的语句int *p = new int[5];就包含了堆与栈。其中new关键字分配了一块堆内存,而指针p本身所占得内存为栈内存(一般4个字节表示地址)。这句话的意思是在栈内存中存放了一个指向一块堆内存的指针p。在程序中先确定在堆中分配内存的大小,然后调用new关键字分配内存,最后返回这块内存首址,放入栈中。汇编代码为:

51a0e738b0397882de2a74f9bcd5c8de.gif

intfoo()

{

008C1520 push ebp

008C1521 mov ebp,esp

008C1523 sub esp,0D8h

008C1529 push ebx

008C152A push esi

008C152B push edi

008C152C lea edi,[ebp-0D8h]

008C1532 mov ecx,36h

008C1537 mov eax,0CCCCCCCCh

008C153C rep stos dword ptr es:[edi]

int *p = new int[5]; 008C153E push 14h 008C1540 call operator new[] (8C1258h) 008C1545 add esp,4008C1548 mov dword ptr [ebp-0D4h],eax 008C154E mov eax,dword ptr [ebp-0D4h] 008C1554 mov dword ptr [p],eax return 0; 008C1557 xor eax,eax } 008C1559 pop edi 008C155A pop esi 008C155B pop ebx 008C155C add esp,0D8h 008C1562 cmp ebp,esp 008C1564 call @ILT+395(__RTC_CheckEsp) (8C1190h) 008C1569 mov esp,ebp 008C156B pop ebp 008C156C ret

cd1f834856372abff1ef4ef909d82251.gif

如果需要释放内存,这里我们需要使用delete[] p,告诉编译器,我要删除的是一个数组。

例子(三)

看下面的一小段代码,试着找出其中的错误:

7aacfc212cca4bee7b9c86375d7c7c87.gif

#include

using namespacestd;

intmain() { char a[] = "Hello"; //分配在栈上 a[0] = 'X'; cout << a <

51b4f9c0c72513ba40aa323fbbca588f.gif

发现问题了吗?是的,字符数组a的容量是6个字符,其内容为"hello\0"。a的内容时可以改变的,比如a[0]='X',因为其是在栈上分配的,也就是在运行时确定的内容。但是指针p指向的字符串"world"分配在字符串常量存储区,内容为"world\0",常量字符串的内容时不可以修改的。从语法上来说,编译器并不觉得语句p[0]='X'有什么问题,但是在运行时则会出现"access violation"非法内存访问的问题。

以下几个函数的变化要看清楚了:

ca83d8940d824ffc74491c06bb1c93a9.png

7f0c945651cacfa32551df9ae82b7ce2.gif

char *GetString1(void)

{

char p[] = "hello,world"; //结果:h。由于数组指针指向第一元素的地址,所以调用之后是h returnp; } char *GetString2(void) { char *p = "hello,world"; //结果:hello,world。由于p指向“hello,world”字符串常量区域地址 returnp; } char *GetString3(void) { char *p = (char *)malloc(20); //指向p所分配的堆上的内存空间。 returnp; } char *GetString4(void) { char *p = new char[20]; //指向p所分配的内存空间,p本身在栈上的,p所指向的空间是堆上的。 returnp; }

fb71f6e0da35763773d2f04f3e881091.gif

附录:内存管理注意事项

d36d0c9116342906ec7eda99b02e082e.png

【规则1】用malloc或new申请内存之后,应该立即检查指针值是否为NULL,防止使用指针值为NULL的内存,可以在函数入口处断言检测。

【规则2】不要忘记为数组或动态内存赋初值(比如calloc比malloc就要好),指针初始化为NULL(c++中为0)。

【规则3】避免数组或指针下标越界,特别

d0053015efdd2bb166c31199cf43a80a.png当心发生“多1”或者"少1"

2221329ea698d6451219f86a3eb7208c.png的操作。

【规则4】动态内存的申请和释放必须配对,防止内存泄露,具体为malloc/calloc/realloc和free配对,new和delete以及delete[]配对。

【规则5】用free或者delete释放内存后,应立即将指针设置为NULL(C++中为0),防止产生“野指针”、"悬垂指针"。

【规则6】遇到不懂得问题及时debug,一般的虫子

792158a84d8c3cfacf8770d17ccb03c8.pngdebug一下就灰飞烟灭了,一切bug都是浮云而已

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378281.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【神经网络八股扩展】:数据增强

课程来源&#xff1a;人工智能实践:Tensorflow笔记2 文章目录前言TensorFlow2数据增强函数数据增强网络八股代码&#xff1a;总结前言 本讲目标:数据增强&#xff0c;增大数据量 关于我们为何要使用数据增强以及常用的几种数据增强的手法&#xff0c;可以看看下面的文章&#…

分享WCF聊天程序--WCFChat

无意中在一个国外的站点下到了一个利用WCF实现聊天的程序&#xff0c;作者是&#xff1a;Nikola Paljetak。研究了一下&#xff0c;自己做了测试和部分修改&#xff0c;感觉还不错&#xff0c;分享给大家。先来看下运行效果&#xff1a;开启服务&#xff1a;客户端程序&#xf…

【神经网络扩展】:断点续训和参数提取

课程来源&#xff1a;人工智能实践:Tensorflow笔记2 文章目录前言断点续训主要步骤参数提取主要步骤总结前言 本讲目标:断点续训&#xff0c;存取最优模型&#xff1b;保存可训练参数至文本 断点续训主要步骤 读取模型&#xff1a; 先定义出存放模型的路径和文件名&#xff0…

小米手环6NFC安装太空人表盘

以前看我室友峰哥、班长都有手环&#xff0c;一直想买个手环&#xff0c;不舍得&#xff0c;然后今年除夕的时候降价&#xff0c;一狠心&#xff0c;入手了&#xff0c;配上除夕的打年兽活动还有看春晚京东敲鼓领的红包和这几年攒下来的京东豆豆&#xff0c;原价279的小米手环6…

为什么两层3*3卷积核效果比1层5*5卷积核效果要好?

目录1、感受野2、2层3 * 3卷积与1层5 * 5卷积3、2层3 * 3卷积与1层5 * 5卷积的计算量比较4、2层3 * 3卷积与1层5 * 5卷积的非线性比较5、2层3 * 3卷积与1层5 * 5卷积的参数量比较1、感受野 感受野&#xff1a;卷积神经网络各输出特征像素点&#xff0c;在原始图片映射区域大小。…

算法正确性和复杂度分析

算法正确性——循环不变式 算法复杂度的计算 方法一 代换法 —局部代换 这里直接对n变量进行代换 —替换成对数或者指数的情形 n 2^m —整体代换 这里直接对递推项进行代换 —替换成内部递推下标的形式 T(2^n) S(n) 方法二 递归树法 —用实例说明 —分析每一层的内容 —除了…

第十五章 Python和Web

第十五章 Python和Web 本章讨论Python Web编程的一些方面。 三个重要的主题&#xff1a;屏幕抓取、CGI和mod_python。 屏幕抓取 屏幕抓取是通过程序下载网页并从中提取信息的过程。 下载数据并对其进行分析。 从Python Job Board&#xff08;http://python.org/jobs&#x…

【数据结构基础笔记】【图】

代码参考《妙趣横生的算法.C语言实现》 文章目录前言1、图的概念2、图的存储形式1、邻接矩阵&#xff1a;2、邻接表3、代码定义邻接表3、图的创建4、深度优先搜索DFS5、广度优先搜索BFS6、实例分析前言 本章总结&#xff1a;图的概念、图的存储形式、邻接表定义、图的创建、图…

如何蹭网

引言蹭网&#xff0c;在普通人的眼里&#xff0c;是一种很高深的技术活&#xff0c;总觉得肯定很难&#xff0c;肯定很难搞。还没开始学&#xff0c;就已经败给了自己的心里&#xff0c;其实&#xff0c;蹭网太过于简单。我可以毫不夸张的说&#xff0c;只要你会windows的基本操…

android对象缓存,Android简单实现 缓存数据

前言1、每一种要缓存的数据都是有对应的versionCode&#xff0c;通过versionCode请求网络获取是否需要更新2、提前将要缓存的数据放入assets文件夹中&#xff0c;打包上线。缓存设计代码实现/*** Created by huangbo on 2017/6/19.** 主要是缓存的工具类** 缓存设计&#xff1a…

通信原理.绪论

今天刚上通信原理的第一节课&#xff0c;没有涉及过多的讲解&#xff0c;只是讲了下大概的知识框架。现记录如下&#xff1a; 目录1、基本概念消息、信息与信号2、通信系统模型1、信息源2、发送设备3、信道4、接收设备5、信宿6、模拟通信系统模型7、数字通信系统模型8、信源编…

css rgba透明_rgba()函数以及CSS中的示例

css rgba透明Introduction: 介绍&#xff1a; Functions are used regularly while we are developing a web page or website. Therefore, to be a good developer you need to master as many functions as you can. This way your coding knowledge will increase as well …

犀牛脚本:仿迅雷的增强批量下载

迅雷的批量下载满好用。但是有两点我不太中意。在这个脚本里会有所增强 1、不能设置保存的文件名。2、不能单独设置这批下载的线程限制。 使用方法 // 下载从编号001到编号020的图片&#xff0c;保存名为猫咪写真*.jpg 使用6个线程 jdlp http://bizhi.zhuoku.com/bizhi/200804/…

android 服务端 漏洞,安卓漏洞 CVE 2017-13287 复现详解-

2018年4月&#xff0c;Android安全公告公布了CVE-2017-13287漏洞。与同期披露的其他漏洞一起&#xff0c;同属于框架中Parcelable对象的写入(序列化)与读出(反序列化)的不一致所造成的漏洞。在刚看到谷歌对于漏洞给出的补丁时一头雾水&#xff0c;在这里要感谢heeeeenMS509Team…

GAP(全局平均池化层)操作

转载的文章链接&#xff1a; 为什么使用全局平均池化层&#xff1f; 关于 global average pooling https://blog.csdn.net/qq_23304241/article/details/80292859 在卷积神经网络的初期&#xff0c;卷积层通过池化层&#xff08;一般是 最大池化&#xff09;后总是要一个或n个全…

zoj1245 Triangles(DP)

/* 动态三角形&#xff1a;每次DP时考虑的是两个子三角形的高度即可 注意&#xff1a; 三角形可以是倒置的。 */ View Code 1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <stdio.h> 5 6 using namespace std; 7 8…

android编程从零开始,从零开始学习android开发

博主最近开通了Android栏目&#xff0c;现在正在从零开始学习android&#xff0c;遇到的所有值得分享的知识点以及遇到的问题将发布在这个博客的android栏目下。因为我有着深厚的java底子&#xff0c;所以学习起来得心应手&#xff0c;十分的简单&#xff0c;当然也只能算是入门…

CNN基本步骤以及经典卷积(LeNet、AlexNet、VGGNet、InceptionNet 和 ResNet)网络讲解以及tensorflow代码实现

课程来源&#xff1a;人工智能实践:Tensorflow笔记2 文章目录前言1、卷积神经网络的基本步骤1、卷积神经网络计算convolution2、感受野以及卷积核的选取3、全零填充Padding4、tf描述卷积层5、批标准化(BN操作)6、池化Pooling7、舍弃Dropout8、卷积神经网络搭建以及参数分析2、经…

第十九章 趣味编程

第十九章 趣味编程 本章将介绍一些通用的Python编程指南。 为何要有趣 Python有趣的地方之一就是让用户的编程效率非常高效。 极限编程是一种软件开发方法 编程柔术 python的灵活性描述原型设计Python的优点之一是让你能够快速地编写程序。要更深入地了解面临的问题&#…

【数据结构基础笔记】【顺序表】

代码参考《妙趣横生的算法.C语言实现》 文章目录前言1、创建顺序表2、顺序表插入元素3、顺序表删除元素4、顺序表实例分析1、静态2、动态5、顺序表总结前言 本章总结&#xff1a;从静态和动态分别进行顺序表的创建、插入、删除、以及实例分析 1、创建顺序表 1、静态地生成一张…