🌟菜鳥主頁:@晨非辰的主頁
👀學習專欄:《C語言學習》
💪學習階段:C語言方向初學者
?名言欣賞:"暴力解法是上帝給的,優化解法是魔鬼教的。"
目錄
1. 字符指針變量
1.1 使用方式
1.2 例題解釋
2. 數組指針變量
2.1 數組指針變量定義????????
?2.2 數組指針變量的初始化
3. 二維數組傳參本質
4. 函數指針變量
4.1 函數指針變量的創建
4.2 函數指針變量的使用
4.3 有趣的代碼
4.3.1?typedef關鍵詞
5. 函數指針數組
1. 字符指針變量
1.1 使用方式
--已經知道有一種指針類型為字符指針:char*,一般有兩種使用方式:
int main()
{//第一種char ch = 'w';char* pc = &ch;//第二種char arr[] = "abcdef";char* pc = arr;return 0;
}
--當然還有另外方式:
int main()
{const char* pc = "abcdef";//這里代表把整個字符串放進了指針嘛?//結果顯而易見,字符串為常量字符串,只是將首字符的地址存放printf("%c\n", *pc);//打印aprintf("%s\n", pc);//打印abcdef//%s打印字符串需要的是地址才能找到下一個字符,所以用pcreturn 0;
}
--對于方式3和方式2對比,方式3略過了數組,這就導致了無法通過*pc來改變字符串,也就是上面說的常量字符串。
1.2 例題解釋
--經典筆試題:
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("str1 and str2 are same\n");//1elseprintf("str1 and str2 are not same\n")//2;if (str3 == str4)printf("str3 and str4 are same\n");//3elseprintf("str3 and str4 are not same\n");//4return 0;
}
--代碼打印;2、3;
????????--首先請注意,根據博客指針(三)的內容,這里比較的都是數組首元素地址而不是數組內容;所以因為str1與str2雖然內容相同但為不同數組,首元素地址自然也不同;
? ? ? ? --對于str3與str4,都指向同一個字符串"abcdef"且為常量字符串,內容不會被修改,C/C++會把常量字符串存儲到單獨的一個內存區域,當幾個指針指向同一個字符串時,他們實際會指向同一塊內存,所以str3和str4相同。
--特別注意,比較字符串內容要使用strcmp函數
2. 數組指針變量
2.1 數組指針變量定義????????
--在上一篇博客,學習了指針數組,數組存放的是指針;那下面也進行類比:
- 整型指針變量:int* p,存放的整型變量地址,指向整型數據;
- 字符指針變量;char* p,存放的字符型變量地址(字符串,存放的是首字符地址),指向字符型數據
--可知,數組指針存放的是數組地址,指向數組數據;
--下面對數據指針變量進行辨析:
1. int* p1[10]; --首先p1與[10]結合,那么p1就成了數組名,這也就是前面學的指針數組;
2. int(*p2)[10]; --首先p2與*結合,p2成為指針變量名,[10]代表指針所指向的是大小為10的數組;所以是數組指針。(必須確保加上(),使p先于和*結合)
?2.2 數組指針變量的初始化
--數組指針變量用來存放數組地址,那么初始化就要獲取數組地址:&數組名。
int(*p) [10] = &arr; --&arr得到數組地址
--數組類型解釋:
int? (*p)? [10]? =? &arr
? |? ? ? |? ? ? ? |
? |? ? ? |? ? ?p指向數組的元素個數
? |? ? p為指針變量名
p指向的數組的元素類型
3. 二維數組傳參本質
--認識了數組指針,下面來理解二維數組是如何傳參的吧:
//構建函數
void test(int arr[][5], int a, int b)
{int i = 0;int j = 0;for (i = 0; i < a; i++){for (j = 0; j < b; j++){printf("%d ", arr[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} };//調用函數打印test(arr, 3, 5);//將數組名,行列數傳過去return 0;
}
--哎,在前面說一維數組傳參時,形參可以寫成數組也可以寫成指針,那二位可以嗎?
????????--首先來看二維數組,可以看成每個元素都是一維數組,也就是每一行就是一個一維數組:
?
?--所以根據數組名含義來說,二維數組的數組名代表的是首元素地址也就是第一行這個一維數組的地址:類型就是int? [5]、數組指針類型是int(*) [5]。
--就意味著二維數組傳參本質上也是傳遞地址,傳遞的是第一行?維數組地址,那么形參也是可以寫成指針形式的:
void test(int(*arr) [5], int a, int b)
{int i = 0;int j = 0;for (i = 0; i < a; i++){for (j = 0; j < b; j++){printf("%d ", *(*(arr+i)+j));//*(arr + i) 解引用得到 arr[i](第 i 行的數組名)//+j再解引用是訪問一維數組的內容,等價于arr[][]}printf("\n");}
}int main()
{//定義出數組int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };//調用函數打印test(arr, 3, 5);//將數組名,行列數傳過去return 0;
總結:二維數組傳參,形參的部分可以寫成數組,也可以寫成指針形式。
4. 函數指針變量
4.1 函數指針變量的創建
--同樣類比其他指針,函數指針就是存放函數地址的,通過地址調用函數,那函數地址怎么獲取呢?
void test()
{printf("hehe\n");
}
int main()
{printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;
}
? ? ? ? --可見函數是有地址的,函數名就是地址(與數組不一樣)。
--下面就要開始創建變量來存放地址:
int* test(int n, char* p)
{(...);
}int* (*pf)(int, char*) = test;int Add(int x, int y)
{return x + y;
}//參數可寫可不寫
int(*pf)(int, int) = Add;
--函數指針類型圖解:
4.2 函數指針變量的使用
--通過函數指針調用指針指向的函數
int Add(int x, int y)
{return x + y;
}
int main()
{int(*pf)(int, int) = Add;printf("%d\n", (*pf)(10, 20));printf("%d\n", pf(10, 20));//解不解引用都可以,因為pf在這里等價于Addreturn 0;
}
--? 注意:C 語言標準規定,函數指針的解引用會自動轉換回函數地址,因此 *pf 仍然等同于 pf。
4.3 有趣的代碼
--代碼1
(* (void (*)())0)();
--解釋:這段代碼是在調用0地址處的函數
- ?void(*)()是一個函數指針類型,這個指針指向的函數沒有參數,返回類型void;
- ?(void (*)())0?是將0強轉為這種函數指針類型,意味著0處有這么一個函數;
- (* (void (*)())0)();對0地址進行解引用,調用函數;
--來自《C陷阱和缺陷》
?--代碼2
void (*signal (int , void(*)(int)) ) (int);
--解釋:是一次函數聲明,函數名叫signal
- signal函數有兩個參數,第一個參數是int類型,第二個參數是函數指針類型?void(*)(int),該指針指向的函數參數是int,返回類型是void;
- signal函數的返回類型也是一個函數指針類型?void(*)(int) ,指針指向的函數參數是int,返回類型是void;
- 直觀表達:void *?(int)?signal (int , void(*)(int)) ;//但是不能這么寫
--來自《C陷阱和缺陷》
4.3.1?typedef關鍵詞
--顯而易見,typedef用來類型重命名的,可以將復雜的類型簡單化。
?--比如:
unsigned int 太長不方便,可以用關鍵詞重定義為uint :typedef??unsigned?int??uint;
? ? ? ? ?--指針類型也可以簡化命名的,將int *重命名
typedef??int*? ptr_t;
? ? ? ? --但是對于數組指針和函數指針就有不同:
? ? ? ?--?數組指針類型?int (*) [5] ,需要重命名為parr_t,要這樣寫:
typedef int(*parr_t)[5]; --新的類型名必須在*的右邊
? ? ? ? --函數指針類型重命名是一樣的,將?void(*) (int)?類型重命名為pfun_t,可以這樣寫:
typedef void(*pfun_t)(int); --新的類型名必須在*的右邊
?--那么為了更好理解上面的代碼2,這樣命名:
typedef void(*pfun_t)(int); ——> pfun_t signal(int, pfun_t);
5. 函數指針數組
--在上一篇博客分享了指針數組,那么把函數的地址放到數組中,就成為了函數指針數組,如何定義呢?
int (*parr1[3])();
?--parr1先和 [ ] 結合,說明parr1是數組,內容是 int (*)()類型的指針;
--對于函數指針的用途:轉移表,小子會在下一篇進行分享,千萬別急~~?
往期復習:
1.?#C語言——學習攻略:深挖指針路線(一)--指針變量、地址、意義與指針運算
2.?#C語言——學習攻略:深挖指針路線(二)--const修飾、野指針分析、斷言和指針的作用
3.?#C語言——學習攻略:深挖指針路線(三)--數組與指針的結合、冒泡排序
結語:本篇內容就到這里了,主要分享了指針變量類型的一些內容,后續仍會分享指針的相關知識;指針的內容需要反復研讀 ,如果這篇文章對你的學習有幫助的話,歡迎一起討論學習,你這么帥、這么美給個三連吧~~~