日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C語言的動態內存分配及動態內存分配函數詳解_C 語言

作者:Green_756 ? 更新時間: 2022-05-23 編程語言

malloc

void *malloc( size_t?size?);

Tips:這里的size代表的是字節的大小

malloc的使用:

//malloc的使用
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
#include 
 
int main()
{
    int* str = 0;
    int* p = 0;
    str = (int*)malloc(10*sizeof(int));//開辟十個整型空間
    if (NULL == str)
    {
        printf("%s\n", strerror(errno));//若開辟失敗
        //使用報錯函數strerror(errno)    要引用頭文件
    }
    else
    {
        p = str;
    }
    free(p);
    p = NULL;
    return 0;
}

free

釋放申請的內存空間,例:free(p)

當釋放后,雖然p中的值還在,不變,但p就為野指針了。所以建議釋放后將p設置為空指針。(p=NULL)

calloc

calloc:開辟并且初始化為0的數組。

void* calloc(size_t num,size_t size)

  • num——元素個數
  • size——元素大小

成功的話返回地址,失敗返回空指針NULL

calloc的使用:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
#include 
 
int main()
{
    int* str = 0;
    int* p = 0;
    str = (int*)calloc(10,sizeof(int));
    if (NULL == str)
    {
        printf("%s\n", strerror(errno));
    }
    else
    {
        p = str;
    }
    free(p);
    p = NULL;
    return 0;
}

realloc

可開辟空間,也可以調整空間。

void?*realloc(?void?*memblock,?size_t?size?);

  • memblock——要開辟空間的指針類型
  • size——要開辟的字節大小

p=(int)realloc(p,80)*——這樣子寫也是有風險的。

風險:為了避免可能會把增容的后面的已有的內存空間給覆蓋掉,所以會在另一塊大小足夠的地方開辟空間,然后把原來的數據轉移到新的空間上。并且把原來的內存空間給釋放掉。

若realloc調整空間失敗,則返回NULL。原來的數據也沒有了。

realloc的使用改進:

int* ptr=(int*)realloc(p,80);
if(NULL!=ptr)
{
    p=ptr;//這樣子能夠保證確定了不為空指針后才正式傳給p,相當于沒有了會失去原來數據的風險
}

realloc的另一種用法:

int* p=(int*)realloc(NULL,40);

這種寫法相當于malloc

常見的動態內存錯誤

對空指針的解引用操作

將malloc函數開辟一個賊大的空間,INT_MAX,此時會有一個空指針,進行判斷,如果為空指針就立馬結束這個程序了。不要出問題(ps:這里的INT_MAX的使用要引用頭文件limits.h)

所以要判斷是不是空指針,是的話就中斷,例:

//錯誤寫法
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
#include 
#include 
 
int main()
{
    int i = 0;
    int* p = (int*)malloc(INT_MAX);
    for (i = 0; i < 5; i++)
    {
        *(p + i) = i;
    }
    return 0;
}
 
//正確寫法:
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
#include 
#include 
 
int main()
{
    int i = 0;
    int* p = (int*)malloc(INT_MAX);
    if (p == NULL)
    {
        printf("%s\n", strerror(errno));//這里是將錯誤報出來
        return 0;//發現是空指針,提前結束
    }
    for (i = 0; i < 5; i++)
    {
        *(p + i) = i;
    }
    return 0;
}
 

對動態開辟空間的越界訪問

不可以不申請即使用動態內存空間,會報錯的。

Tips:沒有開辟的空間是不能使用的

對非動態開辟內存使用free釋放

int main()
{
    int p=0;
    int* a=&p;
    free(a);//這個樣子是錯誤的
    return 0;
}

使用free釋放一塊動態開辟內存的一部分

開辟動態空間的時候,一定要把起始位置給用變量存好,否則到時會無法釋放內存。

//使用free釋放一塊動態開辟內存的一部分
 
//正確寫法:
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
 
int main()
{
    int i = 0;
    int* p = (int*)malloc(10 * sizeof(int));
    //正確寫法:
    for (i = 0; i < 5; i++)
    {
        *(p + i) = i;
    }
    free(p);
    p=NULL;
    return 0;
}
    
    
    
    
//錯誤寫法:
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
 
int main()
{
    int i = 0;
    int* p = (int*)malloc(10 * sizeof(int));
    //錯誤寫法:
    for (i = 0; i < 5; i++)
    {
        *p = i;
        p++;//這里會改變p的原始位置,使得無法指向一開始開辟動態內存空間的位置,最終報錯
    }
    free(p);
    p = NULL;
    return 0;
}

對同一塊動態內存多次釋放

一塊空間釋放后不可再釋放,但釋放完后p置為空指針再次釋放時不會報錯。

Q:free空指針時會有問題么?

A:不會,因為一塊空間釋放后就不能再次釋放了,所以每次free完后記得置為空指針。

動態開辟內存忘記釋放(內存泄露)

即使在函數中開辟內存空間也要記得釋放。因為出了函數在外面想釋放也無法釋放。

但如果返回首元素的地址,free了也行,就是無論怎么樣,一定要釋放。

在任何地方開辟的內存空間都最好要釋放。

找出下面問題:

T1:

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;
}

出現的問題:

在這里str是空指針,而p只是新建的一個形參,運行完函數后無法返回p不存在了,但是內存空間還未被釋放,而這個空間的地址此時是沒有人能夠知道的。也并不能將str里面的NULL改變,所以在strcpy時會出錯,因為str此時為NULL指針,會造成非法訪問內存,程序會崩潰。

而且在使用過程中只進行了動態內存的開辟,沒有進行動態內存的釋放,可能會造成動態內存泄露。

改進方法:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
 
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;
}

函數的棧幀與創建:p盡管銷毀,因為會先把p里面的值放入到寄存器中,寄存器里面不會銷毀,之后再從寄存器位置傳進去str。

T2:

出現的問題:

返回棧空間地址問題:

這里雖然能把p的地址傳回去,但是在函數運行完后在函數里面創建的數據會被銷毀,也就是說雖然能通過指針找到原來的內存所指向的地方,但是數據都以被銷毀。

注意!!!

這樣是可以的,因為返回的是棧空間的變量而不是棧空間的地址。

總結:

在創造函數如果返回地址而不是返回值,在用的時候可能依然是在函數內的值,但也有很大可能不是,可能不是的原因是有關函數棧幀方面,如果在引用地址前再寫上一段例如:"printf("23333\n");",可能會導致覆蓋掉原來地址上的數據,所以無法通過傳址來輸出真正的值,因為會被覆蓋掉。

T3:

出現的問題:

除了free沒有太大毛病了。這里能夠打印出hello。

T4:

出現的問題:

這里的free其實是把動態內存空間還給系統了,但是str的話沒有定為空指針,仍然存著當初指向開辟的內存空間的地址,那么就還可以通過str找到當初開辟的內存空間,只是這個時候因為釋放(free)str了,所以此時沒有訪問空間的權限,也就無法將world拷貝到str所指向的空間。

正確改法:

所以,在每次free后面都要記得設置為空指針。

柔性數組

在c99中,結構體中的最后一個元素是允許未知大小的數組,這就叫做【柔性數組】成員。

柔性數組的定義

//寫法一:
struct s1
{
    int n;
    int arr[0];//大小是未指定
}
//寫法二:
struct s2
{
    int n;
    int arr[];//大小是未指定
}
//總會有一種寫法編譯器不報錯
 

Tips:在計算包含柔性數組大小的時候,柔性數組是不計算在大小里面的。(可以寫一個來試一下)

柔性數組的特點:

  • 柔性數組前至少需要一個其他成員
  • sizeof返回的這種結構大小不包括柔性數組的內存
  • 包含柔性數組成員的結構用malloc()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。

柔性數組的開辟(自己先寫)

包含柔性數組的結構體不可以直接創建,而是要有malloc來開辟空間。

//寫柔性數組的方法一:
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
 
struct p
{
    int i;
    int arr[];
};
 
int main()
{
    struct p* cmp = (struct p*)malloc(sizeof(struct p) + 80);//這里是開辟了一共84個字節空間,分給arr數組80個字節空間
    free(p);
    p = NULL;
    return 0;
}
 
 
//寫柔性數組的方法二:(先開辟整個的,再開辟數組的)
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
 
struct p
{
    int i;
    int* arr;//這樣才能在方法二中使用
};
 
int main()
{
    struct p* cmp = (struct p*)malloc(sizeof(struct p));
    cmp->i = 10;
    cmp->arr = (int*)malloc(80);//從數組開始,再次開辟80個字節空間
    free(p);
    p = NULL;
    return 0;
}
 

第二種方案(劣勢):

1.開辟和釋放的次數多,容易出錯

2.頻繁多次開辟內存,會有內存碎片出現,可能會導致內存的使用效率不高

第一種方案優勢:

1.方便釋放

2.減少內存碎片的出現

總結

原文鏈接:https://blog.csdn.net/Green_756/article/details/123595906

欄目分類
最近更新