激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - C/C++ - C語言動態(tài)內(nèi)存管理分析總結(jié)

C語言動態(tài)內(nèi)存管理分析總結(jié)

2022-02-28 14:58Ersansui C/C++

C語言中開辟內(nèi)存有很多種方式,目前我們最常用的也就是數(shù)組,但數(shù)組是在我們用到他之前就得設(shè)定好它的長度,有時很不方便。隨意我們來探究動態(tài)內(nèi)存管理

什么是動態(tài)內(nèi)存分配

我們都知道在C語言中,定義變量的時候,系統(tǒng)就會為這個變量分配內(nèi)存空間,而且這個空間是在棧上開辟的,這種方式就會有兩個特點。

  • 開辟的空間大小是固定的
  • 數(shù)組在申明的時候,必須要指定數(shù)組的長度,以方便為其分配內(nèi)存大小

但是這種方法并不能滿足我們在開發(fā)中的需要,因為有時候我們需要開辟的空間大小,是在程序巡行的過程中才能知道要開辟多大的。

這時候,數(shù)組的這種開辟方式就不能滿足了,因此也就有了動態(tài)內(nèi)存分配的需要。

而動態(tài)內(nèi)存分配,會根據(jù)需求而分配空間,大小是可以變化的,并且是在堆區(qū)上分配空間,而堆區(qū)的內(nèi)存空間大小一半都會比棧區(qū)大。

動態(tài)內(nèi)存函數(shù)的介紹

需要需要動態(tài)內(nèi)存管理,需要了解C語言中一下的幾個函數(shù)。

統(tǒng)一說明:一下的函數(shù)都包含在<stdlib.h>中。

free

void free (void* ptr);

為了后面可以進(jìn)行代碼演示,這里先介紹free這個函數(shù)。

我們在使用相關(guān)函數(shù)動態(tài)開辟了內(nèi)存空間之后,函數(shù)會返回這片空間的的首地址給使用者,當(dāng)使用者用完空間之后,需要手動對這片空間進(jìn)行釋放,把內(nèi)存空間還給操作系統(tǒng)。

如果參數(shù) ptr 指向的空間不是動態(tài)開辟的,那free函數(shù)的行為是未定義的。
如果參數(shù) ptr 是NULL指針,則函數(shù)什么事都不做。
釋放的空間是ptr所指向的那塊內(nèi)存空間

上面的第一種情況,有可能編譯器會報錯。

如果我們僅僅只是開辟空間使用,而不釋放的話,會占用內(nèi)存空間資源,造成內(nèi)存泄漏,影響性能。

因此,我們需要養(yǎng)成用完就釋放的好習(xí)慣。

malloc

void* malloc (size_t size);

這個函數(shù)向內(nèi)存申請一塊連續(xù)可用的空間,并返回指向這塊空間的指針。

連續(xù)可用的特點,就像數(shù)組一樣。

如果開辟成功,則返回一個指向開辟好空間的指針。
如果開辟失敗,則返回一個NULL指針。
返回值的類型是 void* ,因此使用者需要根據(jù)自己的需求來轉(zhuǎn)化使用。
如果參數(shù)size大小為0,malloc 的行為是標(biāo)志沒有規(guī)定的,取決于編譯器。

size的大小是按照字節(jié)為單位的。

?
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
#include <stdio.h>
 
int main()
{
    //代碼1
    int num = 0;
    scanf("%d", &num);
    int arr[num] = { 0 };
 
    //代碼2
    int* ptr = NULL;
    ptr = (int*)malloc(num * sizeof(int));
    if (NULL != ptr)//判斷ptr指針是否為空
    {
        int i = 0;
        for (i = 0; i < num; i++)
        {
            *(ptr + i) = 0;
        }
    }
 
    free(ptr);//釋放ptr所指向的動態(tài)內(nèi)存
    ptr = NULL;//是否有必要?
    return 0;
}

上面的代碼就是malloc函數(shù)的基本使用過程,在代碼1中,這樣的使用方法,一般來說編譯器是不支持的,而第二種方法,也就是動態(tài)內(nèi)存開辟的方法,編譯器就支持。

在最后,記得要把ptr所指向的空間給釋放掉。

這個時候,ptr種存儲的仍然是那塊空間的地址,這就稱為了野指針,所以我們還要把ptr置空。

calloc

與malloc類相似的,C語言還提供了另外一個動態(tài)開辟內(nèi)存的函數(shù),那就是calloc。

void* calloc (size_t num, size_t size);

需要注意的是,這個函數(shù)的參數(shù)有所不同,第一個參數(shù)num是用來確定你開辟的連續(xù)空間是用來存放多少個元素的,而第二個參數(shù)size表示的是一個元素占用多少的字節(jié)。

函數(shù)的功能是為 num 個大小為 size 的元素開辟一塊空間,并且把空間的每個字節(jié)初始化為0。
與函數(shù) malloc 的區(qū)別只在于 calloc 會在返回地址之前把申請的空間的每個字節(jié)初始化為全0。

所以,如果我們需要在動態(tài)開辟內(nèi)存的時候進(jìn)行初始化,我們可以使用calloc這個函數(shù)。

我們可以通過調(diào)試的監(jiān)視窗口來查看這個函數(shù)在開辟空間的時候是否幫我們初始化。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdlib.h>
 
 
int main()
{
    int* p = (int*)calloc(10, sizeof(int));
    if (NULL != p)
    {
        //使用空間
    }
    free(p);
    p = NULL;
    return 0;
}

realloc

有時候我們會發(fā)現(xiàn),哪怕是使用上面的動態(tài)內(nèi)存管理的函數(shù),也會有不方便的時候。我們可能會因為空間申請小了不夠用而去擴(kuò)容,但是如果我們使用上面兩個函數(shù)去開辟更大的空間的時候,之前的數(shù)據(jù)拷貝過來新開辟的更大的空間又很麻煩。

這個時候,我們就可以使用realloc了。

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

ptr 是要調(diào)整的內(nèi)存地址。
size 調(diào)整之后新大小,和malloc一樣,是以字節(jié)為單位的。
返回值為調(diào)整之后的內(nèi)存起始位置。
這個函數(shù)調(diào)整原內(nèi)存空間大小的基礎(chǔ)上,還會將原來內(nèi)存中的數(shù)據(jù)移動到新的空間。

在realloc函數(shù)使用的時候,會出現(xiàn)兩種情況:

原有的空間之后還有足夠的空間

如果原來的內(nèi)存空間之后還有足夠的空間來滿足新的大小,這時候就會直接對原來的空間進(jìn)行擴(kuò)容,原來空間的數(shù)據(jù)不會發(fā)生變化。

原有的空間之后沒有足夠的空間

如果原來的空間之后沒有足夠的空間滿足需要,就會在內(nèi)存區(qū)域中開辟一片能夠滿足新的大小需要的連續(xù)空間,并且把原來空間的數(shù)據(jù)拷貝的新的空間中,釋放原來的空間,返回新的地址。

?
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
#include <stdio.h>
 
int main()
{
    int* ptr = (int*)malloc(100);
    if (ptr != NULL)
    {
        //業(yè)務(wù)處理
    }
    else
    {
        exit(EXIT_FAILURE);
    }
 
    //擴(kuò)展容量
 
    //代碼1
    ptr = (int*)realloc(ptr, 1000);
    
    //代碼2
    int* p = NULL;
    p = realloc(ptr, 1000);
    if (p != NULL)
    {
        ptr = p;
    }
    //業(yè)務(wù)處理
    free(ptr);
    return 0;
}

上述代碼種,代碼1的做法是不安全的,如果我們擴(kuò)容失敗后,會返回空指針,這個時候,ptr接收了空指針,就會造成ptr原來的那塊空間的數(shù)據(jù)丟失,并且造成內(nèi)存泄漏。

因此,一般我們都需要像代碼2那樣,先用一個臨時的指針變量接收返回值,并且在判定不為空指針后再復(fù)制給ptr。

動態(tài)內(nèi)存管理中常見的錯誤

我們在使上面這些函數(shù)的時候,會經(jīng)常出現(xiàn)一下的錯誤。

對NULL指針的解引用操作

?
1
2
3
4
5
6
7
8
void test()
{
    int* p = (int*)malloc(INT_MAX / 4);
 
    //沒有進(jìn)行判空,就直接使用空間
    *p = 20;//如果p的值是NULL,就會有問題
    free(p);
}

對動態(tài)開辟空間的越界訪問

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void test()
{
    int i = 0;
    int* p = (int*)malloc(10 * sizeof(int));
    if (NULL == p)
    {
        exit(EXIT_FAILURE);
    }
    for (i = 0; i <= 10; i++)
    {
        *(p + i) = i;//當(dāng)i是10的時候越界訪問
    }
    free(p);
}

對非動態(tài)開辟內(nèi)存使用free釋放

?
1
2
3
4
5
6
void test()
{
int a = 10;
int *p = &a;
free(p);
}

使用free釋放一塊動態(tài)開辟內(nèi)存的一部分

?
1
2
3
4
5
6
7
void test()
{
    int* p = (int*)malloc(100);
    p++;
    free(p);//p不再指向動態(tài)內(nèi)存的起始位置
    //這樣做一般編譯器也會報錯的
}

對同一塊動態(tài)內(nèi)存多次釋放

?
1
2
3
4
5
6
void test()
{
    int* p = (int*)malloc(100);
    free(p);
    free(p);//重復(fù)釋放,一般來說,編譯器會報錯
}

動態(tài)開辟內(nèi)存忘記釋放(內(nèi)存泄漏)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
void test()
{
    int* p = (int*)malloc(100);
    if (NULL != p)
    {
        *p = 20;
    }
}
int main()
{
    test();
    while (1);
}

我們在使用上面的函數(shù)的時候,一定要小心,避免上面的這些錯誤。

一些經(jīng)典的筆試題

題目1

?
1
2
3
4
5
6
7
8
9
10
11
void GetMemory(char* p)
{
    p = (char*)malloc(100);
}
void Test(void)
{
    char* str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}

上面的代碼有錯,在開辟空間后,沒有進(jìn)行判空操作,并且在調(diào)用GetMemory函數(shù)的時候,傳入的形參是str的一份臨時拷貝,在函數(shù)內(nèi)部p的改變不會改變main中的str,所以在GetMemory返回的時候,p所指向的空間會泄漏,并且在strcpy中,造成了對空指針的解引用操作。

題目2

?
1
2
3
4
5
6
7
8
9
10
11
char* GetMemory(void)
{
    char p[] = "hello world";
    return p;
}
void Test(void)
{
    char* str = NULL;
    str = GetMemory();
    printf(str);
}

這段代碼也是錯誤的,在GetMemory中,不是用動態(tài)內(nèi)存管理的函數(shù)來開辟空間,而是使用數(shù)組的開辟方式,這樣的開辟方式會在棧區(qū)開辟空間,當(dāng)GetMemory函數(shù)調(diào)用完成的時候,就會銷毀開辟的數(shù)組,這時候,外面的str接收了返回的數(shù)組的地址,就會變成一個野指針。

題目3

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

上面的代碼在使用完成動態(tài)開辟的空間后沒有進(jìn)行判空操作,并且沒有進(jìn)行內(nèi)存釋放,造成了內(nèi)存泄漏。

題目4

?
1
2
3
4
5
6
7
8
9
10
11
void Test(void)
{
    char* str = (char*)malloc(100);
    strcpy(str, "hello");
    free(str);
    if (str != NULL)
    {
        strcpy(str, "world");
        printf(str);
    }
}

上面的代碼提前釋放了空間,后面又使用指針對已經(jīng)釋放的空間進(jìn)行操作,這是非法的,釋放后,指向這塊空間的指針就是野指針,需要進(jìn)行置空。

柔性數(shù)組

C99 中,結(jié)構(gòu)中的最后一個元素允許是未知大小的數(shù)組,這就叫做『柔性數(shù)組』成員。

例如:

?
1
2
3
4
5
typedef struct st_type
{
    int i;
    int a[0];//柔性數(shù)組成員
}type_a;

如果有的編譯器報錯,可以改成下面這個樣子

?
1
2
3
4
5
typedef struct st_type
{
    int i;
    int a[];//柔性數(shù)組成員
}type_a;

柔性數(shù)組的特點

結(jié)構(gòu)中的柔性數(shù)組成員前面必須至少一個其他成員。
sizeof 返回的這種結(jié)構(gòu)大小不包括柔性數(shù)組的內(nèi)存。
包含柔性數(shù)組成員的結(jié)構(gòu)用malloc ()函數(shù)進(jìn)行內(nèi)存的動態(tài)分配,并且分配的內(nèi)存應(yīng)該大于結(jié)構(gòu)的大小,以適應(yīng)柔性數(shù)組的預(yù)期大小。

例如:

?
1
2
3
4
5
6
7
//code1
typedef struct st_type
{
    int i;
    int a[0];//柔性數(shù)組成員
}type_a;
printf("%d\n", sizeof(type_a));//輸出的是4
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct st_type
{
    int i;
    int a[0];//柔性數(shù)組成員
}type_a;
 
//代碼1
int i = 0;
type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
//業(yè)務(wù)處理
p->i = 100;
for (i = 0; i < 100; i++)
{
    p->a[i] = i;
}
free(p);

這樣柔性數(shù)組成員a,相當(dāng)于獲得了100個整型元素的連續(xù)空間。

柔性數(shù)組的優(yōu)勢

上面的代碼也可以這樣設(shè)計:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//代碼2
typedef struct st_type
{
    int i;
    int* p_a;
}type_a;
 
type_a* p = (type_a*)malloc(sizeof(type_a));
p->i = 100;
 
p->p_a = (int*)malloc(p->i * sizeof(int));
 
//業(yè)務(wù)處理
for (i = 0; i < 100; i++)
{
    p->p_a[i] = i;
}
 
//釋放空間
free(p->p_a);
p->p_a = NULL;
free(p);
p = NULL;

上述 代碼1 和 代碼2 可以完成同樣的功能,但是 方法1 的實現(xiàn)有兩個好處:

  • 方便內(nèi)存釋放
  • 這樣有利于訪問速度

到此這篇關(guān)于C語言動態(tài)內(nèi)存管理分析總結(jié)的文章就介紹到這了,更多相關(guān)C語言 動態(tài)內(nèi)存管理內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://blog.csdn.net/weixin_61021362/article/details/121092690

延伸 · 閱讀

精彩推薦
  • C/C++c++ 單線程實現(xiàn)同時監(jiān)聽多個端口

    c++ 單線程實現(xiàn)同時監(jiān)聽多個端口

    這篇文章主要介紹了c++ 單線程實現(xiàn)同時監(jiān)聽多個端口的方法,幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內(nèi)存中的數(shù)據(jù)都是暫時的,當(dāng)程序結(jié)束時,它們都將丟失,為了永久性的保存大量的數(shù)據(jù),C語言提供了對文件的操作,這篇文章主要給大家介紹了關(guān)于C語言中文件...

    針眼_6702022-01-24
  • C/C++深入理解goto語句的替代實現(xiàn)方式分析

    深入理解goto語句的替代實現(xiàn)方式分析

    本篇文章是對goto語句的替代實現(xiàn)方式進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下...

    C語言教程網(wǎng)7342020-12-03
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數(shù)使用

    詳解c語言中的 strcpy和strncpy字符串函數(shù)使用

    strcpy 和strcnpy函數(shù)是字符串復(fù)制函數(shù)。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數(shù)使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++C/C++經(jīng)典實例之模擬計算器示例代碼

    C/C++經(jīng)典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關(guān)于C/C++經(jīng)典實例之模擬計算器的相關(guān)資料,文中通過示...

    jia150610152021-06-07
  • C/C++C語言實現(xiàn)電腦關(guān)機(jī)程序

    C語言實現(xiàn)電腦關(guān)機(jī)程序

    這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)電腦關(guān)機(jī)程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++學(xué)習(xí)C++編程的必備軟件

    學(xué)習(xí)C++編程的必備軟件

    本文給大家分享的是作者在學(xué)習(xí)使用C++進(jìn)行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
主站蜘蛛池模板: 国内成人自拍视频 | 91成人亚洲| xxxxhd73国产 | 亚洲成人福利 | 国产精品久久久久久久久久大牛 | 久久精品视频一区二区 | 国产妇女乱码一区二区三区 | 欧美一级爱操视频 | 欧美精品一区二区三区四区 | 最新日本中文字幕在线观看 | 日本aaa一级片 | 欧美高清另类自拍视频在线看 | 91成人免费版 | 欧美一级高清片_欧美高清aa | 久久成人激情视频 | 狠狠干夜夜操 | 欧美毛片 | 国产无遮挡成人免费视频 | 他也色在线视频 | 视频国产一区二区 | 不卡国产一区二区三区四区 | 欧美一级黄 | 亚洲第五色综合网 | 久久精品欧美一区二区 | 特级黄色一级毛片 | 污版视频在线观看 | 一级做人爱c黑人影片 | 久久久aa| 在线亚洲免费 | 99在线热视频 | 国产成人精品区一区二区不卡 | 久久亚洲春色中文字幕久久 | 亚洲视频综合网 | 女人裸体让男人桶全过程 | 在线观看视频毛片 | 欧洲成人综合网 | 国产在线1区 | 国产1区2区3区中文字幕 | 国内精品久久久久久久星辰影视 | 亚洲最新无码中文字幕久久 | 国产一级在线免费观看 |