C语言-入门-内存管理(二十)

x33g5p2x  于2022-08-17 转载在 其他  
字(3.1k)|赞(0)|评价(0)|浏览(344)

什么是进程

程序,是经源码编译后的可执行文件,可执行文件可以多次被执行,比如我们可以多次打开 office。而进程,是程序加载到内存后开始执行,至执行结束,这样一段时间概念,多次打开的wps,每打开一次都是一个进程,当我们每关闭一个 office,则表示该进程结束。

内存空间布局

有了进程和程序的概念以后,我们再来看一下,程序被加载到内存以后内存空间布局是什么样的

总体来讲说,程序源代码被编译之后主要分成两种段:程序指令和程序数据。代码区属于存放程序指令,常量区、全局数据区、堆区、栈区属于存放程序数据。程序代码区、常量区、全局数据区在程序加载到内存后就分配好了,并且在程序运行期间一直存在,大小固定,只能等到程序运行结束后由操作系统收回。栈区、堆区在程序运行时动态开辟。堆需要手动释放

栈内存(Stack)

  • 栈中存放任意类型的变量,即自动类型的局部变量, 随用随开,用完即消。
  • 内存的分配和销毁系统自动完成,不需要人工干预
  • 栈的最大尺寸固定,超出则引起栈溢出(局部变量过多,过大 或 递归层数太多等就会导致栈溢出)
int ages[10240*10240]; // 程序会崩溃, 栈溢出

堆内存(Heap)

  • 堆内存可以存放任意类型的数据,但需要自己申请与释放
  • 大小,想像中的无穷大,但实际使用中,受限于实际内存的大小和内存是否连续性

malloc和free函数

通过malloc申请的存储空间一定要释放, 所以malloc和free函数总是成对出现
malloc函数

free函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    // 1.申请1块4字节的存储空间
    int *p = (int *)malloc(sizeof(int));
    // 2.初始化4个字节存储空间为0
    memset(p, 0, sizeof(int));
    // 3.释放申请的存储空间
    free(p);
    return 0;
}

free()到底释放了什么? free释放的是指针指向的内存!注意!释放的是内存,不是指针!这点非常非常重要!指针是一个变量,只有程序结束时才被销毁。free释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。因此释放内存后把指针指向NULL,防止指针在后面不小心又被解引用了,导致未知的问题发生。非常重要啊这一点!

在上面我们还用到一个函数memset(指针,0,数据大小) 这个函数可以说是初始化内存的“万能函数”,通常为新申请的内存进行批量初始化工作。它是直接操作内存空间,而且通常是给数组或结构体进行批量初始化。一般的变量如char、int、float、double 等类型的变量直接*p=0初始化即可,没有必要用 memset。如果用 memset 的话反而显得麻烦

calloc函数

和malloc一样,但可以初始化内存空间为0值

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
    // 1.申请4块,4个字节的存储空间
    int *p = calloc(4, sizeof(int));
    printf("%d",*p);
    // 3.释放申请的存储空间
    free(p);
    return 0;
}

realloc函数

基于现有空间进行扩容或者缩小,利用这一特性可以完成数组动态扩容

注意事项:

  • 若参数ptr==NULL,则该函数等同于 malloc
  • 返回的指针,可能与 ptr 的值相同,也有可能不同。若相同,则说明在原空间后面申请,否则,则可能后续空间不足,重新申请的新的连续空间,原数据拷贝到新空间, 原有空间自动释放
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    // 1.申请4个字节存储空间
    int *p = NULL;
    p = realloc(p, sizeof(int)); // 此时等同于malloc
    // 2.使用申请好的空间
    *p = 666;
    printf("*p = %i\n",  *p);
    // 3.释放空间
    free(p);

    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    // 1.申请4个字节存储空间
    int *p = malloc(sizeof(int));
    printf("p = %p\n", p);
    // 如果能在传入存储空间地址后面扩容, 返回传入存储空间地址
    // 如果不能在传入存储空间地址后面扩容, 返回一个新的存储空间地址
    p = realloc(p, sizeof(int) * 2);
    printf("p = %p\n", p);
    // 2.使用申请好的空间
    *p = 666;
    printf("*p = %i\n",  *p);
    // 3.释放空间
    free(p);

    return 0;
}

动态分配内存

编程时,如果您预先知道数组的大小,那么定义数组时就比较容易。例如,一个存储人名的数组,它最多容纳 100 个字符,所以您可以定义数组,如: char name[100];

但是,如果您预先不知道需要存储的文本长度,例如您想存储有关一个主题的详细描述。在这里,我们需要定义一个指针,该指针指向未定义所需内存大小的字符,后续再根据需求来分配内存,如下所示:

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

int main(int argc, char *argv[]) {
    char *description;
    /* 分配30字节的内存 */
    description = (char *) malloc(30 * sizeof(char));
    if (description == NULL) {
        fprintf(stderr, "Error - unable to allocate required memory\n");
    } else {
        strcpy(description, "Zara ali a DPS student.");
    }
    /* 假设您想要存储更大的描述信息,那么我们将description内存大小进行动态调整为100字节大小 */
    description = (char *) realloc(description, 100 * sizeof(char));
    if (description == NULL) {
        fprintf(stderr, "Error - unable to allocate required memory\n");
    } else {
        //拼接字符串
        strcat(description, "She is in class 10th");
    }

    printf("Description: %s\n", description);

    /* 使用 free() 函数释放内存 */
    free(description);
    return 0;
}

您可以尝试一下不重新分配额外的内存,strcat() 函数会生成一个错误,因为存储 description 时可用的内存不足。

如果想看更高级的用法可以看我博客里这篇文章C语言-数据结构-单向链表

相关文章