内存管理

作用域

一个C语言变量的作用域可以使代码块作用域,函数作用域或者文件作用域

代码块是 {    } 之间的一段代码。

出现在 {    } 之外的变量,就是全局变量。

代码块作用域的静态变量

静态变量是指内存中在程序执行期间一直不改变的变量,一个代码块内部的静态变量只能被这个代码块内部访问。

代码块作用域外的静态变量

代码块之外的静态变量在程序执行期间一直存在,但只能被定义这个变量的文件访问,不能跨文件访问。

全局变量

全局变量的存储方式和静态变量相同,但可以被多个文件访问。

全局函数和静态函数

使用static关键字修饰可将函数声明为静态。

内存四区

计算机中有内存,我们的程序是在内存中执行的,在内存中程序与程序之间不能互相访问,对于某一个程序它可以映射出四个区块来存放这段程序,有代码区(程序中所有可执行代码),静态区(所有的静态变量和全局变量),堆区,栈区。

                              

#include <iostream>
using namespace std;

int a = 0;//静态区
static int b = 0;//静态区
int main()//代码区
{
    int c = 0;//栈区
    int d = 0;//栈区
    static int e = 0;//静态区
    printf("%d,%d,%d,%d,%d,%d", &a, &b, &c, &d, &e, main);
    //13083748,13083752,7338828,7338816,13083756,13046455
    system("pause");
    return 0;
}

代码区

程序代码指令、常量字符串、只可读(静态)

程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的。

静态区

存放全局变量/常量、静态变量/常量

所有的全局变量以及程序中的静态变量都存储到静态区

栈区

预先设定大小,自动分配与释放(动态)

栈stack是一种先进后出的内存结构,所有的变量(auto),函数形参都是由编译器自动放到栈中,当一个变量(auto)超出其作用域时,自动从栈中弹出。对于变量的出栈,入栈不需要程序控制,由编译器实现。

int main()
{
    int a = 0;
    int b = 0;
    return 0;
}

上面的代码如下图

                              

注意:在程序中避免出现返回一个栈变量的地址。可以返回一个堆地址,但一定要释放free。

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int *GetA()//避免这种操作
{
    int a = 100;
    return &a;
}//a的作用域

int main(int argc, char** argv)
{
    int *p = GetA();
    //得到了一个临时栈变量的地址,这个地址在函数GetA调用完之后已经无效了
    *p = 10;
    printf("%d", *p);//存在二义性
    return 0;
}

可以这样写

int *GetA()
{
    int *p = (int *)malloc(sizeof(int));
    return p;
}

int main(int argc, char** argv)
{
    int *p = GetA();
    *p = 100;
    printf("%d\n", *p);
    free(p);

    printf("hello.. \n");
    system("pause");
    return 0;
}

栈溢出

当栈空间已满时,还往栈内存中压入变量,这个就叫栈溢出。

对于一个32位操作系统,最大管理管理4G内存,其中1G是给操作系统自己用的,剩下的3G都是给用户程序,一个用户程序理论上可以使用3G的内存空间。

堆区

由程序员控制,使用malloc/new,free/delete来操作(动态)

堆heap和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。堆是一个大容器,它的容量要远远大于栈,但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。

堆的分配和释放

malloc

void * malloc(size_t _Size);

malloc函数在堆中分配参数_Size指定大小的内存,单位:字节,函数返回void *指针。

free

void free(void *p);

free负责在堆中释放malloc分配的内存。参数p为malloc返回的堆中的内存地址。

void PrintArray(int *p,int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("p[%d] = %d\n", i, p[i]);
    }
}

int main(int argc, char** argv)
{
    //栈数组
    int array[10] = { 0 };//代码执行完毕会自动释放

    //堆数组
    int *p = (int *)malloc(sizeof(int) * 10);//需要手动释放
    memset(p, 0, sizeof(int) * 10);

    for (int i = 0; i < 10; i++)
    {
        p[i] = i;
    }
    PrintArray(p, 10);

    char *p1 = (char *)malloc(sizeof(char) * 10);
    memset(p1, 0, sizeof(char) * 10);

    free(p);
    free(p1);

    printf("hello.. \n");
    system("pause");
    return 0;
}

了解C++中的内存分配与释放请移步到:对象的动态建立和释放(new&delete)

 分析一下经常容易混淆的模型

如果是静态变量呢?是可以执行的,因为静态成员变量在程序中永远有效。

int *GetB()//可以,因为静态变量在程序中永远有效
{
    static int a = 0;
    return &a;
}

int main(int argc, char** argv)
{
    int *p1 = GetB();
    *p1 = 200;
    printf("%d\n", *p1);

    printf("hello.. \n");
    system("pause");
    return 0;
}

如果是一下模型呢?

int GetA(int *p)//不可行
{
    p = (int *)malloc(sizeof(int)*10);
}

int GetB(int **p)//可行
{
    *p = (int *)malloc(sizeof(int)*10);
}

int main(int argc, char** argv)
{
    int *p = NULL;
    printf("%d\n", &p);
    //GetA(p);//实参没有任何改变
    GetB(&p);//引用传递,得到了堆内存地址
    p[0] = 1;
    p[1] = 2;
    printf("%d\n", p);
    printf("p[0] = %d,p[1] = %d\n", p[0], p[1]);
    free(p);

    printf("hello.. \n");
    system("pause");
    return 0;
}

模型分析

int GetA(int *p)

这里的形参int*p属于栈区,当GetA程序执行完时生命周期也就结束了,p就会消失。而在代码块中分配的内存在堆区,p的消失导致它指向的具体堆空间的地址编号也随之消失了。造成了内存泄漏,这块内存无法释放。在main代码中free(p)没有任何意义,因为p自始至终没有分配内存。

                              

int GetB(int **p)

首先理解形参*p访问的是main中的p,形参**p访问的是main中的*p,前者指向的是其他变量的地址,后者指向的是变量*p的地址。在执行GetB(&p)时得到了堆内存的地址,GetB代码块中最关键的一步是*p = malloc,这里的*p所指的是main中p的地址,执行完成之后,main中的p就得到了堆内存地址,从而可以进行赋值和释放。

                               

 

memset

将一块内存初试化,最常见的方法

memset(void *_Dst,int _Val,size_t_Size);
//第一个参数是要设置的内存地址,第二个是要设置的值,第三个是初始化的内存大小

//main
int array[100] ={1,54,21,78,32,326,526,66,55};
memset(array,0,sizeof(array));
//将一块内存初试化为0,最常见的方法

memcpy

内存拷贝

///main
int buf1[10] = {1,2,3,4,5,6,7,8,9,10};
int buf2;

memcpy(buf2,buf1,sizeof(buf1));
//将buf1的内存内容全部拷贝到buf2,拷贝大小sizeof(buf1)
//在使用内存拷贝时,一定要确保内存没有重叠区域

memmove

内存移动

main
int buf1[10] = {1,2,3,4,5,6,7,8,9,10};
int buf2[10];

memmove(buf2,buf1,sizeof(buf1));
//将buf1的内存内容全部移动到buf2,移动需要的大小为sizeof(buf1)

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值