目錄
創(chuàng)新互聯(lián)是一家專業(yè)提供南樂企業(yè)網(wǎng)站建設,專注與成都網(wǎng)站制作、成都做網(wǎng)站、H5開發(fā)、小程序制作等業(yè)務。10年已為南樂眾多企業(yè)、政府機構(gòu)等服務。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站制作公司優(yōu)惠進行中。指針數(shù)組
數(shù)組指針
arr與&arr的區(qū)別
數(shù)組指針
1.數(shù)組指針的創(chuàng)建格式:
2.數(shù)組指針的用處
數(shù)組參數(shù),指針參數(shù)
一維數(shù)組傳參
二維數(shù)組傳參(注意事項)
一級指針傳參
二級指針傳參
函數(shù)指針
函數(shù)指針數(shù)組
函數(shù)指針真正用處用法
指針數(shù)組是指在一組數(shù)中能夠存放多個相同指針類型的一組數(shù)
指針數(shù)組的書寫格式:
當然還可以類比出三級指針,四級指針等等等等....
指針的初始化:
如int* arr[5]={0};
在這里我們將數(shù)組arr里的每一個元素賦了0,也就相當于將指針數(shù)組里面的所有元素都變?yōu)榱丝罩羔?,這是因為在編譯器中,NULL相當于0被強制類型轉(zhuǎn)換為了(void*)類型,所以NULL本質(zhì)上就是0。
數(shù)組指針數(shù)組指針是一種指針類型,是一個指向數(shù)組的指針。
數(shù)組指針的書寫格式:
#includeint main()
{
int arr[5]={0};
char s[5]={0};
int (*p)[5]=&arr;//這個就是數(shù)組指針的格式,
//也可以用其他類型如char等
char (*b)[5]=&s;
return 0;
}
arr與&arr的區(qū)別創(chuàng)造一個int型數(shù)組int arr[5]={0}; 我們都知道arr是數(shù)組名,也就是首元素的地址,但&arr表示的是什么?
在編譯器中輸入以下代碼:
#includeint main()
{
int arr[5] = { 0 };
printf("%p\n", arr);
printf("%p\n",arr + 1);
printf("%p\n", &arr);
printf("%p\n", &arr + 1);
return 0;
}
最終輸出結(jié)果如下:
觀察上面的輸出后的圖,我們可以看到arr與&arr的地址是相同的,但arr與&arr其實是兩個完全不同的概念。
我們可以看到,arr+1后的地址與arr的地址相差了4,這是因為arr是數(shù)組名,相當于存放了數(shù)組首元素的地址,也就是一個int*類型的指針,所以每加一后都會直接跳過四個字節(jié),讀取到數(shù)組中下一個元素。而我們再看向&arr與&arr+1的地址,經(jīng)過計算后得到兩地址相差的數(shù)值大小為20,我們會發(fā)現(xiàn),這剛好是我們創(chuàng)建數(shù)組的所占的總共空間大?。?0個字節(jié))。
也就是說,&arr其實取到的并不是數(shù)組首元素地址,類型并不為int*指針,而取到的是整個數(shù)組的地址,類型為數(shù)組指針,所以當我們加一的時候,由于是數(shù)組指針加一,所以會跳過20個字節(jié)。那么數(shù)組指針具體又是什么呢?
數(shù)組指針就像int型會有int指針,char類型有char指針,數(shù)組也會有對應的數(shù)組指針,例如int型數(shù)組,數(shù)組指針的書寫格式就為int(*)[],具體用法如下:
數(shù)組指針的應用
1.數(shù)組指針的創(chuàng)建格式:2.數(shù)組指針的用處Ⅰ:訪問一維數(shù)組
#includeint main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int(*p)[10] = &arr;
int i = 0;
for (i = 0; i< 10; i++)
{
printf("%d ",*((*p) + i));
}
return 0;
}
這里雖然訪問一維數(shù)組用這種方法繁冗且不建議使用,但還是做下解釋。
Ⅱ:訪問二維數(shù)組
如果要寫一個函數(shù),將遍歷二維數(shù)組將每一個元素打印出來,我們會怎么寫呢?
在以前會使用下面的方法:
#includevoid print(int a[3][5], int x, int y)
{
int i = 0;
int j = 0;
for (i = 0; i< x; i++)
{
for (j = 0; j< y; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
print(arr, 3, 5);
return 0;
}
但現(xiàn)在,我們可以用數(shù)組指針的方式來實現(xiàn):
#includevoid print(int(*p)[5], int x, int y)
{
int i = 0;
int j = 0;
for (i = 0; i< x; i++)
{
for (j = 0; j< y; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
print(arr, 3, 5);
return 0;
}
在二維數(shù)組中例如arr[3][5],其中的“[3]”所代表的是有3行元素,“[5]”代表的是每行元素有5列。而arr[5]則代表一行的地址
所以當我們在傳參時,只需要將形參定為int型的指針數(shù)組,先將每一行的地址傳入,就可以實現(xiàn)遍歷數(shù)組的作用。
再次理解:
#includeint main()
{
int a[10] = { 0 };
int* p = a;
//*(a+i)==*(p+i)==a[i]==p[i]
}
數(shù)組參數(shù),指針參數(shù)
一維數(shù)組傳參看下面代碼,判斷傳參是否正確:
#includevoid test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
void test2(int *arr[20])//ok?
{}
void test2(int **arr)//ok?
{}
int main()
{
int arr[10]={0};
int *arr2[20]={0};
test(arr);
test2(arr2);
return 0;
}
一維數(shù)組傳參經(jīng)驗總結(jié):
所以上述代碼中的傳參都是正確的。
二維數(shù)組傳參(注意事項)看下面代碼,判斷傳參是否正確:
#includevoid test(int arr[3][5])//ok? ----1
{}
void test(int arr[][])//ok? ----2
{}
void test(int arr[][5])//ok? ----3
{}
void test(int *arr)//ok? ----4
{}
void test(int (*arr)[5])//ok? ----5
{}
void test(int **arr)//ok? ----6
{}
int main()
{
int arr[3][5]={0};
test(arr);
}
二維數(shù)組傳參經(jīng)驗總結(jié):
綜上,2,4,6是錯誤的傳參形式。
一級指針傳參Q:當函數(shù)的參數(shù)為一級指針時,函數(shù)能接收什么參數(shù)?
A:可以傳入一個地址,或數(shù)組名,或直接傳入一個一級指針。
二級指針傳參Q:當函數(shù)的參數(shù)為二級指針時,函數(shù)能接收什么參數(shù)?
A:可以傳入一級指針的地址、二級指針、指針數(shù)組的數(shù)組名。
函數(shù)指針函數(shù)不同于數(shù)組,函數(shù)名與在函數(shù)名前加上&本質(zhì)上是沒有區(qū)別的,下面是函數(shù)指針的一些使用:
#includeint Add(int x, int y)
{
return x + y;
}
void Fun(char x)
{
;
}
int main()
{
//Add與&Add本質(zhì)上沒有區(qū)別,都表示為同一個地址
printf("%p\n", Add);
printf("%p\n", &Add);
//函數(shù)指針的創(chuàng)建舉例
int (*p)(int, int) = Add;
int (*pa)(int, int) = &Add;
void (*ppa)(char) = Fun;
//函數(shù)指針的調(diào)用也同函數(shù)名一樣,可以直接用名或者在前面加上解引用符
printf("%p\n", p);
printf("%p\n", *p);
//函數(shù)的調(diào)用
printf("%d\n", Add(1, 2));
printf("%d\n", (&Add)(1, 2));//這里特別注意,要將&Add用小括號括起來,否則Add就會先與后面的(1,2)結(jié)合,從而無法調(diào)用函數(shù)。
printf("%d\n", p(1, 2));
printf("%d\n", (*p)(1, 2));//*p也是先要用小括號括起來,否則會與后面的(1,2)結(jié)合,從而無法調(diào)用函數(shù)得到想得到的結(jié)果。
return 0;
}
特別注意:以上面代碼為例,若要以&Add或者*p這種形式來使用,要先用小括號括起來,否則Add/p就會先與后面的(1,2)結(jié)合,從而無法調(diào)用函數(shù)。
在學習函數(shù)指針時,又偶然發(fā)現(xiàn)了一道很經(jīng)典,非常,特別,詭異的題目:
//請分析下面這段代碼:
(*(void(*)())0)();
??????????????????????????????????????????????????????????????????????????????????????
在剛看到這段代碼時我的心情如上👆。心想:“魔法,這一定是魔法”。但其實冷靜思考過后,最終還是理解了這一段離譜且詭異的代碼:
首先從最里開始,void(*)? (),這樣單獨拆開后我們不難發(fā)現(xiàn),這是一個函數(shù)指針類型,只不過是一個void型的函數(shù)且不用傳參,在這整體上再套多一個小括號并在后面加上數(shù)字0,變?yōu)?void(*)?()?)0的樣子,這里最外一層的小括號其實是強制類型轉(zhuǎn)換符,將0這個地址上放入一個函數(shù)指針(也就是放了一個函數(shù))。而最終變?yōu)椋?(void(*)())0)(),其實是調(diào)用了0地址上的這個參數(shù)為無參,返回類型是void的函數(shù)。所以總結(jié)來說,整個代碼是在調(diào)用0地址處的函數(shù)。
但是?。。?!
當我們將上面代碼在編譯器中正式運行時是不可能的,會出現(xiàn)運行中止或報錯或根本不運行。這是因為地址0是我們普通用戶禁止被使用的地址,我們是無法使用的,因為0地址是開機啟動時才訪問的地址。
再來看一條簡單一點的代碼(可能)
void (*signal(int,void(*)(int)))(int);
上面代碼要這么看:void(* signal(int,void(*)(int)) )(int);
這樣子就不難看出(應該不難)signal(int,void(*)(int))是一個函數(shù)的聲明,因為函數(shù)的參數(shù)位置如果是調(diào)用的話,則不應該為類型,所以這個是函數(shù)的聲明,這是一個函數(shù)名為signal,參數(shù)為int型和函數(shù)指針型的函數(shù),函數(shù)名有了,參數(shù)也有了,現(xiàn)在缺少的就是返回類型,這個返回的類型便是剩下的→void ( *)(int)。
Tip:在這里要特別注意,*總是要和名字在一起,不可以寫為void (?*)(int) signal(int,void(*)(int))的形式
看吧,不難吧(后仰攤手)
什么??還是難???那好吧,下面我們嘗試著簡化一下這樣的代碼(其實我也覺得難)
將void(*)(int)這樣類型的函數(shù)指針類型重命名為pfun_t,這里要用到typedef,但注意!!不是寫為這樣:typedef void(* )(int) pfun_t。而是要寫為:typedef void(* pfun_t)(int) 這里這么寫是因為*總要與名字在一起(上面tip里有說過)。
經(jīng)過函數(shù)指針的重命名后,上面的代碼可以最終簡化為:
pfun_t signal(int,pfun_t);
按照字面意思,我們可以知道,這是一組存放函數(shù)指針類型元素的數(shù)組。
函數(shù)指針數(shù)組的使用:
#includeint Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int (*arr[5])(int, int) = { Add,Sub,Mul,Div };
int i = 0;
for (i = 0; i< 5; i++)
{
printf("%d\n",arr[i](8, 4));
}
return 0;
}
函數(shù)指針可以利用寫一個函數(shù),使其形參的類型為函數(shù)指針類型,從而達到一個函數(shù)可以調(diào)用多個不同函數(shù)的一個函數(shù)。
我們先寫下下面這樣的一段代碼:
#includevoid Menu()
{
printf("******************************\n");
printf("**** 1.Add 2.Sub ****\n");
printf("**** 3.Mul 4.Div 0.exit ****\n");
printf("******************************\n");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
do
{
int x = 0;
int y = 0;
Menu();
printf("請輸入你要的操作");
scanf("%d", &input);
switch (input)
{
case 0:
printf("結(jié)束操作\n");
break;
case 1:
printf("請輸入你想要操作的兩個數(shù):");
scanf("%d %d", &x, &y);
printf("%d\n", Add(x, y));
break;
case 2:
printf("請輸入你想要操作的兩個數(shù):");
scanf("%d %d", &x, &y);
printf("%d\n", Sub(x, y));
break;
case 3:
printf("請輸入你想要操作的兩個數(shù):");
scanf("%d %d", &x, &y);
printf("%d\n", Mul(x, y));
break;
case 4:
printf("請輸入你想要操作的兩個數(shù):");
scanf("%d %d", &x, &y);
printf("%d\n", Div(x, y));
break;
default:
printf("輸入錯誤,請重新輸入");
break;
}
} while (input);
}
在上面我們會發(fā)現(xiàn)case1,2,3,4大量代碼是重復冗余的,且Add,Sub,Mul,Div函數(shù)都是返回的int型,且都有兩個int型的參數(shù),對于這種情況,我們可以利用寫一個函數(shù),來調(diào)用這些形式上差不多相同的函數(shù),能夠做到縮減代碼量的目的,而調(diào)用這些函數(shù)以形參形式傳過去,就需要用到函數(shù)指針類型。
在這里我們創(chuàng)建一個Cal函數(shù),其參數(shù)形式為函數(shù)指針型,若以上面函數(shù)來舉例,則寫出如下代碼:
void Cal(int (*fun)(int,int))
{}
然后再將重復的代碼放入Cal函數(shù)內(nèi)之后刪去,便可以達到減少重復代碼的目的了。下面是完整的更改后的代碼:
#includevoid Menu()
{
printf("******************************\n");
printf("**** 1.Add 2.Sub ****\n");
printf("**** 3.Mul 4.Div 0.exit ****\n");
printf("******************************\n");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
void Cal(int (*fun)(int, int))
{
int x = 0;
int y = 0;
printf("請輸入你想要操作的兩個數(shù):");
scanf("%d %d", &x, &y);
printf("%d\n", fun(x, y));
}
int main()
{
int input = 0;
do
{
Menu();
printf("請輸入你要的操作");
scanf("%d", &input);
switch (input)
{
case 0:
printf("結(jié)束操作\n");
break;
case 1:
Cal(Add);
break;
case 2:
Cal(Sub);
break;
case 3:
Cal(Mul);
break;
case 4:
Cal(Div);
break;
default:
printf("輸入錯誤,請重新輸入");
break;
}
} while (input);
}
看起來比上一種寫法干凈整潔了許多。
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧
當前名稱:C語言中多種指針相關類型詳解-創(chuàng)新互聯(lián)
當前鏈接:http://www.rwnh.cn/article10/cciido.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、網(wǎng)站設計公司、靜態(tài)網(wǎng)站、ChatGPT、微信公眾號、網(wǎng)頁設計公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)