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