如果學習方向是c++方向那么c語言有三個板塊的知識是非常重要的. 1:指針 2:結構體 3;動態內存管理.
序言:在c語言中,什么是動態內存
C語言中的動態內存是指在程序運行時,根據需要動態地分配內存空間的一種內存管理方式。與靜態內存相比,動態內存的大小和生命周期都可以在程序運行時動態地確定和調整,因此更加靈活。C語言中提供了四個函數:malloc、calloc、realloc和free,用于動態地分配和釋放內存空間。其中,malloc和calloc用于分配內存空間,realloc用于調整已分配內存空間的大小,free用于釋放已分配的內存空間。動態內存的使用需要引用頭文件<stdio.h>或<malloc.h>。
在本篇文章中,我們將著重簡述何為malloc函數,calloc函數,free,以及柔性數組
1.malloc函數簡述
1.1free函數
C語言提供了另外?個函數free,專門是用來做動態內存的釋放和回收的,函數原型如下:
?void free (void* ptr);
free函數用來釋放動態開辟的內存。
?但凡涉及到動態內存的開辟,釋放空間這一步作為最后一步絕對是不可或缺的.
1.2 malloc函數
函數頭文件: #include <stdlib.h>
函數參數: void* malloc (size_t size);
函數作用: 用于開辟動態內存
從代碼示例看函數的具體使用方法
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main()
{
?? ?int* p = (int*)malloc(10 * sizeof(int));//1.動態內存的開辟(指針p所指向的是malloc函數開辟空間的起始地址)? ? ?malloc():括號里面要寫的是開辟多少大小的內存空間,一般用:任意整數*sizeof(整型)
?? ?if (p == NULL)
?? ?{
?? ??? ?perror("malloc");
?? ??? ?return 1;
?? ?}
?? ?int i = 0;
?? ?for (i = 0; i < 10; i++)//2.對動態內存開辟的空間進行訪問
?? ?{
?? ??? ?*(p + i) = i;//使用指針p去訪問malloc開辟的空間里面的元素,再解引用賦值給i
?? ?}
?? ?for (i = 0; i < 10; i++)//3.對開辟的空間中的內容進行打印
?? ?{
?? ??? ?printf("%d ",i );//將剛才訪問的空間里面存儲的數據打印出來
?? ?}
?? ?free(p);//4.對開辟的空間進行回收
?? ?p=NULL;
?? ?return 0;
?}
為啥要這么寫:??int* p = (int*)malloc(10 * sizeof(int));
解釋:由函數參數 void* malloc (size_t size);可知malloc函數一個空指針類型的函數,但是由于空指針是不可以直接用于指針的運算的,所以我們要將它強制類型轉換成我們想要的類型.
一點聯想:這一點讓我想起了qsort函數,感覺void*類型的指針都要進行這樣的操作,可以在這里留意一下并且進
行相關的知識遷移以便日后遇到相似知識點的學習
2.calloc函數簡述
總而言之,calloc函數的使用方法以及功能和malloc函數基本一致
函數頭文件: #include <stdlib.h>
函數參數: void* calloc (size_t num, size_t size);
函數作用: 用于開辟動態內存
從代碼示例看函數的具體使用方法
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main()
{
?? ?int* p = calloc(10, sizeof(int));//1.動態內存的開辟
?? ?if (p == NULL)
?? ?{
?? ??? ?perror("calloc");
?? ??? ?return 1;
?? ?}
?? ?int i = 0;//2.對動態內存開辟的空間進行訪問
?? ?for (i = 0; i < 10; i++)
?? ?{
?? ??? ?*(p + i) = i;
?? ?}
?? ?for (i = 0; i < 10; i++)//3.對開辟的空間中的內容進行打印
?? ?{
?? ??? ?printf("%d ", *(p + i) );
?? ?}
?? ?free(p);//4.對開辟的空間進行回收
?? ?p = NULL;?? ?return 0;
}
3.對malloc函數與calloc函數進行總結
3.1 不同點 (函數參數上)
以上述兩段代碼為例
malloc函數是:int* p = (int*)malloc(10 * sizeof(int));
calloc函數是:?int* p = (int*)calloc(10, sizeof(int));
觀察上述兩段代碼,其實也沒啥不同點,就是把函數括號里面我們想要開辟的空間的那個表達式把逗號改成了*號的區別嘛
3.2 相同點(函數使用步驟上)
首先要明確的是它們都要進行強制類型轉換轉換成我們想要的指針類型.
最重要的是,它們是使用步驟可以分成四步
1.動態內存的開辟
2.對動態內存開辟的空間進行訪問
3.對開辟的空間中的內容進行打印
4.對開辟的空間進行釋放
簡化一下就是: 1.開辟 2.訪問 3.使用 4.釋放
這兩個函數在這四個步驟上的寫法是一摸一樣,沒有任何區別的.將此四部先后邏輯順序理清并進行適當的記憶這兩個動態內存函數便可以說是掌握了.
所以此二者等價
4.重要的realloc函數的簡介與使用
4.1 對realloc函數的使用進行簡述
?在本文的開頭闡述過何為動態內存"...動態內存的大小和生命周期都可以在程序運行時動態地確定和調整,因此更加靈活。..."
故而沒有realloc函數的介入很難把一段可正常運行的代碼叫做"動態內存管理"
函數頭文件: #include <stdlib.h>
函數參數: void* realloc (void* ptr, size_t size);
對函數參數的解釋: void*ptr就是要進行擴展的對象,size_t size就是要將被擴展的擴展至管理員預期的空間
函數作用: 用于動態內存的擴展,要和malloc函數或者calloc函數進行聯合使用
具體且形象的講解realoc函數的作用:
realloc的作用就是將原本malloc和calloc開辟的空間擴大到多少
舉個例子就是說如果malloc或calloc是一段單向路上不與起點重合的一個質點,calloc就是另外一個質點
用calloc質點到起點的距離減去malloc,calloc質點所在的距離便是calloc函數所追加的空間
如圖
?
從代碼示例看函數的具體使用方法
就是在malloc函數和calloc函數原先的使用步驟上加上擴容這一步
1.動態內存的開辟
2.對動態內存開辟的空間進行訪問
3,對malloc函數或calloc函數開辟的空間進行擴容
4.對開辟的空間中的內容進行打印
5.對開辟的空間進行釋放
總結下來就是 1.開辟 2.訪問 3,擴容 4.使用 5.釋放
4.2?以calloc函數進行開辟為例,擴容的公式
? ? int* ptr = (int*)realloc(p, 15 * sizeof(int));//擴展開辟空間
?? ?if (p != NULL)
?? ?{
?? ??? ?p = ptr;
?? ?}
?? ?else
?? ?{
?? ??? ?perror("errno");
?? ??? ?return 1;
?? ?}?
為什么這么寫: int* ptr = (int*)realloc(p, 15 * sizeof(int));
這里便涉及到calloc函數擴展內存空間的方式了
4.2.1calloc函數擴展內存空間的方式
情況1:擴展失敗,返回NULL,于是便有了?
?else
?? ?{
?? ??? ?perror("errno");
?? ??? ?return 1;
?? ?}?
情況2;擴展成功了,于是便有了
if (p != NULL)
?? ?{
?? ??? ?p = ptr;
?? ?}
擴展成功方式1:
在malloc函數或者calloc函數開辟好的空間后進行擴容,如果沒有足夠的空間進行擴大時候,此時的calloc函數會在堆區中重新選擇一塊大小滿足需求的空間,同時將舊空間中的舊數據連同著一塊拷過來,這也是為啥雖然上述代碼? int* ptr = (int*)realloc(p, 15 * sizeof(int));雖然寫的是15 * sizeof(int)實際上只是擴展了5*sizeof(int)大小的空間.然后釋放就空間,同時返回新的空間.
擴展成功方式1:
若空間大小足夠,則在已經開辟好的空間直接進行追加空間進行擴展,擴大空間后,直接返回就空間的起始地址.
?
4.3 從示例看realloc函數的具體用法?
根據上面總結的五個步驟來寫:??1.開辟 2.訪問 3,擴容 4.使用 5.釋放
int main()// 開辟 訪問 ? 擴容 使用 釋放
{
?? ?//開辟
?? ?int* p = (int*)calloc(10, sizeof(int));
?? ?if (p == NULL)
?? ?{
?? ??? ?perror("calloc");
?? ??? ?return 1;
?? ?}?? ?//訪問
?? ?int i = 0;
?? ?for (i = 0; i < 10; i++)
?? ?{
?? ??? ?*(p + i) = i;
?? ?}?? ?//擴容
? ? ?int* ptr = (int*)realloc(p, 15 * sizeof(int));//擴展開辟空間
?? ?if (p != NULL)
?? ?{
?? ??? ?p = ptr;
?? ?}
?? ?else
?? ?{
?? ??? ?perror("errno");
?? ??? ?return 1;
?? ?}?? ?//使用
?? ?for (i = 0; i < 10; i++)
?? ?{
?? ??? ?printf("%d ", *(p + i) );
?? ?}?? ?
?? ?//釋放
?? ?free(p);
?? ?p = NULL;?? ?return 0;
}
ps:其實realloc函數除了調整空間外,也可以實現和malloc或者realloc函數一樣的功能
int*p=(int*)realloc(NULL,1o*sizeof(int));等價于malloc或者calloc函數,只是一般不這么去寫.
5.柔性數組?
5.1 柔性數組概述
?C99 中,結構中的最后一個元素允許是未知大小的數組,這就叫做『柔性數組』成員。
結構體成員的特點
? 結構中的柔性數組成員前面必須至少一個其他成員。
? sizeof 返回的這種結構大小不包括柔性數組的內存。
? 包含柔性數組成員的結構用malloc ()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。
5.2 柔性數組的使用
以malloc函數為例,柔性數組的使用依舊遵循上述的五個步驟,不同的是要先創建一個結構體
1. 開辟 2.訪問 3.擴容 4.使用 5.釋放
struct st
{
?? ?char c;
?? ?int n;
?? ?int arr[0];
};
int main()
{
?? ?struct st* ps = (struct st*)malloc(sizeof(struct st) + 10 * sizeof(int));//1.開辟
?? ?//前面的sizeof(struct st)是計算此結構體本來的大小
?? ?//后面的 10 * sizeof(int) 意思是將柔性數組成員開辟成多少大小的
?? ?if (ps == NULL)
?? ?{
?? ??? ?perror("malloc");
?? ??? ?return 1;
?? ?}?? ?ps->c = 'w';//2.訪問
?? ?ps->n = 100;
?? ?int i = 0;
?? ?for (i = 0; i < 10; i++)
?? ?{
?? ??? ?ps->arr[i]=i;//也可以這么寫 QUESTION2;點操作符和箭頭操作符的區別,在那些情況下一般用什么的總結歸納
?? ?}?? ?struct st* ptr = (struct st*)realloc(ps, sizeof(struct st) + 15 * sizeof(int));//3.擴容
?? ?if (ptr != NULL)?
?? ?{
?? ??? ?ps = ptr;
?? ?}
?? ?else
?? ?{
?? ??? ?perror("realloc");
?? ??? ?return 1;
?? ?}?? ?for (i = 0; i < 10; i++)//4.使用
?? ?{
?? ??? ?printf("%d ", i);
?? ?}
?? ?printf("\n");
?? ??? ?printf("%c \n", ps->c);
?? ??? ?printf("%d \n", ps->n);
?? ?free(ps);//5.釋放
?? ?ps = NULL;
?? ?return 0;
}
ps:"->"操作符和"."(點)操作符的使用
1.點操作符的使用情況
直接使用
? struct st
{
?? ?int a;
?? ?int b;
};
int main1()
{
?? ?struct st s = { .a = 10 , .b=20 };
?? ?printf("%d %d ", s.a, s.b);
?? ?return 0;
}
2.->的使用情況
由柔性數組的實例情況使用可知,在"結構體+指針"的情況下如果要使用指針對結構體中某一個成員變量進行訪問,那么就是"指針->結構體塵緣變量名=程序員想要賦的值".
封面如下