C語言全方位講解指針與地址和數(shù)組函數(shù)堆空間的關(guān)系_第1頁
C語言全方位講解指針與地址和數(shù)組函數(shù)堆空間的關(guān)系_第2頁
C語言全方位講解指針與地址和數(shù)組函數(shù)堆空間的關(guān)系_第3頁
C語言全方位講解指針與地址和數(shù)組函數(shù)堆空間的關(guān)系_第4頁
C語言全方位講解指針與地址和數(shù)組函數(shù)堆空間的關(guān)系_第5頁
已閱讀5頁,還剩19頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第C語言全方位講解指針與地址和數(shù)組函數(shù)堆空間的關(guān)系目錄一、一種特殊的變量-指針二、深入理解指針與地址三、指針與數(shù)組(上)四、指針與數(shù)組(下)五、指針與函數(shù)六、指針與堆空間七、指針專題經(jīng)典問題剖析

一、一種特殊的變量-指針

指針是C語言中的變量

因?yàn)槭亲兞?,所以用于保存具體值特殊之處,指針保存的值是內(nèi)存中的地址內(nèi)存地址是什么

內(nèi)存是計(jì)算機(jī)中的存儲(chǔ)部件,每個(gè)存儲(chǔ)單元有固定唯一的編號(hào)內(nèi)存中存儲(chǔ)單元的編號(hào)即內(nèi)存地址

需要弄清楚的事實(shí)

程序中的一切元素都存在于內(nèi)存中,因此,可通過內(nèi)存地址訪問程序元素。

內(nèi)存示例

獲取地址

C語言中通過操作符獲取程序元素的地址可獲取變量,數(shù)組,函數(shù)的起始地址內(nèi)存地址的本質(zhì)是一個(gè)無符號(hào)整數(shù)(4字節(jié)或8字節(jié))

下面看一個(gè)簡單的例子:

#includestdio.h

intmain()

intvar=0;

printf("varvalue=%d\n",var);

printf("varaddress=%p\n",var);

return0;

}

下面為輸出結(jié)果:

注意事項(xiàng)

只有通過內(nèi)存地址+長度才能確定一個(gè)變量中保存的值。

指針定義語法:

type*point;

type-數(shù)據(jù)類型,決定訪問內(nèi)存時(shí)的長度范圍*標(biāo)志,意味著定義一個(gè)指針變量pointer變量名,遵循C語言命名規(guī)則

例如:

intmain()

char*pChar;

short*pShort;

int*pInt;

float*pFloat;

double*pDouble;

return0;

}

指針內(nèi)存訪問:

*pointer

指針訪問操作符(*)作用于指針變量即可訪問內(nèi)存數(shù)據(jù)指針的類型決定通過地址訪問內(nèi)存時(shí)的長度范圍指針的類型統(tǒng)一占用4字節(jié)或8字節(jié)

即:sizeof(type*)==4或sizeof(type*)==8

下面看一段代碼,感受一下:

#includestdio.h

intmain()

intvar=0;

intanother=0;

int*pVar=NULL;

printf("1.var=%d\n",var);

printf("1.pVar=%p\n",pVar);

pVar=var;//使用指針保存變量的地址

*pVar=100;//*pVar等價(jià)于var,var=100;

printf("2.var=%d\n",var);

printf("2.pVar=%p\n",pVar);

pVar=another;//改變了pVar的指向,使得pVar保存another的地址

*pVar=1000;//another=1000;

printf("3.another=%d\n",another);

printf("3.pVar=%p\n",pVar);

printf("4.add==%d\n",var+another+*pVar);//100+1000+1000==2100

return0;

}

下面為輸出結(jié)果:

注意NULL地址為00000000

小結(jié)

指針是C語言中的變量(本質(zhì)為容器)指針專用于保存程序元素的內(nèi)存地址可使用*操作符通過指針訪問程序元素本身指針也有類型,指針類型由數(shù)據(jù)類型+*構(gòu)成

二、深入理解指針與地址

靈魂三問

指針類型和普通類型之間的關(guān)系是什么何看待內(nèi)存地址+長度才能訪問內(nèi)存中的數(shù)據(jù)不同類型的指針可以相互賦值嗎

初學(xué)指針的軍規(guī)

Type*類型的指針只保存Type類型變量的地址禁止不同類型的指針相互賦值禁止將普通數(shù)值當(dāng)作地址賦值給指針

注意:指針保存的地址必須是有效地址

下面看一段代碼:

#includestdio.h

intmain()

inti=10;

floatf=10;

int*pi=//WARNING

float*pf=//OK

printf("pi=%p,pf=%p\n",pi,pf);

printf("*pi=%d,*pf=%f\n",*pi,*pf);

pi=i;//WARNING

*pi=110;//OOPS

printf("pi=%p,*pi=%d\n",pi,*pi);

return0;

}

下面為輸出結(jié)果:

這個(gè)程序犯了兩個(gè)錯(cuò)誤:

1、將不同類型的指針相互賦值,雖然int類型的指針變量保存的地址是對(duì)的,但是其所保存的值是錯(cuò)的。

2、將普通數(shù)值當(dāng)作地址賦值給指針,這會(huì)導(dǎo)致嚴(yán)重的錯(cuò)誤,不能正確輸出

編寫函數(shù)交換兩個(gè)變量的值

想要編寫函數(shù)交換變量的值,那么,必須有能力在函數(shù)內(nèi)部修改函數(shù)外部的變量!!!

看下面的代碼:

#includestdio.h

voidfunc(int*p)

*p=100;//修改內(nèi)存中4字節(jié)的數(shù)據(jù),即:修改一個(gè)整型變量的值

voidswap(int*pa,int*pb)

intt=0;

t=*pa;

*pa=*pb;

*pb=t;

intmain()

intvar=0;

inta=1,b=2;

printf("1.var=%d\n",var);

func(var);

printf("2.var=%d\n",var);

printf("3.a=%d,b=%d\n",a,b);

swap(a,

printf("4.a=%d,b=%d\n",a,b);

return0;

}

下面為輸出結(jié)果:

小結(jié)論

可以利用指針從函數(shù)中返回多個(gè)值(return只能返回一個(gè)值)!!

下面看一段代碼:

#includestdio.h

intcalculate(intn,longlong*pa,longlong*pm)

intret=1;

if((1=n)(n=20))

inti=0;

*pa=0;

*pm=1;

for(i=1;ii++)

*pa=*pa+i;

*pm=*pm*i;

else

ret=0;

returnret;

intmain()

longlongar=0;

longlongmr=0;

if(calculate(5,ar,mr))

printf("ar=%lld,mr=%lld\n",ar,mr);

return0;

}

下面為輸出結(jié)果:

這段代碼中的子函數(shù)通過指針,計(jì)算了1加到5以及1乘到5的值,這就間接地通過指針從子函數(shù)返回多個(gè)值

小結(jié)

指針是變量,因此賦值時(shí)必須保證類型相同

指針變量保存的地址必須是有效地址

通過指針參數(shù)

一能夠?qū)崿F(xiàn)函數(shù)交換變量的值一能夠從函數(shù)中返回多個(gè)值

三、指針與數(shù)組(上)

問題

數(shù)組的本質(zhì)是一片連續(xù)的內(nèi)存,那么,數(shù)組的地址是什么?如何獲取

一些事實(shí)

使用取地址操作符獲取數(shù)組的地址數(shù)組名可看作一個(gè)指針,代表數(shù)組中0元素的地址當(dāng)指針指向數(shù)組元素時(shí),可進(jìn)行指針運(yùn)算(指針移動(dòng))

深入理解數(shù)組地址(inta[]={1,2,3,4,5};)

a與a在數(shù)值上相同,但是意義上不同a代表數(shù)組地址,類型為:int(*)[5]a代表數(shù)組0號(hào)元素地址,類型為:int*指向數(shù)組的指針:int(*pName)[5]=

下面看一段代碼:

#includestdio.h

intmain()

inta[]={1,2,3,4,0};

int*p=a;//a的類型為int*,a[0]==int*

int(*pa)[5]=

printf("%p,%p\n",p,a);

p++;

*p=100;//a[1]=100;

printf("%d,%d\n",*p,a[1]);

printf("%p,%p\n",a,a);

p=pa;//WARNING!!!!

p=a;

while(*p)

printf("%d\n",*p);

p++;

return0;

}

下面為運(yùn)行結(jié)果:

需要注意的是,p和pa不是一個(gè)指針類型,所以令p=pa這種做法是不正確的。

注意

數(shù)組名并不是指針,只是代表了0號(hào)元素的地址,因此可以當(dāng)作指針使用。

四、指針與數(shù)組(下)

指針與數(shù)組的等價(jià)用法

假如:

inta[]={1,2,3,4,5}

int*p=a;

則以下等價(jià):

a[i]--*(a+i)--*(p+i)--p[i]

下面看一段代碼,加深理解:

#includestdio.h

intmain()

inta[]={1,2,3,4,5};

int*p=a;

inti=0;

//a[i]==*(a+i)==*(p+i)==p[i]

for(i=0;ii++)

printf("%d,%d\n",a[i],*(a+i));

printf("\n");

for(i=0;ii++)

printf("%d,%d\n",a[i],p[i]);

printf("\n");

for(i=0;ii++)

printf("%d,%d\n",p[i],*(p+i));

printf("\n");

printf("a=%p,p=%p\n",a,p);

printf("a=%p,p=%p\n",a,

return0;

}

下面為輸出結(jié)果:

這里可以看到a和p的地址不同,因?yàn)樗鼈兪莾蓚€(gè)不同的指針變量。

字符串拾遺

字符串常量是char*類型,一種指針類型

指針移動(dòng)組合拳:

intv=*p++;

解讀:

指針訪問操作符(*)和自增運(yùn)算操作符(++)優(yōu)先級(jí)相同

所以,先從p指向的內(nèi)存中取值,然后p進(jìn)行移動(dòng)

等價(jià)于:

intv=*p;

p++;

下面看一段代碼,體會(huì)一下:

#includestdio.h

intmain()

inta[]={1,2,3};

int*p=a;

intv=*p++;

char*s=NULL;

printf("%p\n","D.T.Software");

printf("%p\n","D.T.Software");

printf("v=%d,*p=%d\n",v,*p);

printf("First=%c\n",*"D.T.Software");

s="D.T.Software";

while(*s)printf("%c",*s++);

printf("\n");

return0;

}

下面為輸出結(jié)果:

因?yàn)镈.T.Software在全局?jǐn)?shù)據(jù)區(qū)的起始地址一樣,所以兩次打印出來的地址一樣。

小結(jié)

數(shù)組名可看作一個(gè)指針,代表數(shù)組中0元素的地址a與a在數(shù)值上相同,但是意義上不同C語言中的字符串常量的類型是char*當(dāng)指針指向數(shù)組元素時(shí),才能進(jìn)行指針運(yùn)算

五、指針與函數(shù)

問題

函數(shù)調(diào)用時(shí)會(huì)跳轉(zhuǎn)到函數(shù)體對(duì)應(yīng)的代碼處執(zhí)行,那么,如何知道函數(shù)體代碼的具體位置

深入函數(shù)之旅

函數(shù)的本質(zhì)是一段內(nèi)存中的代碼(占用一片連續(xù)內(nèi)存)函數(shù)擁有類型,函數(shù)類型由返回類型和參數(shù)類型列表組成例:

函數(shù)申明類型intsum(intn);int(int)voidswap(int*pa,int*pb)void(int*,int*)voidg(void);void(void)

函數(shù)的一些事實(shí)

函數(shù)名就是函數(shù)體代碼的起始地址(函數(shù)入口地址)通過函數(shù)名調(diào)用函數(shù),本質(zhì)為指定具體地址的跳轉(zhuǎn)執(zhí)行因此,可定義指針,保存函數(shù)入口地址

函數(shù)指針(Typefunc(Type1a,Type2b))

函數(shù)名即函數(shù)入口地址,類型為Type(*)(Type1,Type2)對(duì)于func的函數(shù),func與func數(shù)值相同,意義相同指向函數(shù)的指針:Type(*pFunc)(Type1,Type2)=func;

函數(shù)指針參數(shù)

函數(shù)指針的本質(zhì)還是指針(變量,保存內(nèi)存地址)可定義函數(shù)指針參數(shù),使用相同代碼實(shí)現(xiàn)不同功能

注意

函數(shù)指針只是單純的保存函數(shù)的入口地址

因此

只能通過函數(shù)指針調(diào)用目標(biāo)函數(shù)不能進(jìn)行指針移動(dòng)(指針運(yùn)算)

下面看一段代碼,理解一下:

#includestdio.h

intadd(inta,intb)

returna+b;

intmul(inta,intb)

returna*b;

intcalculate(inta[],intlen,int(*cal)(int,int))

intret=a[0];

inti=0;

for(i=1;ii++)

ret=cal(ret,a[i]);

returnret;

intmain()

inta[]={1,2,3,4,5};

int(*pFunc)(int,int)=NULL;

pFunc=add;

printf("%d\n",pFunc(1,2));

printf("%d\n",(*pFunc)(3,4));

pFunc=mul;

printf("%d\n",pFunc(5,6));

printf("%d\n",(*pFunc)(7,8));

printf("1+...+5=%d\n",calculate(a,5,add));

printf("1*...*5=%d\n",calculate(a,5,mul));

return0;

}

下面為輸出結(jié)果:

這里注意,只有調(diào)用的時(shí)候,才能確定calculate()子函數(shù)中的cal是什么函數(shù)。

再論數(shù)組參數(shù)

函數(shù)的數(shù)組形參退化為指針!因此,不包含數(shù)組實(shí)參的長度信息。使用數(shù)組名調(diào)用時(shí),傳遞的是0號(hào)元素的地址。

voidfunc(inta[])--voidfunc(int*a)

--voidfunc(inta[1])

--voidfunc(inta[10)

--voidfunc(inta[100)

下面看一段代碼:

#includestdio.h

intdemo(intarr[],intlen)//intdemo(int*arr,intlen)

intret=0;

inti=0;

printf("demo:sizeof(arr)=%d\n",sizeof(arr));

while(ilen)

ret+=*arr++;

i++;

returnret;

intmain()

inta[]={1,2,3,4,5};

//intv=*a++;

printf("returnvalue:%d\n",demo(a,5));

return0;

}

下面為輸出結(jié)果:

定義的形參arr[]可以進(jìn)行*arr++的操作,這就說明函數(shù)的數(shù)組形參退化為指針,因?yàn)閿?shù)組不可以進(jìn)行++的運(yùn)算。

小結(jié)

函數(shù)名的本質(zhì)是函數(shù)體的入口地址函數(shù)類型由返回類型和參數(shù)類型列表組成可定義指向函數(shù)的指針:Type(*pFunc)(Type1,Type2);函數(shù)指針只是單純的保存函數(shù)的入口地址(不能進(jìn)行指針運(yùn)算)

六、指針與堆空間

再論內(nèi)存空間

內(nèi)存區(qū)域不同,用途不同

全局?jǐn)?shù)據(jù)區(qū):存放全局變量,靜態(tài)變量??臻g:存放函數(shù)參數(shù),局部變量堆空間:用于動(dòng)態(tài)創(chuàng)建變量(數(shù)組)

堆空間的本質(zhì)

備用的內(nèi)存?zhèn)}庫,以字節(jié)為單位預(yù)留的可用內(nèi)存程序可在需要時(shí)從倉庫中申請(qǐng)使用內(nèi)存(動(dòng)態(tài)借)當(dāng)不需要再使用申請(qǐng)的內(nèi)存時(shí),需要及時(shí)歸還(動(dòng)態(tài)還)

問題

如何從堆空間申請(qǐng)內(nèi)存如何歸還

預(yù)備知識(shí)--void*

void類型是基礎(chǔ)類型,對(duì)應(yīng)的指針類型為void*void*是指針類型,其指針變量能夠保存地址通過void*指針無法獲取內(nèi)存中的數(shù)據(jù)(無長度信息)

void*總結(jié)

不可使用void*指針直接獲取內(nèi)存數(shù)據(jù)。void*指針可與其它數(shù)據(jù)指針相互賦值。

下面看一段代碼:

#includestdio.h

intmain()

charc=0;

inti=0;

floatf=2.0f;

doubled=3.0;

void*p=NULL;

double*pd=NULL;

int*pi=NULL;

/*void*指針可以保存任意類型的地址*/

p=

p=

p=

p=

printf("%p\n",p);

//void*類型的指針無法訪問內(nèi)存中的數(shù)據(jù)

//printf("%f\n",*p);

/*void*類型的變量可以直接合法的賦值給其他具體數(shù)據(jù)類型的指針變量*/

pd=p;

pi=p;

//void*是例外,其他指針類型的變量不能相互賦值

//pd=pi;

return0;

}

下面為輸出結(jié)果:

注意幾個(gè)問題:

1.void*指針可以保存任意類型的地址

2.void*類型的指針無法訪問內(nèi)存中的數(shù)據(jù)

3.void*類型的變量可以直接合法的賦值給其他具體數(shù)據(jù)類型的指針變量

4.void*是例外,其他指針類型的變量不能相互賦值

堆空間的使用

工具箱:stdlib.h申請(qǐng):void*malloc(unsignedbytes)歸還:voidfree(void*p)

堆空間的使用原則

有借有還,再借不難(杜絕只申請(qǐng),不歸還)malloc申請(qǐng)內(nèi)存后,應(yīng)該判斷是否申請(qǐng)成功free只能釋放申請(qǐng)到的內(nèi)存,且不可多次釋放(free釋放的是堆空間的地址)

下面看一段代碼感受一下:

#includestdio.h

#includestdlib.h

intmain()

int*p=malloc(4);//從堆空間申請(qǐng)4個(gè)字節(jié)當(dāng)作int類型的變量使用

if(p!=NULL)//如果申請(qǐng)失敗p為0,即:空值

*p=100;

printf("%d\n",*p);

free(p);

p=malloc(4*sizeof(int));

if(p!=NULL)

inti=0;

for(i=0;ii++)

p[i]=i*10;

for(i=0;ii++)

printf("%d\n",p[i]);

free(p);

return0;

}

下面為輸出結(jié)果:

小結(jié)

堆空間是程序中預(yù)留且可用的內(nèi)存區(qū)域void*指針只能能夠保存地址,但無法獲取內(nèi)存數(shù)據(jù)void*指針可與其它數(shù)據(jù)指針相互賦值malloc申請(qǐng)內(nèi)存后,應(yīng)該判斷是否申請(qǐng)成功free只能釋放申請(qǐng)到的內(nèi)存,且不可多次釋放

七、指針專題經(jīng)典問題剖析

多級(jí)指針

可以定義指針的指針保存其它指針變量的地址

如:

Typev;

Type*pv=

Type**ppv=

type***pppv=ppv;

下面看一段代碼:

#includestdio.h

#includestdlib.h

intmain()

inta=0;

intb=1;

int*p=

int**pp=

**pp=2;//a=2;

*pp=//p=

*p=3;//b=3;

printf("a=%d,b=%d\n",a,b);

return0;

}

下面為輸出結(jié)果:

*pp就是取pp里面的內(nèi)容,而pp里面存的內(nèi)容是p的地址,所以*pp就相當(dāng)于p的內(nèi)容,而p的內(nèi)容就是a的地址,所以說**p就相當(dāng)于a,**p=2也就是把2賦值給a,*pp=b即為p=b,所以*p=3,就是把3賦值給b。

下面再看一段代碼:

#includestdio.h

#includestdlib.h

intgetDouble(double**pp,unsignedn)

intret=0;

double*pd=malloc(sizeof(double)*n);

if(pd!=NULL)

printf("pd=%p\n",pd);

*pp=pd;

ret=1;

returnret;

intmain()

double*p=NULL;

if(getDouble(p,5))

printf("p=%p\n",p);

free

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論