当前位置:   article > 正文

C语言——内存管理_c语言内存管理

c语言内存管理

C语言目录:

1. 概述

2. 数据类型

3. 量

4. 运算符

5. 流程控制

6. 函数

7. C程序编译过程

8. 文件

9. 内存管理


9.1 进程空间

程序(静态):经编译后的可执行文件可以被多次执行

进程(动态):程序在内存中的运行状态为进程

在这里插入图片描述

9.1.1 栈内存(stack)

栈中存放任意类型变量,但必须是 auto 类型修饰符的局部变量

随用随开,用完即销

内存的分配和销毁由系统自动完成,不需要人工干预

  • 栈内存的分配:从高地址到低地址,为变量分配的内容由 数据类型空间首地址 标记

栈有最大存储空间限制,超出则引起栈溢出

  • 如局部变量过多,过大或递归层数太多
#include <stdio.h>

int main(){
    int a = 1;
    int b = 2;
    
    printf("&a = %p\n", &a); // &a = 0ABC0FFC
    printf("&b = %p\n", &b); // &b = 0ABC0FF8

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

9.1.2 堆内存(heap)

堆内存可以存放任意类型的数据,但需要自己申请与释放

int *p = (int *)malloc(sizeof(int)*1024*1024);
//不一定会崩溃
  • 1
  • 2

连续两次申请的内存可能不连续,但一定是从小地址开始分配内存

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

int main(){
    int *p1 = (int *)malloc(4);
    *p1 = 1;
    int *p2 = (int *)malloc(4);
    *p2 = 2;
   
    printf("p1 = %p\n", p1); //p1 = 00000227639268C0
    printf("p2 = %p\n", p2); //p2 = 0000022763926900

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

9.2 变量内存分析

9.2.1 变量存储过程

一个变量所占用的存储空间,和 定义变量时声明的类型 以及 编译环境 有关

类型32位编译器64位编译器
char11
int44
float44
double88
short22
long48
long long88
void*48
a. 变量存储的过程
  • 根据定义变量时声明的 变量类型 和当前 编译环境 确定需要开辟多大存储空间
  • 在内存中开辟一块存储空间,开辟时从内存地址大的开始开辟,将最小的地址返回给变量
  • 将数据保存到已经开辟好的内存空间内

变量名:变量的首地址,是变量所占存储空间字节地址(最小的那个地址 )

小端模式:数据的高字节高地址

9.2.2 内存读写操作

在这里插入图片描述

CPU在运行时要明确三件事:

  • 存储单元的地址(地址信息)

    通过地址总线找到存储单元的地址

  • 对部件的操作(读或写,控制信息)

    通过控制总线发出内存读写指令

  • 数据(数据信息)

    通过数据总线传输需要读写的数据

a. 写入过程
  • CPU 通过地址线将找到地址为 FFFFFFFC 的内存空间
  • CPU 通过控制线发出内存写入命令,选中存储器芯片,并通知它,要其写入数据
  • CPU 通过数据线将数据 12 送入内存 FFFFFFFC 单元中

在这里插入图片描述

b. 读入过程
  • CPU 通过地址线将找到地址为 FFFF FFFC 的内存
  • CPU 通过控制线发出内存读取命令,选中存储器芯片,并通知它,将要从中读取数据
  • 存储器将 FFFFFFFC 号单元中的数据 12 通过数据线送入 CPU寄存器中

在这里插入图片描述

9.3 数据存储区

执行C语言程序,可用的存储区有

  1. 程序区

  2. 静态存储区:编译时分配内存;包括 static,extern

  3. 动态存储区:运行时分配内存;包括 static,auto

9.3.1 static

静态局部变量

  • 仅在本函数中使用,调用后不清零
  • 只能赋一次初值,若不初始化 自动赋0或空字符
  • 可使用静态局部变量跟踪调用次数

静态全局变量

  • 仅限在定义的文件中使用,可使不同源文件中的静态全局变量独立

9.3.2 extern

在函数外部引用变量使用 extern ,扩充已定义的全局变量作用域

一个C语言程序包含多个源程序,若使用同一外部变量,在一个源文件中定义,在另一个中用 extern 说明是外部变量。引用外部函数时,被引用函数需添加 extern

9.4 动态分配内存

stdlib.h

9.4.1 malloc

void *malloc(size_t size); :在内存中申请一块连续的堆内存空间并返回,所申请的空间并未初始化

  • size :表示要申请的字符数

  • 返回值:

    成功,返回非空指针,指向申请的空间

    失败,返回 NULL

初始化方法:memset(指针, 初始值, 待初始化字节数) 字节初始化

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

int main(){
    int *p = (int *)malloc(sizeof(int));
    printf("p = %d\n", *p); // 垃圾值
    
    memset(p, 0, sizeof(int)); // 初始化为0
    printf("p = %d\n", *p);
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

9.4.2 free

void free(void *p); : 释放通过 malloc() 申请的堆内存空间,所以 malloc()free() 总是成对出现

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

int main(){
    int *p = (int *)malloc(sizeof(int));
    printf("p = %d\n", *p); // 垃圾值
    
    memset(p, 0, sizeof(int)); // 初始化为0
    printf("p = %d\n", *p);
    
    free(p);
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

9.4.3 calloc

void *calloc(size_t n,size_t size); :在堆内存中申请 n 块长度为 size 的空间,申请的空间自动初始化为0

  • 成功,返回非空指针指向申请的空间
  • 失败,返回 NULL
#include <stdio.h>
#include <stdlib.h>

int main(){
    // 申请3块4个字节的存储空间
    int *p = calloc(3, sizeof(int));
   
    p[0] = 1;
    p[1] = 3;
    p[2] = 5;
    printf("p[0] = %d\n", p[0]);
    printf("p[1] = %d\n", p[1]);
    printf("p[2] = %d\n", p[2]);
    
    free(p);

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

9.4.4 realloc

void *realloc(void *p,size_t size); :扩充或缩小原有内存的大小

  • 参数

    p :表示待扩容的空间指针

    size :表示扩容后内存的大小

  • 返回

    成功:返回非空指针指向申请的空间

    失败:返回 NULL

注意:

  • p == NULL 时,realloc(NULL,sizeof(int)) 等同于 malloc(sizeof(int))

  • 返回的指针,可能与 p 原本的值相同,也可能不同

    相同:则原空间后后续空间充足

    不同:原空间后续内存空间不足,重新申请新的连续空间后,将元数据拷贝到新空间,原有空间会被自动释放

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

int main(){
   // 申请4个字节存储空间
   int *p = malloc(sizeof(int));
   printf("p = %p\n", p);
   
   //扩容
   p = realloc(p, sizeof(int) * 2);
   printf("p = %p\n", p);
   
   //使用申请好的空间
   *p = 666;
   printf("*p = %d\n",  *p);
   // 释放空间
   free(p);

   return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

9.4.5 应用——动态链表

a. 链表与静态链表

链表:将零碎的内存空间组织为一组可用的内存空间

  • 使用 malloc 申请内存空间时,若没有足够大的连续内存空间,则申请失败

在这里插入图片描述

静态链表:链表的长度固定,链表的结点固定

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

//链表结点
typedef struct node{
    int data;
    struct node *next;
}Node;

int main(){
    Node a,b,c;
    
    Node *head = &a;
    a.data = 1;
    b.data = 2;
    c.data = 3;
    a.next = &b;
    b.next = &c;
    c.next = NULL;
    
    while(head != NULL){
        int cur = head->data;
        printf("%d\n",cur);
        head = head->next;
    }
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在这里插入图片描述

空链表

头指针带了一个空链表结点,空链表结点中的 next 指向 NULL

在这里插入图片描述

静态链表数据存储在栈上,而栈的存储空间有限。所以要实现链表存储空间的动态分配,需要申请堆里的存储空间

b. 动态链表的创建
头插法

涉及数据结构的线性表,可自行查看:https://auspicetian.life/posts/3975030669/

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

//定义结点
typedef struct node {
    int data;
    struct node *next;
}Node;

//创建链表
Node *createList();
//按顺序打印链表中数据
void printNodeList();

int main(){
    Node *head = createList();
    printNodeList();
    
    return 0;
}

Node *createList(){
    //创建头结点
    Node *head = (Node *)malloc(sizeof(Node));
    if(head == NULL)
        return Null;
    head->next = NULL;
    
    //从键盘接收数据
    //-1表示输入结束
    printf("请逆序输入结点数据:\n");
    int data;
    scanf("%d",&data);
    
    while(data != -1){
        //创建新结点
        Node *cur = (Node *)malloc(sizeof(Node));
        cur->data = data;
        cur->next = head->next;
        head->next = cur;
        
        printf("请逆序输入结点数据:\n");
        scanf("%d",&data);
    }
    
    return head;
}

void printNodeList(Node *node){
    Node *head = node->next;
    
    while(head != NULL){
        int data = head->data;
        printf("%d\n",data);
        head = head->next;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
尾插法
# include<stdio.h>
# include<stdlib.h>

//定义结点
typedef struct node {
    int data;
    struct node *next;
}Node;

//创建链表
Node *createList();
//按顺序打印链表中数据
void printNodeList();

int main(){
    Node *head = createList();
    printNodeList();
    
    return 0;
}

Node *createList(){
    Node *head = (Node *)malloc(sizeof(Node));
    if(head == NULL)
        return NULL;
    head->next = NULL;
    
    //从键盘接收数据
    //-1表示输入结束
    printf("请顺序输入结点数据:\n");
    int data;
    scanf("%d",&data);
    
    Node *pre = head;
    while(data != -1){
        Node *cur = (Node *)malloc(sizeof(Node));
        cur->data = data;
        pre->next = cur;
        cur->next = NULL;
        
        pre = cur;
        scanf("%d",&data);
        
        return head;
    }
}

void printNodeList(Node *node){
    Node *head = node->next;
    
    while(head != NULL){
        int data = head->data;
        printf("%d\n",data);
        head = head->next;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
c. 插入
void preInsertNode(Node *head,int data){
    Node *cur = (Node *)malloc(sizeof(Node));
    cur->data = data;
    
    cur->next = head->next;
    head->next = cur;
}

void tailInsertNode(Node *head,int data){
    Node *pre = head;
    
    while(pre != NULL && pre->next != NULL)
        pre = pre->next;
    
    Node *cur = (Node *)malloc(sizeof(Node));
    cur->data = data;
    cur->next = NULL;
    
    pre->next = cur;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
d. 销毁
void destoryList(Node *head){
    Node *cur = NULL;
    
    while(head != NULL){
        cur = head->next;
        free(head);
        
        head = cur;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/592186
推荐阅读
相关标签
  

闽ICP备14008679号