C语言-内存分配

内存分配

1. 引入

int nums[10] = {0}; //对

int len = 10;
int nums[len] = {0}; //错

是因为系统的内存分配原则导致的

2. 概述

在程序运行时,系统为了 更好的管理进程中的内存,所以有了 内存分配机制

分配原则:

2.1 静态分配

静态分配原则:

特点:

1、在程序编译过程中,按事先规定的大小 分配内存空间的分配方式;

2、必须事先知道所需空间的大小;

3、分配在 栈区或全局变量区,一般 以数组的形式

4、按计划分配

2.2 动态分配

特点:

1、在程序运行过程中,根据需要大小自由分配所需空间;

2、按需分配

3、分配在堆区,一般 使用特定的函数进行分配

案例:

需求:
    1、班级有15个学员,定义数组记录学员成绩
    	double score[15] = {0};
    
    2、记录学员成绩
    	- 输入学员数量
        - 在堆区申请
        - 扩展
        - 释放

注意:

在c语言中提供了一系列动态分配内存的函数
这些函数大部分都在stdlib.h头文件中声明

free 		释放
malloc 		申请空间,默认值随机
calloc 		申请空间,默认值为0
realloc 	扩展空间

string.h中提供的函数
memset 将malloc中的随机数设为0

3. 动态分配函数

3.1 memset 函数

作用:重置

语法:

#include <string.h>

void *memset(void *s, int c, size_t n);

参数:
	s: 开始的位置
	c: 重置后的数据
	n: 重置的数量
	从s开始, 将n个字节的数据, 设置为c

示例:

#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    char str[10] = {0};
    memset(str, 'a', 10);
    for (char i = 0; i < 10; i++)
    {
        printf("%c ", str[i]);
    }
    printf("\n");
    // a a a a a a a a a a 

    int nums[5] = {1,2,3,4,5};
    memset(nums, 0, 20);
    for (int i = 0; i < 5; i++)
    {
        printf("%d ", nums[i]);
    }
    printf("\n");
    //0 0 0 0 0 
    return 0;
}

3.2 free 函数

作用:释放内存

语法:

#include <stdlib.h>

void free(void *ptr);

参数:
	ptr: 指针

注意:

ptr 指向的内存必须是 malloccallocrelloc 动态申请的内存

3.3 malloc 函数

作用:在堆内存中开辟空间

语法:

void *malloc(size_t size)

参数:
	size_t: 可以理解为无符号int;
	size: 开辟空间大小,单位字节。
	
返回值:
    开辟的空间的地址
    开辟失败返回NULL

注意:

  1. 在使用malloc时,需要 判断是否开辟成功
  2. 如果多次 malloc 申请的内存,第 1 次和第 2 次申请的内存不一定是连续的;
  3. malloc的返回值在使用中 记得 强制类型转换 (因为该函数原型返回 void*指针 );
  4. malloc从堆区申请空间后 空间的内容中的值是随机的(与局部变量一样大概率为0),可以使用memset函数对空间中的数据进行置0。

示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    //1、申请空间
    //申请一个可以存储10个int数据的空间
    int *nums = (int *)malloc(10 * sizeof(int));
    //2、判断是否开辟失败
    if(nums == NULL)
    {
        printf("内存开辟失败\n");
        return 0;
    }
    //置0
    memset(nums, 0, 10*sizeof(int));
    //3、使用空间
    for (int i = 0; i < 10; i++)
    {
        printf("请输入第%d个数\n", i+1);
        scanf("%d", &nums[i]);
    }
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", nums[i]);
    }
    printf("\n");
    //4、释放空间
    free(nums);
    return 0;
}

3.4 calloc 函数

作用:在堆内存中开辟空间

语法:

void *calloc(size_t nmemb, size_t size);

参数:
	nmemb: 申请的块数
	size: 每块的大小
	
返回值:
    开辟的空间的地址
    开辟失败返回NULL
    
int *p = malloc(10 * sizeof(int));
int *p = calloc(10, sizeof(int));

示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    //1、申请空间
    //申请一个可以存储10个int数据的空间
    // int *nums = (int *)malloc(10 * sizeof(int));
    int *nums = (int *)calloc(10, sizeof(int));
    //2、判断是否开辟失败
    if(nums == NULL)
    {
        printf("内存开辟失败\n");
        return 0;
    }
    //3、使用空间
    for (int i = 0; i < 10; i++)
    {
        printf("请输入第%d个数\n", i+1);
        scanf("%d", &nums[i]);
    }
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", nums[i]);
    }
    printf("\n");
    //4、释放空间
    free(nums);
    return 0;
}

3.5 realloc 函数

作用:扩展空间,其实是重新申请内存

语法:

void *realloc(void *ptr, size_t size);

参数:
	ptr:原指针
	size:从新开辟的大小,原大小+新开的大小
	
返回值:
    开辟成功返回新地址
    开辟失败返回NULL

注意:

新地址不一定等于原地址,但是大概率相同

在原先 ptr 指向的内存基础上重新申请内存,新的内存的大小为 size 个字节,如果原先内存后面有足够大的空间,就追加,如果后边的内存不够用,则 relloc 函数会在堆区找一个 size 个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址。

在这里插入图片描述

示例:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    char * strs = (char *)calloc(3, sizeof(char));

    strs = realloc(strs, 2*sizeof(char));
    for (int i = 0; i < 5; i++)
    {
        scanf("%s", &strs[i]);
    }
    for (int i = 0; i < 5; i++)
    {
        printf("%c ", strs[i]);
    }
    printf("\n");
    free(strs);
    
    return 0;
}

4. 内存泄漏

4.1 概念

申请的内存,首地址丢了,找不了,再也没法使用了,也没法释放了,这块内存就被泄露了。

4.2 记录申请内存的指针变量指向别的地方

int *p = (int *)malloc(40);
int nums[10] = {};
p = nums; //p 指向别的地方了
//从此以后,再也找不到申请的 40 个字节了。 则动态申请的 40 个字节就被泄露了

4.3 在函数中申请空间,使用完毕没有释放

void test()
{
	int *p = (int *)malloc(40);
} 
test();  //每调用一次 test 泄露 40 个字节

5. 防止多次释放

多次释放示例:

int *p = (int *)malloc(40);
free(p);
free(p);

//注意多次释放会报错

防止多次释放:

释放前判断,释放后置NULL

示例:

int *p = (int *)malloc(40);
if(p != NULL)
{
    free(p);
    p = NULL;
} 
if(p != NULL)
{
    free(p);
    p = NULL;
}

6. 练习

设计函数,接收一个字符串,返回这个字符串的逆向内容
#include <stdio.h>
#include <stdlib.h>

int my_strlen(char *str)
{
    int len = 0;
    while (*str != '\0')
    {
        str++;
        len++;
    }
    return len;
}

char * my_strrev(char *str)
{
    int len = my_strlen(str);
    char *new_str = (char *)calloc((len+1), sizeof(char));
    for (int i = 0; i < len; i++)
    {
        new_str[i] = str[len-i-1];
    }
    new_str[len] = '\0';
    return new_str;
}

int main(int argc, char const *argv[])
{
    char *str = "helloworld";
    char *new_str = my_strrev(str);
    printf("%s\n", new_str);
    if (new_str != NULL)
    {
        free(new_str);
        new_str = NULL;
    }
    
    return 0;
}
// dlrowolleh
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值