服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言 动态内存开辟常见问题解决与分析流程

C语言 动态内存开辟常见问题解决与分析流程

2022-10-20 13:51K稳重 C/C++

动态内存是相对静态内存而言的。所谓动态和静态就是指内存的分配方式。动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存

前言

当我们用动态内存分配函数来编写程序时,在编写的过程中常常会产生一些不易被察觉,被发现的错误,例如对NULL指针的解引用操作,对动态开辟空间的越界访问,使用free释放非动态开辟的空间,使用free释放动态内存中的一部分,对同一块动态开辟的空间,多次释放,动态开辟空间忘记释放。下面我们挨个来分析,刨析一下这些个常见的动态内存开辟的问题。

一、动态内存错误

1.对NULL指针的解引用操作

代码如下(示例):

?
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
错误示例:
//动态内存开辟
int main()
{
 
    int* p = malloc(100000000000);
    //没有对mollac函数的返回值做判空处理
        int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = 5;
    }
    
    return 0;
}
 
正确示例:
//动态内存开辟
int main()
{
 
    int* p = malloc(100000000000);
    if (p == NULL)
    {
        return 1;
    }
        int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = 5;
    }
    for (i = 0; i < 10; i++)
    {
        printf("%d ", p[i]);
    }
    free(p);
    p = NULL;
    return 0;
}

2.对动态开辟空间的越界访问

代码如下(示例):

?
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
错误示例:
//动态内存开辟
int main()
{
 
    int* p = (int*)malloc(10*sizeof(int));
    if (p == NULL)
    {
        return 1;
    }
        int i = 0;
        //越界访问
    for (i = 0; i < 40; i++)//malloc函数只是开辟了十个整型的空间,这里却要访问四十个元素。
    {
        *(p + i) = 5;
    }
    for (i = 0; i < 40; i++)
    {
        printf("%d ", p[i]);
    }
    free(p);
    p = NULL;
    return 0;
}
 
正确示例:
//动态内存开辟
int main()
{
 
    int* p = (int*)malloc(10*sizeof(int));
    if (p == NULL)
    {
        return 1;
    }
        int i = 0;
        
    for (i = 0; i < 10; i++)
        *(p + i) = 5;
    }
    for (i = 0; i < 10; i++)
    {
        printf("%d ", p[i]);
    }
    free(p);
    p = NULL;
    return 0;
}

3.使用free释放非动态开辟的空间

代码如下(示例):

?
1
2
3
4
5
6
7
8
9
//动态内存开辟
int main()
{
    int arr[10] = { 0 };//栈区
    int* p = arr;
    free(p);//使用free释放非动态开辟的空间
    p = NULL;
    return 0;
}

4.使用free释放动态内存中的一部分

代码如下(示例):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//动态内存开辟
int main()
{
    int* p = malloc(10 * sizeof(int));
    if (p == NULL)
    {
        return 1;
    }
    int i = 0;
    for (i = 0; i < 5; i++)
    {
        *p++ = i;//1:p一直往后走之后没人知道起始空间的位置在哪,2:p释放的只是后面空间的一部分,前面的空间并没有得到释放。
    }
    free(p);
    p = NULL;
    return 0;
}

5.对同一块动态内存动态开辟的空间多次释放

代码如下(示例):

?
1
2
3
4
5
6
7
8
9
10
11
12
//动态内存开辟
int main()
{
    int* p = malloc(10 * sizeof(int));
    //使用
    //释放
    free(p);
    //再次释放
    free(p);//free要是传的是空指针什么事都不会发生。
    p = NULL;
    return 0;
}

6.动态开辟的空间忘记释放(容易造成内存泄露,比较严重)

代码如下(示例):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void test()
{
    int* p = malloc(10 * sizeof(int));
    if (p == NULL)
    {
        return 1;
    }
    //使用
    //忘记释放
}
 
//动态内存开辟
int main()
{
    test();
    return 0;
}

二、动态内存错误面试题分析

1.NULL指针传参不取地址传的也是一份临时拷贝

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
例题分析:
 
void GetMemory(char* p)
{
    p = (char*)malloc(100);
}
void Test(void)
{
    char* str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}
 
int main()
{
    test();
    return 0;
}

程序运行结果:

C语言 动态内存开辟常见问题解决与分析流程

拷贝不成功,程序直接挂掉。

原因分析:

str传给GetMemory函数的时候是值传递,所以GetMemory函数的形参p是str的一份临时拷贝。
在GetMemory函数内部动态申请空间的地址,存放在P中,不会影响外面str,所以当GetMemory函数返回
之后,str任然是NULL指针,所以strcpy会失败。
当GetMemory函数返回之后,形参p销毁,使得动态开辟的100个字节存在内存泄漏。

 正确代码:

?
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
//第一种改法:
 
char* GetMemory(char* p)
{
    p = (char*)malloc(100);
    return p;
}
void test(void)
{
    char* str = NULL;
    str = GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
    free(str);
    str = NULL;
}
 
int main()
{
    test();
    return 0;
}
 
 
//第二种改法:
 
char* GetMemory(char** p)
{
    *p = (char*)malloc(100);
}
void test(void)
{
    char* str = NULL;
    GetMemory(&str);
    strcpy(str, "hello world");
    printf(str);
    free(str);
    str = NULL;
}
 
int main()
{
    test();
    return 0;
}

2.局部变量和形式参数存在于栈上

代码如下(示例):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//例题分析:
 
char* GetMemory(void)
{
    char p[] = "hello world";
    return p;
}
void Test(void)
{
    char* str = NULL;
    str = GetMemory();
    printf(str);
}
 
int main()
{
    test();
    return 0;
}

程序运行结果:

C语言 动态内存开辟常见问题解决与分析流程

打印不成功,打印的都是随机值

原因分析:

GetMemory函数内部创建的数组是在栈区上创建的
出了函数,p的数组的空间就还给了操作系统
返回的地址是没有实际意义的,如果通过返回的地址,去访问内存就是非法访问内存。

 正确代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char* GetMemory(void)
{
    static char p[] = "hello world";
    return p;
}
void test(void)
{
    char* str = NULL;
    str = GetMemory();
    printf(str);
}
 
int main()
{
    test();
    return 0;
}

3.动态内存开的空间记得free释放掉

代码如下(示例)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void GetMemory(char** p, int num)
{
    *p = (char*)malloc(num);
}
void Test(void)
{
    char* str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);
}
int main()
{
    test();
    return 0;
}

错误分析:

申请的动态内存空间使用完之后没有及时free释放掉。

正确代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void GetMemory(char** p, int num)
{
    *p = (char*)malloc(num);
}
void test(void)
{
    char* str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);
    free(str);
    str = NULL;
}
int main()
{
    test();
    return 0;
}

4.非法访问内存

代码如下(示例)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void test(void)
{
    char* str = (char*)malloc(100);
    strcpy(str, "hello");
    free(str);
    if (str != NULL)
    {
        strcpy(str, "world");
        printf(str);
    }
}
int main()
{
    test();
    return 0;
}

错误分析:

申请的空间已经free释放还给操作系统了,及时str还记得这块空间的起始地址,但是也不能访问,属于非法访问内存空间。
free完之后要及时把str置成NULL指针。

正确代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void test(void)
{
    char* str = (char*)malloc(100);
    strcpy(str, "hello");
    free(str);
    str = NULL;
    if (str != NULL)
    {
        strcpy(str, "world");
        printf(str);
    }
}
int main()
{
    test();
    return 0;
}

总结:

上述给大家简单介绍了动态内存开辟常见的几种问题,也分析了往年的几道面试题里面的错误,让我们加深了对这一章的理解,后续自己使用的时候可以有效的规避掉这些问题。相信大家都学会了。如果上述文章有任何问题 ,欢迎大佬们提出质疑,我会虚心学习和改正,最重要的是能共同进步,共同成长,学习好编程。

到此这篇关于C语言 动态内存开辟常见问题解决与分析流程的文章就介绍到这了,更多相关C语言 动态内存开辟内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/m0_64397675/article/details/122942990

延伸 · 阅读

精彩推荐