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