c语言内存管理(待完善)

内容概要

  一、动态内存管理

  二、c语言内存布局

  三、思考题

 

1、动态内存管理

  c语言中的变量是要事先定义好才能使用,在程序执行过程中动态创建是不允许的。但是在C99后已经实现了变量动态创建

    如果想要动态创建变量,可以使用库stdlib.h下提供的功能

      -malloc(参数一)

        参数一:要开辟多少字节的内存

        功能:创建一个动态的内存,这个内存位于堆中

        返回值:返回一个指向这个内存位置的void类型指针

复制代码
#include <stdio.h>
#include <stdlib.h>

int main(void){
    int *pa;
    pa = (int *)malloc(sizeof(int));  //创建一个int类型大小的内存空间
    *pa = 120;
    
    printf("%d   %p\n", *pa, pa);
    free(pa);  //释放pa指向的内存,这个内存应该有malloc,calloc或者realloc开辟的内存(存放在堆中)
    return 0;
}
复制代码

        需要注意的是malloc开辟的空间存放在堆区,这部分内存不会在函数调用结束之后释放,需要程序员指定才会释放

        像是在python使用open()函数之后使用close()向操作系统发出回收文件资源的命令

复制代码
#include <stdio.h>
#include <stdlib.h>

int main(){
    int *pa;
    while (1){
        pa = (int *)malloc(sizeof(int));
    }
    
    return 0;
}
复制代码

        把内存搞炸

复制代码
#include <stdio.h>
#include <stdlib.h>

int main(){
    int *pa;
    while (1){
        pa = (int *)malloc(sizeof(int));
        free(pa);
    }
    
    return 0;
}
复制代码

        炸不了了

 

        使用malloc也可能调用失败,只是函数会返回NULL指针

 

    像操作数组那样操作返回的指针

复制代码
#include <stdio.h>
#include <stdlib.h>

int main(){
    
    int n;
    int *pa;
    // 动态创建
    printf("please input how long you want:");
    scanf("%d",&n);
    pa = (int *)malloc(n * sizeof(int));

    // 像数组一样遍历,发现是随机值
    for (int i = 0; i < n; i++){
        printf("%d\n",pa[i]);
    }
    
    // 回收资源
    free(pa);
    return 0;
}
复制代码

    为"数组"初始化,利用memset函数

        要使用memset函数,需要导入库string.h

        -memset(参数一,参数二,参数三)

          参数一:动态内存指针

          参数二:初始化值

          参数三:动态内存大小

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    int *pa;
    pa = (int *)malloc(8,sizeof(int));
    // 初始化
    memset(pa, 0 ,8 * sizeof(int));
    
    for (int i = 0; i < 8; i++){
        printf("%d\n",pa[i]);
    }
    return 0;
}
复制代码

 

    切记要使用指针关联动态开辟的内存

复制代码
#include <stdio.h>
#include <stdlib.h>

int main(){
    int *pa;
    int num = 10;
    pa = (int *)malloc(sizeof(int));
    pa = &num; // 再也找不到指向那个位置的内存地址了
    
    return 0;
}
复制代码

    同时不要对局部变量使用free

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    int *pa;
    int num = 10;
    pa = (int *)calloc(8,sizeof(int));
    pa = &num;
    free(pa)
    }
    return 0;
}
复制代码

 

      -calloc(参数一,参数二)

        参数一:存放元素的个数

        参数二:每个元素的长度

        功能:在上面的例子中,会发现遍历未初始化的动态内存,会打印随机的数值。使用calloc函数可以在动态创建内存空间时设置初始值。

        返回值:void类型指针

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    int *pa;
    pa = (int *)calloc(8,sizeof(int));for (int i = 0; i < 8; i++){
        printf("%d\n",pa[i]);
    }
    return 0;
}
复制代码

        得学会看源码了,哪怕只是皮毛

        -memcpy拷贝内存空间

        -memove拷贝内存空间

        -memcmp比较内存空间

        -memchr在内存空间中查找一个字符

 

      -realloc(参数一,参数二)

        参数一:要更新的动态内存地址

        参数二:新的内存大小

        功能:更新动态内存的大小

        返回值:void类型的内存地址(这个void地址可能和传入的参数相同,也可能不同)

 

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    int *pa;
    pa = (int *)calloc(8,sizeof(int));
    printf("before: %p\n",pa);
    pa = (int *)realloc(pa,16 * sizeof(int));
    printf("after: %p\n",pa);  //两次地址应该一样
    
    free(pa);
    return 0;
}
复制代码
复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    int *pa;
    pa = (int *)calloc(8,sizeof(int));
    printf("before: %p\n",pa);
    pa = (int *)realloc(pa,1000 * sizeof(int));
    printf("after: %p\n",pa);  //两次地址应该不一样
    
    free(pa);
    return 0;
}
复制代码

    如果向realloc函数传入NULL指针,就相当于调用malloc函数

 

    一个根据用户输入创建动态"整形数组"的小练习

复制代码
#include <stdio.h>
#include <stdlib.h>

int main(){
    int num;
    int count = 0;
    int *pnum = NULL;
    
    while (1){
        printf("please input a number(input -1 end process):");
        scanf("%d",num);

        if (num == -1){
            for (int i = 0; i<count; i++){
                printf("%d\n",pnum[i]);
            }
            break;
        }
        
        count++;
        pnum = (int *)realloc(pnum,count * sizeof(int));
        pnum[count] = num;
    }
}
复制代码

 

2、c语言内存管理

  c语言不同类型变量内存地址的比较

复制代码
#include <stdio.h>
#include <stdlib.h>

int global_var_uninit;
int global_var_init = 1024;

void func(void);

void func(void){
    ;
}

int main(void){
    int local_var_uninit;
    int local_var_init = 111;
    
    static int static_var_uninit;
    static int static_var_init = 512;
    
    char *str1_before = "You are right!";
    char *str2_after = "hello world";
    
    int *pa = (int *)malloc(sizeof(int));
    
    printf("addr of local_var_uninit is: %p\n", &local_var_uninit);
    printf("addr of local_var_init is: %p\n", &local_var_init);
    printf("addr of pa is: %p\n", pa);
    printf("addr of static_var_uninit is: %p\n", &static_var_uninit);
    printf("addr of global_var_uninit is: %p\n", &global_var_uninit);
    printf("addr of static_var_init is: %p\n", &static_var_init);
    printf("addr of global_var_init is: %p\n", &global_var_init);
    printf("addr of str2_after is: %p\n", str2_after);
    printf("addr of str1_before is: %p\n", str1_before);
    printf("addr of func is: %p\n", func);
    
    return 0;
}
复制代码

 

  内存分段图解

 

 

  补充:栈的大小是固定的,一般用于存放函数执行时的形参变量和函数体内定义的变量,一般大小只有几M

  使用size命令查看每个分段大小

在ubuntu终端中输入size a.out
   text       data        bss        dec        hex    filename
   2307        624         16       2947        b83    a.out

 

  堆和栈区别

    这里所涉及得堆和栈并不是数据结构和算法中的堆和栈

      -堆:动态内存空间,在程序执行过程中动态创建的一些数据存放在这里,需要手动回收申请的空间

          在堆中创建的同类数据是不连续的,一定程度上会缩小能够存储的数据的数量,但同时

          为空间扩展带来方便

 

      -栈:用于存放函数执行时的数据,在函数调用结束之后会自动销毁

          在栈中创建的同类数据是连续的

复制代码
#include <stdio.h>
#include <stdlib.h>

int main(){
    int *pa = NULL;
    int *pb = NULL;
    
    printf("%d\n",sizeof(int *));
    
    pa = (int *)malloc(sizeof(int));
    pb = (int *)malloc(sizeof(int));    
    
    printf("pa:%p  pb:%p\n", pa, pb);
    printf("&pa:%p  &pb:%p\n", &pa, &pb);
    
    free(pa);
    free(pb);
    return 0;
}
复制代码

 

复制代码
#include <stdio.h>
#include <stdlib.h>

int main(){
    int *pa,*pb;
    
    printf("%d\n",sizeof(int *));
    
    pa = (int *)malloc(sizeof(int));
    pb = (int *)malloc(sizeof(int));    
    
    printf("pa:%p  pb:%p\n", pa, pb);
    printf("&pa:%p  &pb:%p\n", &pa, &pb);
    
    free(pa);
    free(pb);
    return 0;
}
复制代码

      堆中两个数据相隔32个字节,但我们申请的是4个字节

      栈中两个数据相隔8个字节,这正好是一个指针的长度

      **待解决**在虚拟机上无论是堆还是栈,地址大小都是向上发展的,可能我理解还是有问题

 

3、思考题

复制代码
#include <stdio.h>

int main(void){
    char a=0, b=0;
    int *pa = (int *)&b;
    
    *pa = 258;
    
    printf("%d %d\n", a, b);
    
    return 0;
}
复制代码

  ubuntu虚拟机打印的结果为什么为1,2(在windows dev-c++上为0,2。应该不用管)

    解析
      函数体代码被保存在代码段中,调用函数时,将函数参数复制到栈中

      char a ,b在栈中是连续的,char类型站据1个字节

      将b的地址(类型为字符指针)强转为整形指针,pa解引用时会读取4个字节而非1个字节

      *pa解引用赋值,258的二进制值为 100000010 共9个比特位

      其中后8个比特位00000010存放于b对应的内存中

      前一个比特位1存放于a对应的内存

      打印时a的值就为1,而b的值就为2


  验证

    如果*pa = 528;

    528的二进制值为1000010000

    分开后为10和00010000
    打印的值应为 a = 2, b = 16;

 

***待完善***

posted @   口乞厂几  阅读(131)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结
点击右上角即可分享
微信分享提示