長沙優(yōu)化官網(wǎng)公司滁州網(wǎng)站seo
一、什么是內(nèi)存的動態(tài)分配
全局變量分配在內(nèi)存中的靜態(tài)存儲區(qū)。局部變量(包括形參)分配在內(nèi)存中的動態(tài)存儲區(qū),這個存儲區(qū)是一個稱為棧的區(qū)域。除此之外,C語言還允許建立內(nèi)存動態(tài)分配區(qū)域,以存放一些臨時用的數(shù)據(jù),這些數(shù)據(jù)不必在程序的聲明部分定義,也不必等到函數(shù)結(jié)束時才釋放,而是需要時隨時開辟,不需要時隨時釋放。這些數(shù)據(jù)是存放在一個特別的自由存儲區(qū),稱為堆區(qū)??梢愿鶕?jù)需要向系統(tǒng)申請所需大小的空間。由于未在聲明部分定義它們?yōu)樽兞炕驍?shù)組,因此不能通過變量名或數(shù)組名去引用這些數(shù)據(jù),只能通過指針來引用。
也就是說,全局變量分配于靜態(tài)存儲區(qū),局部變量分配于棧,動態(tài)內(nèi)存分配在堆。
二、怎樣建立內(nèi)存的動態(tài)分配
對于內(nèi)存的動態(tài)分配是通過系統(tǒng)提供的庫函數(shù)來實現(xiàn)的,主要有malloc,calloc,realloc,free這四個函數(shù)。
1.用malloc函數(shù)開辟動態(tài)內(nèi)存
malloc的函數(shù)原型為:
void *malloc(unsigned int size);
malloc函數(shù)的作用是:
在內(nèi)存的動態(tài)存儲區(qū)中分配一個長度為size的連續(xù)空間。形參size的類型定為無符號整型(不允許為負數(shù))。此函數(shù)的值(即“返回值”)是所分配區(qū)域的第一個字節(jié)的首地址,或者說,此函數(shù)是一個指針型函數(shù),返回的指針指向該分配域的第一個字節(jié)。如:
malloc(100);
//開辟100字節(jié)的臨時分配域,函數(shù)值為其第一個字節(jié)的地址。
注意指針的基類型為void,即不指向任何類型的數(shù)據(jù),只提供一個地址。如果此函數(shù)未能成功執(zhí)行(例如內(nèi)存空間不足),則返回空指針(NULL),失敗只有一種情況,就是申請的內(nèi)容太大,超出堆能提供的最大連續(xù)空間。
2.用calloc函數(shù)開辟動態(tài)內(nèi)存
calloc的函數(shù)原型為:
void*calloc(unsigned n,unsigned size);
calloc的函數(shù)的作用是:
在內(nèi)存的動態(tài)存儲區(qū)中分配n個長度為size的連續(xù)空間,這個空間一般比較大,足以保存一個數(shù)組。
用calloc函數(shù)可以為一維數(shù)組開辟動態(tài)內(nèi)存存儲空間,n為數(shù)組元素個數(shù),每個元素長度為size。這就是動態(tài)數(shù)組。函數(shù)的指針指向所分配域的第一個字節(jié),此函數(shù)的值(即“返回值”)是所分配區(qū)域的第一個字節(jié)的首地址;如果不成功,返回空指針(NULL)。如:
p=calloc(50,4);
//開辟50個長度為4個字節(jié)的臨時分配域,把首地址賦給指針變量p
calloc和malloc最大的區(qū)別為:申請完空間之后calloc會將每個元素的值直接置為0。
3.用realloc函數(shù)重新分配動態(tài)內(nèi)存
realloc函數(shù)的原型:
void *realloc(void*p,unsigned int size);
參數(shù)的意義:p:舊內(nèi)存的地址;size:新申請的內(nèi)存大小,以字節(jié)為單位
realloc函數(shù)的作用是:
如果已經(jīng)通過malloc函數(shù)或calloc函數(shù)獲得了動態(tài)內(nèi)存空間,想改變其大小,可以用realloc函數(shù)重新分配。用realloc函數(shù)將p所指向的動態(tài)內(nèi)存空間的大小改變?yōu)閟ize。p的值不變。函數(shù)值成功返回時返回新的動態(tài)內(nèi)存的首地址,如果重分配不成功,返回空指針(NULL)。失敗只有一種情況就是申請的內(nèi)容太大,超出堆能提供的最大連續(xù)空間。如:
realloc(p,50);
//將p指向的動態(tài)內(nèi)存空間的大小改變?yōu)?0字節(jié)
用malloc實現(xiàn)realloc擴容:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main()
{int n = 10;int *p=(int*)malloc(n * sizeof(int));assert(p != NULL);int i;for (i = 0; i < n; i++){p[i] = i;}for (i = 0; i < n; i++){printf("%d ", p[i]);}printf("\n");//1發(fā)現(xiàn)p申請的太少,于是重新申請int*q=(int *)malloc(2* n * sizeof(int));//1申請新內(nèi)存//2搬家for (i = 0; i < 2*n; i++){q[i] =i;}for (i = 0; i < 2*n; i++){printf("%d ", q[i]);}free(p);//3釋放p的內(nèi)存p = q;//4接收新內(nèi)存q = NULL;//...后面可以繼續(xù)使用preturn 0;
}
realloc實現(xiàn)擴容:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main()
{int n = 10;int* p = (int*)malloc(n * sizeof(int));assert(p != NULL);int i;for (i = 0; i < n; i++){p[i] = i;}for (i = 0; i < n; i++){printf("%d ", p[i]);}printf("\n");//1發(fā)現(xiàn)p申請的太少,于是重新申請p = (int*)realloc(p, 2 * n * sizeof(int));for (i = 0; i < 2 * n; i++){p[i] = i;}//2搬家for (i = 0; i < 2 * n; i++){printf("%d ", p[i]);}//...后面可以繼續(xù)使用pfree(p);return 0;
}
4.用free函數(shù)釋放動態(tài)存儲區(qū)
free函數(shù)的原型:
void free(viod*p);
free函數(shù)的作用是:
釋放指針變量p所指向的動態(tài)內(nèi)存空間,使這部分空間能重新被其他變量使用。p應(yīng)是最近一次調(diào)用calloc或malloc函數(shù)時得到的函數(shù)返回值。free函數(shù)無返回值。如:
free(p);
//釋放指針變量p所指向的已分配的動態(tài)內(nèi)存空間
free崩潰的原因:
1.p移動了(p++),因為申請p的時候有一個n的大小,如果p移動,找不到頭信息
2.找不到尾信息
3.重復(fù)釋放了一段內(nèi)存
以上4個函數(shù)malloc,calloc,realloc,free的聲明都在stdlib.h的頭文件中,在用到這些函數(shù)時應(yīng)該用#include<stdlib.h>
指令把stdlib.h頭文件包含在程序文件中。
三、void指針類型
C99允許使用基類型為void的指針類型。可以定義一個基類型為void的指針變量(即void*型變量),它不指向任何類型的數(shù)據(jù)。
注意:不要把“指向void類型”理解為能指向“任何類型”的數(shù)據(jù),而應(yīng)理解為“指向空類型”或“不指向確定的類型”的數(shù)據(jù)。在將它的值賦給另一指針變量時由系統(tǒng)對它進行類型轉(zhuǎn)換,使之適合于被賦值的變量的類型。
例如:
int main()
{int a = 3; //定義a為整型變量int* p1 = &a;//p1指向int型變量char* p2;//p2指向char型變量void* p3;//p3為無類型指針變量(基類型為void型)p3 = (void*)p1;//將p1的值轉(zhuǎn)換為void*類型,然后賦值給p3p2 = (char*)p3;//將p3的值轉(zhuǎn)換為char*類型,然后賦值給p2printf("%d", *p1);//合法,輸出整型變量a的值void* p3 = &a; printf("%d", *p3);//錯誤,p3是無指向的,不能指向a
}
說明:
地址信息應(yīng)該包含位置信息和基類型的信息。 一定要注意基類型的信息,即存放存放在以此地址標(biāo)志的存儲單元中的數(shù)據(jù)類型,否則無法實現(xiàn)對數(shù)據(jù)的存取。所以對于void*
類型的指針,這種指針無指向,在這種無指向的地址所標(biāo)志的存儲單元中是不可以存儲任何數(shù)據(jù)的,也就是說無法通過這種地址對內(nèi)存存取數(shù)據(jù)。
什么情況下會用到void*類型的指針:
在調(diào)用動態(tài)存儲分配函數(shù)(如malloc、calloc、realloc函數(shù))時會用到void*
類型的指針。用戶用這些函數(shù)開辟動態(tài)存儲區(qū)時,希望獲得此動態(tài)存儲區(qū)的起始地址,以便利用該動態(tài)存儲區(qū)。
C99規(guī)定malloc、calloc、realloc函數(shù)返回void*
指針,使其“無指向”,這種指針稱為“空類型指針”,它不指向任一種具體的數(shù)據(jù)類型,只提供一個地址。這是C有關(guān)地址應(yīng)用的一種特殊情況。
這種空類型指針在形式上和其他指針一樣,遵循C語言對指針的有關(guān)規(guī)定,它也有基類型,只是它的基類型是void
,可以這樣定義:
void*p; //定義p是void*型的指針變量
void*型指針代表“無指向的地址”,這種指針不指向任何類型的數(shù)據(jù)。不能企圖通過它存取數(shù)據(jù),在程序中它只是過渡性的,只有轉(zhuǎn)換為有指向的地址,才能存取數(shù)據(jù)。
四、建立動態(tài)內(nèi)存分配區(qū)和使用void指針
【例題】建立動態(tài)數(shù)組,輸入5個學(xué)生的成績,另外用一個函數(shù)檢查其中有無低于60分的,輸出不合格的成績。
【思路】用malloc開辟一個動態(tài)自由區(qū)域,用來存放5個學(xué)生的成績,會得到這個動態(tài)域的第一個字節(jié)的地址,他的基類型是void型。用一個基類型是int的指針變量p來指向動態(tài)數(shù)組的各元素,并輸出它們的值。但必須先把malloc函數(shù)返回的void指針轉(zhuǎn)換為整型指針,然后賦給p1。
#include <stdio.h>
#include <stdlib.h> //程序中用了malloc函數(shù),應(yīng)包含stdlib.h
void check(int* p)//定義check函數(shù),形參是int*指針
{printf("不合格的成績?yōu)?#xff1a;");for (int i = 0; i < 5; i++){if (p[i] < 60)printf("%d ", p[i]);//輸出不合格成績}printf("\n");
}
int main()
{int* p1 = (int*)malloc(5 * sizeof(int)); //p1是int型指針,開辟動態(tài)內(nèi)存,將地址轉(zhuǎn)換為int*型,然后放在p1中for (int i = 0; i < 5; i++){scanf("%d", &p1[i]);//輸入5個學(xué)生的成績}check(p1);//調(diào)用check函數(shù)free(p1);return 0;
}
五、斷言assert
一種比較保守的編程方式:每一次都要驗證動態(tài)申請內(nèi)存的返回值是否為空,這里就需要用到斷言assert,assert函數(shù)的聲明都在assert.h的頭文件中,在用到這個函數(shù)時應(yīng)該用#include<assert.h>
指令把assert.h頭文件包含在程序文件中。
【例題】輸出n個連續(xù)自然數(shù)中的所有素數(shù)
void Getprimer(int n)
{int* p = (int*)malloc(n * sizeof(int));//堆assert(p != NULL);for (int i = 0; i < n; i++){p[i] = 1;}p[1] = p[0] = 0;//0和1不是素數(shù)for (int i = 2; i < n; i++){for (int j = i + 1; j < n; j++){if (j % i == 0)p[j] = 0;}}for (int i = 2; i < n; i++){if (p[i] == 1){printf("%d是素數(shù)\n", i);}}free(p);
}
int main()
{int n;printf("請輸入n的大小:");scanf("%d", &n);Getprimer(n);return 0;
}