c和c++程序内存空间浅说
- 格式:doc
- 大小:55.50 KB
- 文档页数:3
C语言内存使用详解C语言是一种低级语言,开发者可以直接控制内存使用。
了解C语言内存使用的机制和技巧对于编写高效、安全和可靠的程序至关重要。
本文将详细介绍C语言内存使用的知识和技术,并提供一些实用的建议。
在C语言中,内存是以字节为单位进行管理的,通常将内存分为栈和堆两种。
栈是一种自动分配和自动释放内存的数据结构。
它的特点是后进先出(LIFO),即最后分配的内存最先释放。
栈主要用于存储局部变量、函数参数和函数调用的上下文信息。
在函数调用结束后,分配给局部变量的内存会自动释放。
堆是一种动态分配内存的数据结构,程序员可以手动分配和释放内存。
堆的管理需要调用系统提供的函数,如malloc(和free(。
堆主要用于存储动态分配的数据,如数组、结构体和指针。
程序员需要手动管理堆内存,确保及时释放不再使用的内存,否则会造成内存泄漏。
为了更好地使用内存,提高程序的性能和可靠性,下面是一些C语言内存使用的技巧和注意事项:1.使用局部变量:局部变量是保存在栈上的,它们的生命周期与函数的调用关系密切相关。
局部变量不仅可以节约内存,还可以提高程序的执行效率。
2.合理分配静态变量和全局变量:静态变量和全局变量在程序执行过程中一直存在,它们的生命周期不受函数调用的影响。
过多的静态变量和全局变量会占用大量的内存,影响程序的性能。
3. 动态分配内存时要检查返回值:在调用malloc(等动态分配内存的函数时,要检查返回值是否为NULL。
如果返回值为NULL,表示没有足够的内存可用。
处理内存分配失败的情况至关重要,可以提前终止程序或采取其他恰当的措施。
4. 及时释放不再使用的内存:动态分配的内存在不再使用时要及时释放,以避免内存泄漏。
使用free(函数将内存返回给系统,以供其他程序使用。
5.防止指针错误:指针是C语言中非常重要的概念,但也容易出现指针错误,如空指针引用、越界访问等。
使用指针时要特别小心,确保指针正确地指向有效的内存区域。
深入理解C11/C++11内存模型现代计算机体系结构上,CPU执行指令的速度远远大于CPU访问内存的速度,于是引入Cache机制来加速内存访问速度。
除了Cache以外,分支预测和指令预取也在很大程度上提升了CPU的执行速度。
随着SMP的出现,多线程编程模型被广泛应用,在多线程模型下对共享变量的访问变成了一个复杂的问题。
于是我们有必要了解一下内存模型,这是多处理器架构下并发编程里必须掌握的一个基础概念。
1. 什么是内存模型?到底什么是内存模型呢?看到有两种不同的观点:•A:内存模型是从来描述编程语言在支持多线程编程中对共享内存访问的顺序。
•B:内存模型的本质是指在单线程情况下CPU指令在多大程度上发生指令重排(reorder)[1]。
实际上A,B两种说法都是正确的,只不过是在尝试从不同的角度去说明memory model的概念。
个人认为,内存模型表达为“内存顺序模型”可能更加贴切一点。
一个良好的memory model定义包含3个方面:•Atomic Operations•Partial order of operations•Visable effects of operations这里要强调的是:我们这里所说的浪潮服务器内存模型和CPU的体系结构、编x86_64和Sparc是强顺序模型(Total Store Order),这是一种接近程序顺序的顺序模型。
所谓Total,就是说,内存(在写操作上)是有一个全局的顺序的(所有人看到的一样的顺序),就好像在内存上的每个Store动作必须有一个排队,一个弄完才轮到另一个,这个顺序和你的程序顺序直接相关。
所有的行为组合只会是所有CPU内存程序顺序的交织,不会发生和程序顺序不一致的地方[4]。
TSO 模型有利于多线程程序的编写,对程序员更加友好,但对芯片实现者不友好。
CPU为了TSO的承诺,会牺牲一些并发上的执行效率。
弱内存模型(简称WMO,Weak Memory Ordering),是把是否要求强制顺序这个要求直接交给程序员的方法。
c语言的内存空间储存
C语言中的内存空间存储有以下几种方式:
1. 栈内存(stack):栈内存是程序在运行时自动分配和释放的一块连续内存空间,用于存储局部变量、函数参数、函数返回地址等。
栈内存的分配和释放由编译器自动管理,具有自动回收的特性。
2. 堆内存(heap):堆内存是由程序员手动申请和释放的一块内存空间,用于存储动态分配的变量、数据结构等。
堆内存的分配和释放由程序员负责管理,需要调用相应的函数(如malloc()、free())来进行操作。
3. 全局变量存储区(global/static):全局变量和静态变量(static)存储在程序的全局数据段中,这部分内存空间在程序启动时分配,直到程序终止时才被释放。
全局变量存储在静态存储区,有固定的内存地址,全局变量默认为零初始化。
4. 常量存储区(const):常量存储区用于存储程序中的常量值,这些常量值不能被修改。
常量存储区通常存储在只读内存中,具有固定的内存地址。
5. 程序代码区(code/text):程序代码区存储了程序的指令代码,是只读内存区域,存储了程序的执行逻辑。
需要注意的是,C语言的内存模型可能因编译器和操作系统的不同而有所差异,
以上是一种常见的内存存储模型。
C语言中内存四区的详解C语言编程2022-05-10 14:00来自:今日头条,作者:抖点料er链接:https:///article/7046019680989037069/1、内存四区1.1数据类型本质分析1.1.1数据类型的概念•“类型”是对数据的抽象•类型相同的数据有相同的表示形式、存储格式以及相关的操作•程序中使用的所有数据都必定属于某一种数据类型1.1.2数据类型的本质•数据类型可理解为创建变量的模具:是固定内存大小的别名。
•数据类型的作用:编译器预算对象(变量)分配的内存空间大小。
•注意:数据类型只是模具,编译器并没有分酤空间,只有根据类型(模具)创建变量(实物),编译器才会分配空间。
1.2变量的本质分析1.2.1变量的概念概念:既能读又能写的内存对象,称为变量;若一旦初始化后不能修改的对象则称为常量。
变量定义形式:类型标识符,标识符,…,标识符;1.2.2变量的本质1、程序通过变量来申请和命名内存空间int a = 0。
2、通过变量名访问内存空间。
1.3程序的内存四区模型流程说明1、操作系统把物理硬盘代码load到内存2、操作系统把c代码分成四个区栈区( stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等堆区(heap):一般由程序员分配释放(动态内存申请与释放),若程序员不释放程序结束时可能由操作系统回收全局区(静态区)(statIc):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,该区域在程序结束后由操作系统释放常量区:字符串常量和其他常量的存储位置,程序结束后由操作系统释放。
程序代码区:存放函数体的二进制代码。
3、操作系统找到main函数入口执行1.4函数调用模型1.5函数调用变量传递分析(1)(2)(3)(4)(5)1.5栈的生长方向和内存存放方向相关代码:02_数据类型本质.c#define _CRT_SECURE_NO_WARNINGS #include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int main(){int a;//告诉编译器,分配4个字节int b[10];//告诉编译器,分配4*10个字节/*类型本质:固定内存块大小别名可以通过sizeof()测试*/printf("sizeof(a)=%d,sizeof(b)=%d\n", sizeof(a), sizeof(b));//打印地址//数组名称,数组首元素地址,数组首地址printf("b:%d,&b:%d\n",b,&b);//地址相同//b,&b数组类型不同//b,数组首地址元素一个元素4字节,+1 地址+4//&b,整个数组首地址一个数组4*10=40字节, +1 地址+40 printf("b+1:%d,&b+1:%d\n", b + 1, &b + 1);//不同//指针类型长度,32位机器32位系统下长度是 4字节// 64 64 8char********* p = NULL;int* q = NULL;printf("%d,%d\n", sizeof(p), sizeof(q));//4 , 4return0;}03_给类型起别名.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>typedef unsigned int u32;//typedef 和结构体结合使用struct Mystruct{int a;int b;};typedef struct Mystruct2{int a;int b;}TMP;/*void 无类型1.函数参数为空,定义函数时用void修饰 int fun(void)2.函数没有返回值:使用void void fun (void)3.不能定义void类型的普通变量:void a;//err 无法确定是什么类型4.可以定义 void* 变量 void* p;//ok 32位系统下永远是4字节5.数据类型本质:固定内存块大小别名6.void *p万能指针,函数返回值,函数参数*/int main(){u32 t;//unsigned int//定义结构体变量,一定要加上struct 关键字struct Mystruct m1;//Mystruct m2;//errTMP m3;//typedef配合结构体使用struct Mystruct2m4;printf("\n");return0;}04_变量的赋值.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int main(){//变量本质:一段连续内存空间别名int a;int* p;//直接赋值a = 10;printf("a=%d\n", a);//间接赋值printf("&a:%d\n", &a);p = &a;printf("p=%d\n", p);*p = 22;printf("*p=%d,a=%d\n", *p, a);return0;}05_全局区分析.c#define _CRT_SECURE_NO_WARNINGS #include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int main(){//变量本质:一段连续内存空间别名int a;int* p;//直接赋值a = 10;printf("a=%d\n", a);//间接赋值printf("&a:%d\n", &a);p = &a;printf("p=%d\n", p);*p = 22;printf("*p=%d,a=%d\n", *p, a);return0;}06_堆栈区分析.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>char* get_str(){char str[] = "abcdef";//内容分配在栈区,函数运行完毕后内存释放printf("%s\n", str);return str;}char* get_str2(){char* temp = (char*)malloc(100);if (temp == NULL){return NULL;}strcpy(temp, "abcdefg");return temp;}int main(){char buf[128] = { 0 };//strcpy(buf,get_str());//printf("buf = %s\n", buf);//乱码,不确定内容char* p = NULL;p = get_str2();if (p != NULL){printf("p=%s\n", p);free(p);p = NULL;}return0;}07_静态局部变量.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int* getA(){static int a = 10;//在静态区,静态区在全局区return &a;}int main(){int* p = getA();*p = 5;printf("%d\n",);return0;}08_栈的生长方向.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int* getA(){static int a = 10;//在静态区,静态区在全局区return &a;}int main(){int* p = getA();*p = 5;printf("%d\n",);return0;}版权申明:内容来源网络,版权归原创者所有。
C程序的存储空间布局(⼀) 前两天在朋友的QQ空间上看到⼀个函数,如下:void foo(){int a[4];int i;for (i = 0; i <= 4; i++) {a[i] = 0;}}朋友说这个函数⾥会陷⼊死循环。
我看了下,除了数组越界问题,其它没看出有什么问题。
后来果断敲代码⽤GCC编译运⾏⼀下,发现并不像他说得那样。
于是我找他讨论,他说没道理啊,在他那确实陷⼊死循环了。
我想这应该是跟编译环境有关,可是,在什么情况下它会陷⼊死循环呢?我很疑惑,在听到朋友说到程序是⽤堆栈存放数据的,我突然想起APUE上⾯讲过的程序存储空间布局。
于是,⽴马翻到那⼀章,看到这个表:参数及环境变量栈堆未初始化数据段初始化数据段正⽂这个表每个区域的地址从底部到顶部递增。
正⽂存放机器指令部分,初始化数据段存放程序中明确赋初值的全局变量,未初始化数据段(bss段)存放未明确赋值的全局变量,栈存放⾃动变量及每次函数调⽤时所需保存的信息,堆存放动态分配的数据。
看到这表,突然,脑海⾥顿时有点思路了。
在朋友的机器上,foo函数的变量i存放于栈底(请注意栈底的地址最⾼),⽽数组a则紧跟其后(准确来说是元素a[3]),如下:ia[3]a[2]a[1]a[0]⽽for循环中,i=4时程序尝试访问a[4],此处地址刚好就是变量i的地址!于是乎,i的值就被赋予了0。
经过i++后其值变成了1但仍满⾜ i<=4的条件,新的循环便⼜开始了。
这就是为什么在朋友的机器上会发⽣死循环的原因。
但是,很不幸,这种情况在我的机器上没有发⽣。
刚开始我以为在我这⾥i位于数组a的上⾯,后来使⽤a[-1]尝试给i赋值,失败了;后来以来是编译器优化的原因,于是尝试⽤-O0选项关闭优化后再编译⼀次,还是失败了……现在暂时不知道原因。
不过,我在实验过程中⼜发现⼀些有趣的情况,我将变量i跟数组a放到函数外⾯时,我发现程序陷⼊死循环了,情况跟上⾯的类似。
C语言内存使用详解C语言是一种底层的编程语言,对内存的使用非常重要。
本文将详细解释C语言内存的使用方法和原理。
首先,我们需要了解C语言内存的组成。
C语言内存可以分为以下几个部分:1. 栈(Stack):栈是用来存储局部变量和函数调用信息的地方。
当一个函数被调用时,它的局部变量和函数调用信息会被存储在栈中。
当函数返回时,这些数据会被自动清理。
栈是一个后进先出(LIFO)的数据结构,它的大小是固定的。
2. 堆(Heap):堆是用来存储动态分配的内存的地方。
在C语言中,我们可以使用malloc(函数来在堆上动态分配内存。
堆的大小是可变的,程序员需要手动管理内存的分配和释放。
3. 数据区(Data Segment):数据区分为全局区和静态区。
全局区用来存储全局变量和静态变量,其大小是固定的。
静态区用来存储静态局部变量,它的生命周期和程序的整个执行时间相同。
4. 代码区(Code Segment):代码区用来存储程序的执行代码,包括函数体和常量数据。
代码区是只读的,不允许进行写操作。
接下来,我们来讨论一些内存使用的注意事项。
1.局部变量和全局变量:局部变量是在函数体内定义的变量,它只在函数内部可见。
全局变量是在函数外部定义的变量,它在整个程序中可见。
局部变量存储在栈上,全局变量存储在数据区。
2. 动态内存分配:在C语言中,我们可以使用malloc(函数来在堆上动态分配内存。
动态内存分配允许我们在运行时根据需要分配内存。
使用完动态分配的内存后,我们需要手动调用free(函数来释放内存,否则会造成内存泄漏。
3.内存溢出:内存溢出指的是程序在申请内存时超出了可用的内存大小。
内存溢出可能会导致程序崩溃或者产生不可预期的行为。
为了避免内存溢出,我们应该合理地管理内存的分配和释放。
4.指针和数组:指针是用来存储内存地址的变量。
我们可以使用指针来操作内存中的数据。
数组是一种特殊的数据结构,它可以在内存中连续存储多个相同类型的数据。
C语⾔与内存的关系⼀、C语⾔为什么需要内存1.C语⾔的⽬的 计算机程序⽬的是程序的运⾏,运⾏的⽬的是为了得到⼀定的结果或者是在运⾏的过程中执⾏了某项动作。
程序 = 代码(加⼯数据的动作) + 数据 函数可以完美的解释:返回值是void ,说明没有输出结果,形参为void,说明没有输⼊数据。
反之,套⽤即可。
当然也有即输出结果和输⼊数据的,如 int add(int a,int b); 所以,这就回答了为什么C语⾔需要内存这个问题了:99.9的程序需要数据的参与,内存存储可变数据,数据在程序中表现为全局变量和局部变量(在gcc中,常量也存储在内存中,在单⽚机中,常量存储在flash中,也就是存在代码段中)。
所以内存对于程序的有效⼒是本质相关的!所以说C语⾔程序需要内存。
2.冯诺依曼结构和哈佛结构 冯诺依曼结构:数据和代码放在⼀起;哈佛结构:数据和代码分开。
按照我此刻的思想去理解,代码即函数,数据即局部变量+全局变量。
在JZ2440 中运⾏的Linux系统上,运⾏应⽤程序时,所有的应⽤程序代码和数据都在DRAM中,所以是冯诺依曼结构;在单⽚机(裸机)中,程序烧写在NorFlash上,然后将程序在Flash中原地运⾏,程序中涉及到的数据在RAM(SRAM)中运⾏,这就是哈佛结构。
3.动态内存DRAM和静态内存SRAM4.内存的管理现在的⽔平,还没到管理内存的地步,我们先不研究它。
但是得知道,内存有内存管理机制,为⽤户提供了API接⼝,如C/C++中的malloc(),free(),new()等函数;java/C# 则直接回收内存,基本不⽤对内存进⾏操作了。
⼆、位、字节、半字、字的概念和内存位宽1.概念解析位=1bit,字节=8bit内存位宽、半字和字得看处理器是多少位的,⽐如说处理器是32bit的,则内存位宽是32bit,字=32bit ,半字是字的⼀半,就是16bit 了。
在MSP430上,由于它是16bit处理器,所以它的字是16bit的。
C语言程序的内存模型C语言是一种广泛使用的编程语言,其特点之一就是直接操作内存。
了解C语言程序的内存模型对于编写高效和可靠的代码至关重要。
本文将介绍C语言程序的内存模型,包括内存的组成和分配方式,以及如何正确使用内存。
1. 内存的组成计算机的内存由一系列连续的存储单元组成,每个存储单元称为一个字节(byte)。
C语言中的数据类型在内存中占用的字节数是确定的,比如整型通常占用4个字节,字符型占用1个字节。
2. 栈与堆C语言程序的内存可以分为栈(stack)和堆(heap)两部分。
栈用于存储函数调用时的局部变量和函数调用的上下文信息,堆用于动态分配内存空间。
在函数调用时,栈会自动为函数分配一块内存空间,称为栈帧(stack frame)。
栈帧中存储了函数的局部变量、返回地址等信息。
当函数调用结束后,栈帧会被销毁,释放出的内存可以被其他函数使用。
堆是一块动态分配的内存空间,用于存储程序中需要在运行时动态分配的对象。
比如通过malloc函数分配的内存块就位于堆中。
堆上的内存需要手动释放,否则会导致内存泄漏。
3. 全局变量与静态变量全局变量和静态变量在程序运行期间都存储在静态存储区。
全局变量在程序开始执行时就会被分配内存空间,并一直存在于整个程序的执行过程中。
静态变量与全局变量类似,但其作用域限制在定义它的函数或文件内。
4. 内存分配与释放C语言提供了malloc和free函数用于在堆中动态分配和释放内存。
malloc函数接受一个字节数作为参数,返回指向分配内存的指针。
使用完动态分配的内存后,需要调用free函数释放内存,以便其他程序使用。
除了malloc和free,C语言还提供了calloc和realloc函数用于动态分配内存。
calloc函数在分配内存的同时会将内存块中的每个字节都初始化为0,而realloc函数可以在内存不足时重新分配更大的内存空间。
5. 指针与内存操作指针是C语言中重要的概念,它保存了内存地址。
沉沉-_-最近在课堂上听到老师讲了C语言中的内存分配,感觉挺深刻的,于是就写下来,当做是对自己的提醒!微机原理的知识告诉我们,内存大致可以分为三部分:代码区、堆栈区、数据段(我的计算机学的不好,按照自己的理解)。
C和C++中有全局变量,静态变量以及局部变量等几类。
全局变量和静态变量是在编译的时候就已经分配好在数据段,也就是说在执行main函数之前就已经分配好了地址,如果定义的时候认为初始化,那么就存放初始化的值,如果没有初始化,则由编译器初始化为0。
这就说明在整个程序的执行过程中全局变量和静态变量的地址和类型不会发生变化,可能变化的只是他的值。
对于动态变量,则在编译的阶段并不分配内存,而是执行到相应的代码段时再分配内存。
局部变量共有以下几种:1 int m ain(void){int a;//局部变量定义于main中;...........{int a;//局部变量定义于代码段内;}}2 void fun(){int a;//局部变量定义于普通函数内;...}3 void fun(int a,int b)//形式参数{...}编译器对局部变量的处理是放入栈中,学过微机原理知道,出栈和入栈操作的操作数都是跟着指令,存在于代码段内的,当函数执行到局部变量出,进行入栈操作,给他分配栈中的内存,供其它操作访问。
在使用结束后再出栈。
所以,局部变量的生存期是从定义的地方到该函数结束和代码段结束。
对应情况1第一个,在main函数结束时出栈,第二个在{}内的代码执行结束后出栈。
这也很好理解,函数的调用本身就涉及到对指令地址的入栈操作,调用结束后涉及到指令地址的出栈操作:模拟一下如下函数的栈活动:void fun1(int a){int b;}main(){int a;fun1();}过程:执行到main函数,对CPU IP的指针入栈:入栈1,保护现场环境(这里简化入栈操作)给局部变量a入栈分配栈内存:入栈2。
执行fun1(),对CPU IP的指针入栈:入栈3,保护现场。
C语⾔中内存分布及程序运⾏中(BSS段、数据段、代码段、堆栈)BSS段:(bss segment)通常是指⽤来存放程序中未初始化的全局变量的⼀块内存区域。
BSS是英⽂Block Started by Symbol的简称。
BSS 段属于静态内存分配。
数据段:数据段(data segment)通常是指⽤来存放程序中已初始化的全局变量的⼀块内存区域。
数据段属于静态内存分配。
代码段:代码段(code segment/text segment)通常是指⽤来存放程序执⾏代码的⼀块内存区域。
这部分区域的⼤⼩在程序运⾏前就已经确定,并且内存区域通常属于只读 , 某些架构也允许代码段为可写,即允许修改程序。
在代码段中,也有可能包含⼀些只读的常数变量,例如字符串常量等。
程序段为程序代码在内存中的映射.⼀个程序可以在内存中多有个副本.堆(heap):堆是⽤于存放进程运⾏中被动态分配的内存段,它的⼤⼩并不固定,可动态扩张或缩减。
当进程调⽤malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被剔除(堆被缩减)栈(stack) :栈⼜称堆栈,存放程序的局部变量(但不包括static声明的变量, static 意味着在数据段中存放变量)。
除此以外,在函数被调⽤时,栈⽤来传递参数和返回值。
由于栈的先进先出特点,所以栈特别⽅便⽤来保存/恢复调⽤现场。
储动态内存分配,需要程序员⼿⼯分配,⼿⼯释放下图是APUE中的⼀个典型C内存空间分布图例如:#include <stdio.h>int g1=0, g2=0, g3=0;int max(int i){int m1=0,m2,m3=0,*p_max;static n1_max=0,n2_max,n3_max=0;p_max = (int*)malloc(10);printf("打印max程序地址\n");printf("in max: 0x%08x\n\n",max);printf("打印max传⼊参数地址\n");printf("in max: 0x%08x\n\n",&i);printf("打印max函数中静态变量地址\n");printf("0x%08x\n",&n1_max); //打印各本地变量的内存地址printf("0x%08x\n",&n2_max);printf("0x%08x\n\n",&n3_max);printf("打印max函数中局部变量地址\n");printf("0x%08x\n",&m1); //打印各本地变量的内存地址printf("0x%08x\n",&m2);printf("0x%08x\n\n",&m3);printf("打印max函数中malloc分配地址\n");printf("0x%08x\n\n",p_max); //打印各本地变量的内存地址if(i) return 1;else return 0;}int main(int argc, char **argv){static int s1=0, s2, s3=0;int v1=0, v2, v3=0;int *p;p = (int*)malloc(10);printf("打印各全局变量(已初始化)的内存地址\n");printf("0x%08x\n",&g1); //打印各全局变量的内存地址printf("0x%08x\n",&g2);printf("0x%08x\n\n",&g3);printf("======================\n");printf("打印程序初始程序main地址\n");printf("main: 0x%08x\n\n", main);printf("打印主参地址\n");printf("argv: 0x%08x\n\n",argv);printf("打印各静态变量的内存地址\n");printf("0x%08x\n",&s1); //打印各静态变量的内存地址printf("0x%08x\n",&s2);printf("0x%08x\n\n",&s3);printf("打印各局部变量的内存地址\n");printf("0x%08x\n",&v1); //打印各本地变量的内存地址printf("0x%08x\n",&v2);printf("0x%08x\n\n",&v3);printf("打印malloc分配的堆地址\n");printf("malloc: 0x%08x\n\n",p);printf("======================\n");max(v1);printf("======================\n");printf("打印⼦函数起始地址\n");printf("max: 0x%08x\n\n",max);return 0;}打印结果:可以⼤致查看整个程序在内存中的分配情况:可以看出,传⼊的参数,局部变量,都是在栈顶分布,随着⼦函数的增多⽽向下增长.函数的调⽤地址(函数运⾏代码),全局变量,静态变量都是在分配内存的低部存在,⽽malloc分配的堆则存在于这些内存之上,并向上⽣长.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~在操作系统中,⼀个进程就是处于执⾏期的程序(当然包括系统资源),实际上正在执⾏的程序代码的活标本。
c/c++程序内存空间浅说
一个由C/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack):又编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构的栈。
2、堆区(heap):一般是由程序员分配释放,若程序员不释放的话,程序结束时可能由OS回收,值得注意的是他与数据结构的堆是两回事,分配方式倒是类似于数据结构的链表。
3、全局区(static):也叫静态数据内存空间,存储全局变量和静态变量,全局变量和静态变量的存储是放一块的,初始化的全局变量和静态变量放一块区域,没有初始化的在相邻的另一块区域,程序结束后由系统释放。
4、文字常量区:常量字符串就是放在这里,程序结束后由系统释放。
5、程序代码区:存放函数体的二进制代码。
下图便是unix系统一个进程的内存占用示意图
从图中可以看出:
1.从低地址到高地址分别为:代码段、(初始化)数据段、(未初始化)数据段(BSS)、堆、栈、命令行参数和环境变量
2.堆向高内存地址生长
3.栈向低内存地址生长
在实际编程中会遇到的与内存空间相关的问题
1.内存申请
为了解决数据存储的问题,我们有3种办法申请空间并使用它们
第一,从栈空间中申请(即直接定义数组)
第二,从堆空间中申请(使用malloc或者new动态申请内存)
第三,使用文件存储数据
我们主要讨论前两个方案
1、申请后系统的响应:
栈:只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请的空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的delete或free语句就能够正确的释放本内存空间。
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会将多余的那部分重新放入空闲链表中。
2、申请的大小限制不同:
栈:在windows下,栈是向低地址扩展的数据结构,是一块连续的内存区域,栈顶的地址和栈的最大容量是系统预先规定好的,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域,这是由于系统是由链表在存储空闲内存地址,自然堆就是不连续的内存区域,且链表的遍历也是从低地址向高地址遍历的,堆得大小受限于计算机系统的有效虚拟内存空间,由此空间,堆获得的空间比较灵活,也比较大。
3、申请的效率不同:
栈:栈由系统自动分配,速度快,但是程序员无法控制。
堆:堆是有程序员自己分配,速度较慢,容易产生碎片,不过用起来方便。
4、堆和栈的存储内容不同:
栈:在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令的地址,然后是函数的各个参数,在大多数的C编译器中,参数是从右往左入栈的,
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令。
堆:一般是在堆得头部用一个字节存放堆得大小,具体内容由程序员安排。
总结
数据量较小时,推荐使用栈空间申请,即直接定义数组
数据量稍大或者不确定时,推荐使用堆空间内存,即使用malloc或者new 动态申请,因为栈空间常常会有大小的限定,当栈空间耗尽时,栈溢出会导致程序崩溃
当数据量超大的,建议重新审阅算法或者使用文件存储
栈空间与子函数,递归与栈溢出
当一个子函数被调用时,子函数的数据及代码都会被装入栈中,因为栈空间通常会有大小限制,如果子函数太多时,就会有栈溢出的风险。
所以当程序员考虑使用递归函数解决问题时,应当考虑到栈溢出的风险。
建议学会使用将递归函数写成非递归函数的方法。