目錄
前言:
幾個經(jīng)典的例題題
例一:
例二:
例三:
例四:
例五:?
?C/C++程序的內(nèi)存開辟
柔性數(shù)組
柔性數(shù)組的特點:
柔性數(shù)組的使用:?
柔性數(shù)組的代替:
柔性數(shù)組的優(yōu)勢:
小結(jié):
前言:
希望在復(fù)習(xí)完詳解C語言—動態(tài)內(nèi)存分配(一)???????,閱讀此篇文章會進一步提升你的能力!
幾個經(jīng)典的例題題
例一:
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;
}
運行之后 沒有結(jié)果,異常退出:?
我們在調(diào)試中可以看到程序發(fā)生異常。?
- 首先主函數(shù)調(diào)用Test函數(shù),Test函數(shù)聲明一個char*類型變量str對其賦值為NULL,
- 然后調(diào)用GetMemory函數(shù),將str作為參數(shù)傳入,形式參數(shù)p只是將實參str的值拷貝一份,兩者是獨立的,函數(shù)中對 p 動態(tài)分配空間100個字節(jié)空間,與str沒有關(guān)系,所以調(diào)用GetMemory函數(shù)函數(shù)之后 str 還是空指針,
- strcpy的參數(shù)是指針類型,strcpy內(nèi)部需要對指針類型參數(shù)進行解引用,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? str作為參數(shù)造成對空指針進行解引用操作,形成對空指針的非法訪問。
該程序會造成兩個問題:
- 對NULL指針進行了解引用操作,程序會崩潰。
- 沒有釋放空間,造成內(nèi)存泄漏問題。
因此我們做出以下修改:?
想要修改指針變量需要將指針變量的地址傳入函數(shù)中,函數(shù)的參數(shù)用二級指針接收,這樣在函數(shù)內(nèi)部可以對其動態(tài)分配空間。
void 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;
}
?輸出結(jié)果:
例二:
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
輸出結(jié)果:?
GetMemory函數(shù)中,p 儲存了"hello world"首地址,返回為p,當GetMemory函數(shù)調(diào)用完后,雖然str獲取到p儲存的地址,但p的空間會被銷毀,p變成了野指針,str獲取到的就是野指針。相當于非法訪問。
?如果在給p加上static聲明 p 為靜態(tài)變量,延長生命周期,就可以輸出"hello world"。
例三:
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}
輸出結(jié)果:
調(diào)用test函數(shù)時 一旦test
函數(shù)返回,變量a
的空間不會立即被刪除,但是它的生命周期結(jié)束,該內(nèi)存空間可以被系統(tǒng)重用,也就是說,該內(nèi)存空間可能會被分配給后續(xù)的函數(shù)調(diào)用或變量,該內(nèi)存可能會被其他數(shù)據(jù)覆蓋。
這次可能只是僥幸a的空間沒有占用,如果在輸出語句前再加上這樣一條這樣的輸出語句,
printf("hehe");
a的空間就被占用了,輸出結(jié)果將不再是 10 。
?輸出結(jié)果:
例四:
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;
}
輸出結(jié)果:?
看似沒有問題,但還是存在唯一的問題——忘記釋放內(nèi)存。?
free(str);
str = NULL;
例五:?
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,str變成野指針,所以判斷不為空,對其進行拷貝,但str指向的空間已被free回收了,再進行拷貝就造成非法訪問內(nèi)存。
其實用free在釋放完,要及時將str即使賦值為NULL。
?C/C++程序的內(nèi)存開辟
1. 棧區(qū)(stack):在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。 棧區(qū)主要存放運行函數(shù)而分配的局部變量、函數(shù)參數(shù)、返回數(shù)據(jù)、返回地址等。2. 堆區(qū)(heap):一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時可能由OS回收 。分配方式類似于鏈表。3. 數(shù)據(jù)段(靜態(tài)區(qū))(static)存放全局變量、靜態(tài)數(shù)據(jù)。程序結(jié)束后由系統(tǒng)釋放。4. 代碼段:存放函數(shù)體(類成員函數(shù)和全局函數(shù))的二進制代碼。
實際上普通的局部變量是在棧區(qū)分配空間的,棧區(qū)的特點是在上面創(chuàng)建的變量出了作用域就銷毀。但是被static修飾的變量存放在數(shù)據(jù)段(靜態(tài)區(qū)),數(shù)據(jù)段的特點是在上面創(chuàng)建的變量,直到程序結(jié)束才銷毀,所以生命周期變長。
柔性數(shù)組
struct s
{
int n;
int arr[];//柔性數(shù)組
};
struct s
{
int n;
int arr[0];//柔性數(shù)組
};
柔性數(shù)組的特點:
- 結(jié)構(gòu)中的柔性數(shù)組成員前面必須至少一個其他成員。
- sizeof 返回的這種結(jié)構(gòu)大小不包括柔性數(shù)組的內(nèi)存。
- 包含柔性數(shù)組成員的結(jié)構(gòu)用malloc ()函數(shù)進行內(nèi)存的動態(tài)分配,并且分配的內(nèi)存應(yīng)該大于結(jié)構(gòu)的大小,以適應(yīng)柔性數(shù)組的預(yù)期大小。
柔性數(shù)組的使用:?
struct s
{
int n;
int arr[0];
};
int main()
{
//printf("%d\n", sizeof(struct S));
struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);
if (ps == NULL)
{
perror("malloc");
return 1;
}
ps->n = 100;
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i + 1;
}
free(ps);
ps = NULL;
return 0;
}
?如需增容則加上realloc函數(shù)進行增容:
int main()
{
//printf("%d\n", sizeof(struct S));
struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);
if (ps == NULL)
{
perror("malloc");
return 1;
}
ps->n = 100;
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i + 1;
}
//空間不夠,需要增容
struct S* ptr = realloc(ps, sizeof(struct S) + 60);
if (ptr == NULL)
{
perror("realloc");
return 1;
}
ps = ptr;
ps->n = 15;
for (i = 0; i < 15; i++)
{
printf("%d\n", ps->arr[i]);
}
//釋放
free(ps);
ps = NULL;
return 0;
}
柔性數(shù)組的代替:
我們可以用指針類型的結(jié)構(gòu)體成員代替柔性數(shù)組:?文章來源:http://www.zghlxwxcb.cn/news/detail-721282.html
struct S
{
int n;
int* arr;
};
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S));
if (ps == NULL)
{
perror("malloc->ps");
return 1;
}
ps->n = 100;
ps->arr = (int*)malloc(40);//1 2 3 4 5 6 7 8 9 10
if (ps->arr == NULL)
{
perror("malloc->arr");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i + 1;
}
//調(diào)整
int* ptr = (int*)realloc(ps->arr, 60);
if (ptr != NULL)
{
ps->arr = ptr;
}
else
{
perror("realloc");
return 1;
}
//打印
for (i = 0; i < 15; i++)
{
printf("%d\n", ps->arr[i]);
}
//釋放
free(ps->arr);
ps->arr = NULL;
free(ps);
ps = NULL;
return 0;
}
柔性數(shù)組的優(yōu)勢:
如果我們的代碼是在一個給別人用的函數(shù)中,你在里面做了二次內(nèi)存分配,并把整個結(jié)構(gòu)體返回給用戶。用戶調(diào)用free可以釋放結(jié)構(gòu)體,但是用戶并不知道這個結(jié)構(gòu)體內(nèi)的成員也需要free,所以你不能指望用戶來發(fā)現(xiàn)這個事。所以,如果我們把結(jié)構(gòu)體的內(nèi)存以及其成員要的內(nèi)存一次性分配好了,并返回給用戶一個結(jié)構(gòu)體指針,用戶做一次free就可以把所有的內(nèi)存也給釋放掉。
連續(xù)的內(nèi)存有益于提高訪問速度,也有益于減少內(nèi)存碎片。(其實,我個人覺得也沒多高了,反正你跑不了要用做偏移量的加法來尋址)
小結(jié):
?學(xué)習(xí)之路道阻且長,希望大家堅持復(fù)習(xí),堅持敲代碼,未來的你們一定會收到心儀的offer?。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-721282.html
到了這里,關(guān)于詳解C語言—動態(tài)內(nèi)存分配(二)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!