Linux C语言 18-内存管理

Linux C语言 18-内存管理

本节关键字:Linux、C语言、内存管理、C语言五大区、
相关C库函数:malloc、free、memset、memcpy、sizeof

什么是内存管理?

大白话,需要使用的内存保留,不需要使用的内存就释放。
在计算机系统中,尤其是嵌入式系统,内存资源是非常有限的。硬件资源的限制使得“如何有效地管理内存资源”称为程序员在程序设计中需要考虑的首要问题。

C语言五大区

计算机中的内存是分区来管理的,程序和程序之间的内存是独立的,不能互相访问。每个程序的内存也是分区管理的,一个应用程序所占的内存可以分为很多个区域,我们需要了解的主要有四个区域,通常叫内存四区。在C语言中,细分为C语言五大区:

区域名称说明
栈区(stack)由编译器自动分配释放,存放函数的形参,局部变量的值等,涉及到一个栈溢出问题;
堆区(heap)一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,注意它与数据结构中的堆是两回事,分配方式倒是类似于链表;
全局区(静态区)(static)全局变量和静态变量,程序结束后由系统释放;
文字常量区(只读)常量字符串就是放在这里的,程序结束后由系统释放;
程序代码区存放函数体的二进制代码,内存由系统管理。
堆和栈的区别
  • 栈是从高地址想高地址分配内存
  • 堆是从低地址想高地址分配内存
#include <stdio.h>
#include <stdlib.h>

int main()
{
char *ptr1 = NULL;
char *ptr2 = NULL;

ptr1 = (char *)malloc(sizeof(char));
ptr2 = (char *)malloc(sizeof(char));

// 定义指针ptr1和ptr2是从栈中分配,指向的内存地址是从堆中申请的
printf("statck: %p -> %p\n", &ptr1, &ptr2);    // 指针的地址
printf("heap: %p -> %p\n", ptr1, ptr2);    // 开辟内存返回的地址

return 0;
}

/** 运行结果:
statck: 0x7ffe576aff78 -> 0x7ffe576aff70
heap: 0x109f010 -> 0x109f030
*/
栈溢出问题

话不多说,直接来先看代码:

#include <stdio.h>

int main()
{
  char arr[1024 * 1024 * 1024] = {0};

  arr[0] = 'a';
  printf("%s\n", arr);
  getchar();

  return 0;
}

/** 运行结果:
段错误(吐核)
*/

栈不会很大,一般都是以K为单位。如果在程序中直接将较大的数组保存在函数内的栈变量中,很可能会内存溢出,导致程序崩溃(如下实验三),严格来说应该叫栈溢出(当栈空间以满,但还往栈内存压变量,这个就叫栈溢出)。

堆区耗尽问题

如果一直在堆区开辟内存空间而不进行释放,会严重影响计算机的性能。

相关函数

#include <stdlib.h>

// 在堆上申请一段内存,成功返回开辟的内存地址,内存未初始化,如果大小为0或失败返回NULL
void *malloc(size_t size);

// 释放ptr指向的内存空间,如果ptr为NULL或者不是malloc、calloc和realloc的返回值时,会发生意料之外的行为
void free(void *ptr);

// 返回NULL或者内存地址,在内存中动态的申请nmemeb个长度为size的连续内存,这些内存空间全部被初始化为0,如果nmemb或size为0则返回NULL
void *calloc(size_t nmemb, size_t size);

// 返回NULL或者内存地址,为ptr重新开辟size大小的内存空间,原内容保持不变;如果失败,原始块保持不变
// 第一步:优先在原空间地址后申请连续的空间,若成功就成功返回,若不成功进行第二步;
// 第二步:realloc调用malloc重新开辟一块内存空间,若成功就将原内存中的数据拷贝到新内存中并释放掉原来的内存空间,返回一个新的内存首地址;若失败返回NULL表示realloc执行失败,原内存数据不动。
// 注意:使用原来的指针变量去接收realloc的返回值可能会造成内存泄漏!
void *realloc(void *ptr, size_t size);

// 内存清理,用常量c填充s指向的内存空间的前n个字节,返回指向内存区域s的指针
#include <string.h>
void *memset(void *s, int c, size_t n);

// 内存拷贝,将n个字节从内存区域src复制到内存区域dest,内存区域不得重叠;返回指向dest的指针
void *memcpy(void *dest, const void *src, size_t n);

相关库函数使用示例

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

int main()
{
    char *ptr1 = NULL;
    char *ptr2 = NULL;

    ptr1 = (char *)malloc(sizeof(char));
    ptr2 = (char *)malloc(sizeof(char));

    // 定义指针ptr1和ptr2是从栈中分配,指向的内存地址是从堆中申请的
    printf("statck: %p -> %p\n", &ptr1, &ptr2);    // 指针的地址
    printf("heap: %p -> %p\n", ptr1, ptr2);    // 开辟内存返回的地址
    free(ptr1);
    free(ptr2);
    printf("\n\n\n");
    
    char *pchar = NULL;
    char *tmp, *pcopy = NULL;
    int mLen, *pint = NULL;
    
    pchar  = (char*)malloc(sizeof(char));
    if (!pchar)
        return -1;
    
    printf("pchar=%p, *pchar=%d\n", pchar, *pchar);
    *pchar = 100;
    
    pint = (int*)realloc(pchar, sizeof(int));
    if (!pint)
    {
        free(pchar);
        return -1;
    }
    
    printf("pchar=%p, *pchar=%d\n", pchar, *pchar);
    printf("pint=%p, *pint=%d\n", pint, *pint);
    
    mLen = sizeof(char) * 100;
    tmp = (char*)realloc(pchar, mLen);
    if (!tmp)
    {
        printf("failed to realloc\n");
    }
    else
    {
        pcopy = (char*)malloc(mLen);
        if (!pcopy)
            printf("failed to malloc\n");
    
        memset(pchar, 0, mLen);
        memset(pcopy, 0, mLen);
        sprintf(pchar, "this is \"pchar\"       will cpoy to \"pcopy\"\n");
        memcpy(pcopy, pchar, mLen);
        
        printf("pchar: %s", pchar);
        printf("pcopy: %s", pcopy);
        free(pcopy);
    }
    free(pchar);
    
    return 0;
}
/** 运行结果:
statck: 0x7ffdb82c2480 -> 0x7ffdb82c2478
heap: 0x10af010 -> 0x10af030
pchar=0x10af030, *pchar=0
pchar=0x10af030, *pchar=100
pint=0x10af030, *pint=17494116
pchar: this is "pchar"       will cpoy to "pcopy"
pcopy: this is "pchar"       will cpoy to "pcopy"
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值