- 相關(guān)推薦
C語言中各種類型的指針的概念總結(jié)
指針可以說是C語言本身所具有的最大特性,平時(shí)根據(jù)不同使用場合習(xí)慣地將其簡單分類,下面是小編為大家整理的C語言中各種類型的指針的概念總結(jié),希望對大家有所幫助。
C語言中各種類型的指針的概念總結(jié)
1.什么是指針
假設(shè)我們要去訪問一個(gè)變量,有兩種方式:1.通過變量名訪問。2.通過地址訪問。
int a = 10;
printf("a = %d",a); //1.通過變量名訪問
printf("a =%d",*(&a)); //2.通過地址訪問
//取值運(yùn)算符:把后面內(nèi)存地址中的數(shù)據(jù)取出來
有了上面的例子,我們引入指針的概念。我們用最粗暴的方法來理解什么是指針,指針就是地址。什么是指針變量?存放地址的變量就是指針變量。指針變量可以類比跟我們之前學(xué)的整形變量,字符變量等,都是屬于變量的一種。
2.指針基礎(chǔ)內(nèi)容
。1)如何定義指針
我們用指針標(biāo)識符*來定義,*的作用是告訴系統(tǒng)這是一個(gè)指針變量,用來存放別人的地址。
注意:這個(gè)*只有在定義指針變量的時(shí)候才是指針標(biāo)識符,其它時(shí)候是運(yùn)算符的意思。
int a;
int *p;
*p = &a;
printf("a =%d",*(&a));
printf("a =%d",*p);
。2)指針變量對變量類型的要求
為什么會(huì)在定義指針變量的時(shí)候要特別注意數(shù)據(jù)類型,拿下面的demo為例比來講解。定義一個(gè)int型變量a,這個(gè)變量由于是一個(gè)int型的所以大小為4個(gè)字節(jié),也就是32位。同樣,如果用char類型來定義這個(gè)指針變量的話就是只用了一個(gè)字節(jié),就是八位。這樣就會(huì)出現(xiàn)一個(gè)問題,就是這個(gè)1個(gè)字節(jié)內(nèi)存地址放不下一個(gè)4個(gè)字節(jié)大小的變量。編譯器可能會(huì)報(bào)錯(cuò),即使不報(bào)錯(cuò)打印出來的數(shù)據(jù)也是不完整的。
#include
int main()
{
int a = 0x1234;
int *p = &a;
char *q = &a; //這樣定義是錯(cuò)誤的
printf("a = %x",*p);
printf("a = %x",*q);
return 0;
}
。3)練習(xí):指針實(shí)戰(zhàn)之交換數(shù)據(jù)大小
下面這個(gè)demo的作用是用指針的方式交換兩個(gè)數(shù)據(jù)的大小,通過地址訪問到變量之后再進(jìn)行交換。
#include
void changedata(int *p,int *q)
{
int tmp;
tmp = *p;
*p = *q;
*q = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("交換前:a=%d,b=%d",a,b);
changedata(&a,&b);
printf("交換后a=%d,b=%d",a,b);
return 0;
}
運(yùn)行結(jié)果:
3.通過指針引用數(shù)組
(1)定義一個(gè)指針變量指向數(shù)組
如何獲取一個(gè)數(shù)組的首地址,通常有兩種方式:
1.數(shù)組中首個(gè)元素的地址就是數(shù)組的首地址。
2.數(shù)組名就是數(shù)組的首地址。下面用代碼表示一下這兩種方式。
int main()
{
int arrary[3] = {1,2,3};
int *p;
p = &arrary[0]; //1.數(shù)組中首個(gè)元素的地址就是數(shù)組的首地址。
p = arrary; //2.數(shù)組名就是數(shù)組的首地址。
return 0;
}
。2)指針偏移遍歷數(shù)組
我們訪問了首元素的地址之后便可以訪問到該數(shù)組中的每一個(gè)元素。具體如何訪問看下面這個(gè)demo。這里要特別注意一點(diǎn) *(p+i)這里的每次+1不是地址的加一,而是偏移了一個(gè)類型的大小。比如你是int型的指針就偏移4個(gè)字節(jié),是char類型的就偏移一個(gè)字節(jié)。
用指針的方式訪問數(shù)組在效率上是遠(yuǎn)遠(yuǎn)大于用數(shù)組名訪問數(shù)組的。
#include
int main()
{
int i;
int arr[3] = {1,2,3};
int *p;
p = arr;
for(i=0;i<3;i++){
printf("%d ",*(p+i)); //這里的+1不是地址的加一,而是偏移了一個(gè)類型的大小
}
return 0;
}
運(yùn)行結(jié)果:
。3)練習(xí):指針與數(shù)組的結(jié)合
實(shí)驗(yàn)效果:輸入五個(gè)數(shù),并依次打印出來
#include
void InputArr(int *parr,int size)
{
int i;
for(i=0;i
printf("請輸入");
scanf("%d",parr);
parr++;
}
}
void PrintArr(int *parr,int size)
{
int i;
for(i=0;i
printf("%d ",*parr);
parr++;
}
}
int main()
{
int arrary[5];
int size;
size = sizeof(arrary)/sizeof(arrary[0]);
InputArr(arrary,size);
PrintArr(arrary,size);
return 0;
}
運(yùn)行結(jié)果:
4.二維數(shù)組的地址
為了研究二維數(shù)組的地址,我們可以把二維數(shù)組看作成“父子數(shù)組”(C語言并沒有這個(gè)概念,只是方便理解起的名字而已)。有了“父子數(shù)組”的概念,我們就可以把二維數(shù)組拆成兩個(gè)一維數(shù)組來理解。“父數(shù)組”和“子數(shù)組”都可以看作是兩個(gè)一維數(shù)組,只不過“父數(shù)組”比較特殊,它里面的每一項(xiàng)又是一個(gè)數(shù)組。我們知道數(shù)組名就是數(shù)組的首地址這個(gè)概念,如果定義一個(gè)二維數(shù)組int a[3][4],那么a就是數(shù)組名,也就是“父數(shù)組”的名字,接下來我們就要想辦法得出“子數(shù)組”的數(shù)組名,獲得了數(shù)組名也就獲得了地址,這個(gè)也是研究二維數(shù)組地址的重中之重。
現(xiàn)在定義一個(gè)二維數(shù)組int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}。
找個(gè)數(shù)組a是一個(gè)包含了三個(gè)元素的數(shù)組(也可以看作是包含了三個(gè)“子數(shù)組”),并且每一個(gè)元素又是一個(gè)數(shù)組。于是這里的a[0],a[1],a[2]就是代表了數(shù)組的名字,而中[ ]里面的“0,1,2”代表的也不是數(shù)組的大小。a[0],a[1],a[2]分別是這三個(gè)一維數(shù)組的名字,因此子數(shù)組的名字分別為a[0],a[1],a[2],也可以叫做是列地址,a就是行地址。
所以現(xiàn)在可以思考這樣一個(gè)問題,a+1和a[0]+1分別代表什么含義?
a+1偏移的是整個(gè)數(shù)組,也就是說從a[0]偏移到a[1],它偏移了一行。
a[0]+1只是從一個(gè)數(shù)組的某個(gè)元素偏移到了下一個(gè)元素,它偏移了一列。
5.指針數(shù)組
指針數(shù)組:多個(gè)指針,叫做指針數(shù)組,數(shù)組中的每一項(xiàng)都是一個(gè)指針變量。
int a = 2;
int b = 3;
int c = 4;
int *arrary[3]; //指針數(shù)組
arrary[1] = &a; //數(shù)組中的每一項(xiàng)存放的都是指針變量
arrary[2] = &b;
arrary[3] = &c; //三個(gè)普通沒有任何關(guān)系的整形變量的地址存入指針數(shù)組
6.數(shù)組指針
數(shù)組指針相比于指針數(shù)組最大的區(qū)別就是它是一個(gè)指針,指向的是數(shù)組。而數(shù)組指針是多個(gè)指針的集合。
數(shù)組指針,強(qiáng)調(diào)的是類型,數(shù)組的個(gè)數(shù),偏移值是偏移了整個(gè)數(shù)組的大小。
int arrary[3] = {3,4,5};
int (*p)[3]; //數(shù)組指針,強(qiáng)調(diào)的是類型,數(shù)組的個(gè)數(shù),偏移值是偏移了整個(gè)數(shù)組的大小。
p = arrary;
7.函數(shù)指針
我們知道普通變量有兩種訪問方式,一種是通過變量名訪問,還有一種是通過指針訪問。同樣函數(shù)也用通過函數(shù)名和指針來調(diào)用。
#include
void printwelcome()
{
printf("welcome");
}
int main()
{
void (*p)(); //定義一個(gè)函數(shù)指針
p = printwelcome; //給指針賦值
p(); //通過函數(shù)指針調(diào)用函數(shù)
(*p)();
return 0;
}
如何定義一個(gè)函數(shù)指針?
void(*p)();
1.如何表示指針:*
2.如何指知道是函數(shù):()
3.函數(shù)指針是專用的,格式要求很強(qiáng)(參數(shù)類型,個(gè)數(shù),返回值)。
如何給指針賦值?
p = printwelcome;
函數(shù)名就是地址名,就像數(shù)組一樣,數(shù)組名就是地址。
如何通過函數(shù)指針調(diào)用函數(shù)?
1.直接通過指針名字加括號:p();
2.取內(nèi)容。(*指針名字)+():(*p)();
函數(shù)指針練習(xí):有兩個(gè)整數(shù)a和b,用戶輸入1,2或3。如果輸入1則給出a,b中的最大值,如果輸入2,則給出a,b中的最小值,輸入3則輸出a+b的和。
#include
int getmax(int data1,int data2)
{
return data1>data2 ? data1:data2;
}
int getmin(int data1,int data2)
{
return data1
}
int getsum(int data1,int data2)
{
return data1+data2;
}
int datahandler(int data1,int data2,int (*pfunc)(int data1,int data2))
{
int ret;
ret = (*pfunc)(data1,data2);
return ret;
}
int main()
{
int a = 10;
int b = 20;
int cmd;
int ret;
int (*pfunc)(int data1,int data2);
printf("請輸入1最大值,輸入2取最小值,輸入3求和");
scanf("%d",&cmd);
switch(cmd){
case 1:
pfunc = getmax;
break;
case 2:
pfunc = getmin;
break;
case 3:
pfunc = getsum;
break;
default:
printf("輸入錯(cuò)誤");
break;
}
ret = datahandler(a,b,pfunc);
printf("輸出結(jié)果為%d",ret);
return 0;
}
8.無類型指針
函數(shù)原型:void *malloc(size_t size);
1.void作為函數(shù)返回值的時(shí)候是無類型返回值(無類型不代表沒有返回值),void*在這里同樣是返回的是無類型的指針。
2.size_t 類型表示C中任何對象所能達(dá)到的最大長度,它是無符號整數(shù)。(其值是大于等于0的)
3.malloc:中文叫動(dòng)態(tài)內(nèi)存分配,用于申請一塊連續(xù)的指定大小的內(nèi)存塊區(qū)域以void*類型返回分配的內(nèi)存區(qū)域地址。
用下面的demo加深一下理解(用malloc開辟空間遍歷數(shù)組)。
#include
#include
int main()
{
int i,n;
int *arrary = (int *)malloc(n*sizeof(int)); //將void型指針強(qiáng)轉(zhuǎn)成int型指針
printf("請輸入開辟個(gè)數(shù)");
scanf("%d",&n);
for(i=0;i
printf("請輸入第%d個(gè)數(shù)",i+1);
scanf("%d",&arrary[i]);
}
for(i=0;i
printf("第%d個(gè)數(shù)是:%d",i+1,*arrary++);
}
return 0;
}
9.二級指針
對二級指針的理解是和一級指針是一樣的,一級指針存放的是普通變量的,而二級指針存放的是指針變量的地址。
#include
int main()
{
int data = 10;
int *p = &data; //定義一級指針
int **pp = &p; //定義二級指針
printf("通過一級指針訪問:data=%d",*p);
printf("通過二級指針訪問:data=%d",**pp);
printf("p的地址是%p",p);
printf("pp的地址是%p",pp);
return 0;
}
用圖像理解上面的代碼就如下圖,多級指針是相對的概念,都是一級指向另一級層層遞進(jìn)的。
10.內(nèi)存泄漏
什么是內(nèi)存泄漏?
內(nèi)存泄漏最主要的體現(xiàn)是,程序剛跑起來的時(shí)候很好,跑了幾小時(shí),跑了幾天或者幾周,程序崩潰了。
這種情況一般來說就是不斷地開辟內(nèi)存空間,最后超過了你計(jì)算機(jī)的內(nèi)存,然后就崩潰了。用malloc開辟的空間,程序不會(huì)主動(dòng)釋放。Linux系統(tǒng)中,程序結(jié)束,系統(tǒng)會(huì)回收這個(gè)空間。
如何避免內(nèi)存泄漏?
1.注意循環(huán)中有沒有一直申請空間。
2.有沒有及時(shí)合理釋放。(用free釋放)
11.為什么要用指針
。1)可以將變量強(qiáng)制保存在我想要的地址中。
平時(shí)我們定義一個(gè)變量int a = 10;這個(gè)a的地址是系統(tǒng)隨機(jī)分配的,那么如果我們想在指定的地址去定義這個(gè)變量該如何操作,這個(gè)時(shí)候就可以用到指針。
例:在內(nèi)存空間0077ff00中定義int a = 10。
int *p = (int *)0077ff00;
*p = 10;
通過(int *)的操作強(qiáng)制給*p這個(gè)指針變量附上一個(gè)int型的地址,然后再通過*p=10給變量附上值,這個(gè)操作就做完了。
。2)通過被調(diào)函數(shù)修改調(diào)用函數(shù)的對象。
還記得上面我們寫過一個(gè)交換數(shù)值大小的demo嗎?
如果我把紅框中的函數(shù)改成以下這個(gè)樣子,想一想還能否完成數(shù)值交換的操作。很顯然是不行的,因?yàn)樾薷暮蟮暮瘮?shù)在定義參數(shù)時(shí)開辟的內(nèi)存空間與主函數(shù)在調(diào)用時(shí)所應(yīng)定義的變量開辟的內(nèi)存空間是完全兩個(gè)不一樣的空間。說白了,如果把整個(gè)程序比作是一個(gè)大樓的話,被調(diào)函數(shù)為變量開辟的空間在301房間,而在主函數(shù)定義的變量開辟的空間在403房間,301房間的人根本控制不了在403房間的人,因此傳參這種方式無效。
什么是有效的傳參?通過指針,對變量地址進(jìn)行操作,讓指定地址中的變量值進(jìn)行交換。
【C語言中各種類型的指針的概念總結(jié)】相關(guān)文章:
C語言中各種類型的指針的概念02-14
C語言中指針的概念03-16
C語言中各種類型指針的特性與用法介紹04-17
C語言指針的概念02-25
C語言中的指針解讀04-08
什么是C語言中指針 C語言指針的基礎(chǔ)使用04-19
C語言指針數(shù)組的概念07-19
C語言中的指針指什么03-30
C語言中的指針是什么08-08