進階的內容比較龐大,但請各位耐心看完,我相信一定會有所識訓,如果我有錯誤的地方希望大家可以指正我的錯誤,萬分感謝
防止有些博友沒有看過前一篇我的博客,在此簡單概括一下那篇博客的內容有哪些,
- 指標變數是用來存放地址的,對其解參考就可以找到該地址唯一標識的記憶體空間
- 指標大小在32位平臺上為4位元組,在64位平臺上為8位元組
- 指標具有型別之分,指標型別決定了其進行解參考操作時的訪問權限及+/-整數的步幅
- 指標的幾種運算(指標加減整數,指標減指標,指標關系運算)
本文章主要內容
- 1.字符指標
- 2.指標陣列
- 3.陣列指標
- 4.陣列傳參和指標傳參
- 5.函式指標
- 6.函式指標陣列
- 7.指向函式指標陣列的指標
- 8.回呼函式
1.字符指標
我們知道字符指標的使用方法為
#include<stdio.h>
int main()
{
char a='0';
char *p=&a;
*p='a';
return 0;
}
那下面這種是什么意思呢
#include<stdio.h>
int main()
{
char *p="Thanks for browsing";
return 0;
}
我們知道char*只能存一個元素的地址,但怎么可以和一個字串相等呢?

有次我們看到,原來上面這個代碼的意思是存放該字串首元素的地址(即‘T’的地址存放在p中)
那么我們就可以這樣使用它了

但是這樣寫有個小問題,這里的"Thanks for browsing"和存放于陣列中的字串不同,是不可更改的,被稱為常量字串,它在記憶體里只會存盤一份,
不理解只存一份的可以點我哦
2.指標陣列
初階已介紹指標陣列,即存放指標的陣列就是指標陣列
在此就不再贅述,主要是為了區分下面的陣列指標
int *arr1[10];//存放一級整形指標的陣列
int **arr2[10];//存放二級整形指標的陣列
3.陣列指標
陣列指標重點說的是指標
例如整形指標是指向整形的指標、字符指標是指向字符的指標
那么陣列指標就是指向陣列的指標
int arr[10]={0};
int (*p1)[10]=&arr;
int *p2=arr;
//*p代表這是個指標,因為[]比*的結合性要高,會與指標陣列產生歧義,所以需要加上()
//即int (*p1)[10]=&arr的完整解釋為p是一個指向有十個整形元素的指標
&arr是指整個陣列的首地址
arr是指首元素的地址
兩者雖數值上相同,但加1后移動的步幅不同!(如下圖)

陣列名只有在兩種情況下是指整個陣列的首地址
- &arr
- sizeof(arr)
除此之外陣列名均指代陣列首元素地址
接下來我們來介紹一下陣列指標的使用方法
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
int(*p)[10] = &arr;
for (i = 0; i < 10; i++)
{
printf("%d ", (*p)[i]);
printf("%d ", *((*p)+i));
printf("%d ", (*p)[0][i]);
//以上三種均可找到陣列中的元素
//*p等價于arr,即arr[i]==(*p)[i]
//&具有‘升維’的作用,*具有‘降維’的作用
}
return 0;
}

#include<stdio.h>
void print(int (*p)[5],int r,int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c;j++)
{
printf("%d ", *(*(p+i)+j));
//p+i相當于&arr+i 第i行的地址
//*(p+i)+j相當于arr+j 第j個的地址
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { { 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 } };
print(arr, 3, 5);//二維陣列傳參,傳的是第一行的地址
//傳引數也會發生降維
//傳二維陣列,函式得到的為第一行的地址
//傳一維陣列,函式得到的為首元素地址
return 0;
}
這里做一個小練習,可以測驗一下自己是否真的明白了指標陣列和陣列指標
int arr[5]; 有五個整形元素的陣列
int *p[5]; 有五個整形指標的陣列
int (*p)[5]; 指向有五個整形元素的陣列的指標
int (*p[5])[10]; 存放五個指向有十個整形元素的陣列的指標的陣列
//p先和[]結合成為陣列,然后拆解為int (*)[10],
//現在即使陣列中的元素型別,每個元素為指向有十個整形元素的陣列指標
4.陣列傳參和指標傳參
一維陣列傳參
void test1(int arr[])
{}//該方法就是普通的陣列傳參
void test2(int arr[10])
{}//該方法與test1相同,10并沒有實際意義,只是形式上感覺上下匹配
void test3(int *p)
{}//該方法就是利用了陣列傳參會發生降維,就利用整形指標接收
void test4(int *p[10])
{}//同test2
void test5(int **p)
{}//傳的元素為一級指標,所以可以用二級指標接收
int main()
{
int arr[10] = { 0 };
int *p[10] = { 0 };
test1(arr);
test2(arr);
test3(arr);
test4(p);
test5(p);
return 0;
}
二維陣列傳參
void test1(int arr[3][5])
{}//該方式可行
void test2(int arr[][5])
{}//該方法可行
void test3(int arr[][])
{}//該方法不可行,對二維陣列而言,可以不知道行數,但必須知道列數
void test4(int *arr)
{}//該方法不可行經降維后得到是一維陣列的地址,而此處卻用整形指標接收
void test5(int* arr[])
{}//該方法不可行,這里用的是指標陣列接收,要想用陣列接收只能用上面兩種
void test6(int (*p)[5])
{}//該方法可行,利用陣列指標指向一個一維陣列
void test7(int **p)
{}//該方法不可行,該方法是為了接收一級指標,利用指標接收只能用上面這種
int main()
{
int arr[3][5] = { 0 };
test1(arr);
test2(arr);
test3(arr);
test4(arr);
test5(arr);
test6(arr);
test7(arr);
return 0;
}
我們知道一級指標傳值,可以用一級指標接收
那么,反過來想想,都有哪些傳過去可以用指標接收呢?
void test(int *p)
{}
int main()
{
int a = 10;
int *pa = &a;
int arr[5] = { 0 };
test(&a);
test(pa);
test(arr);
//傳參基本原則:傳過去的引數和形參型別相同
return 0;
}
同理,二級指標呢?
void test(int **p)
{}
int main()
{
int a = 10;
int *pa = &a;
int **ppa = &pa;
int *arr[10] = { 0 };
test(ppa);
test(&pa);
test(arr);
return 0;
}
5.函式指標
#include<stdio.h>
int test(int x,int y)
{return x*y;}
int main()
{
int a=0;
int *pa=&a;//指向整形的指標
int arr[10]={0};
int (*parr)[10]=&arr;//指向陣列的指標
int (*pf)(int ,int)=&test;//指向函式的指標
//函式回傳值型別 引數型別 函式名取地址即可獲得函式地址
test==&test 兩者相同,上面可替換,這里可沒有函式首元素一說
函式指標如何呼叫函式呢?
int b=(*pf)(2,3);//和正常使用指標是類似的,只需解參考即可使用
return 0;
}
但上面已經寫過,將test賦給pf,那么是否意味著pf也可以和test一樣直接呼叫函式呢?
答案是肯定的,可以,pf前面加星號是為了初學者可以更快了解C語言語法,其實 并未起到解參考的用處,但如果寫成(pf)(2,3)的形式就一定要帶括號,否則意思就變成了對呼叫函式的結果解參考

現在我們已經初步認識函式指標,接下來我們來看看兩句很“有趣”的代碼
1.(*(void(*)())0)()
- 這個的確很不好看明白,但首先我們知道void(*)()是一個不需要引數且回傳值為空的函式指標型別,而 ()0就是將0強制型別轉換為函式指標型別,接著就是解參考呼叫0地址處的函式,該函式無引數,回傳型別為void,
(該代碼源自C陷阱和缺陷)

2.void(* signal(int, void(*)(int) ) )(int);
- 首先要肯定的是這是一個函式宣告,signal是函式名,需要整形和函式指標(該函式指標引數為int,回傳值型別為void)兩個引數,signal的回傳值型別為void(* )(int)(該函式指標引數為int,回傳值型別為void),
上面這個其實不容易理解,所以我們可以利用typedef讓它變得好看一點
typedef void(*new_pf)(int) ;
注意new_pf為新名字,void(*)(int)為舊名字
這樣寫是語法規定,不可以寫成typedef void(*)(int) new_pf;
所以上面的函式宣告就可以寫成 new_pf signal(int,new_pf);
6.函式指標陣列
這個應該可以舉一反三了吧,函式指標陣列即存放函式指標的陣列
#include<stdio.h>
void test1()
{
printf("hello\n");
}
void test2()
{
printf("world\n");
}
int main()
{
void(*pt1)() = test1;
void(*pt2)() = test2;
void(*parr[2])() = (test1, test2);
//函式指標陣列其實就是在函式指標的名字后面加 [元素個數]
return 0;
}
運用函式指標陣列很經典的案例函式指標陣列實作計算器(可以點進來了解一下)
7.指向函式指標陣列的指標
函式指標陣列終歸是個陣列,既然是陣列,那就可以用指標指向它
int (*p)(int ,int);//函式指標
int (*parr[5])(int ,int);//函式指標陣列
int (*(*pparr)[5])(int ,int)= &parr;//指向函式指標陣列的指標
(*pparr)證明是個指標,[5]說明指向的是個陣列,元素型別為int (*)(int ,int)
8.回呼函式
注意重點來了
回呼函式就是一個通過函式指標呼叫的函式,如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回呼函式,回呼函式不是由該函式的實作方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用于對該事件或條件進行相應,
舉個例子
void test1()
{
printf("hello");
}
void test2(void (*p)())
{
p();
}
int main()
{
test2(test1);
并沒有主動呼叫test1,而是將test1地址傳給test2,在適當情況下test2呼叫test1
此時test1被稱為回呼函式
return 0;
}
如果想加深一下對回呼函式的理解可以點下面兩個鏈接
回呼函式計算器
快速排序的使用(qsort就是一個很好的回呼函式)
大家好,上次的初階版讓我第一次感受到了寫博客喜悅,原來可以有這么多人看我的博客呀,非常感謝大家對我的支持與鼓勵,我一定會再接再厲,繼續創作出優質的內容來向大家分享知識,——2021.3.23
今天終于把它磨出來了,雖然有些小細節還是可能還需更改,但內容上還是很充實的,非常感謝點進來的每個人,——2021.4.4
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/272539.html
標籤:其他
下一篇:從0到1實戰微服務架構
