對指針的初步認知
這裏主要就是簡單總結一下在C初級階段我們接觸的指針
- 指針是對內存地址的編號,用來存放地址,地址唯一標識一塊內存空間。
- 指針的大小是固定的4或8個字節(32位平臺/64位平臺)。
32位/64位指的是計算機CPU中通用寄存器單位時間能處理的二進制的位數,以32位平臺爲例,會產生32根地址線,每根地址線會有0和1兩種可能,32個0或1就是32個bit,也就是4個byte。
- 指針也是有類型的,指針的類型決定了指針±整數的步長,指針解引用操作的權限。
- 還有關於一些指針簡單的運算。
指針可不止這些內容歐,還有更多的知識等着我們去學習,接下來讓我們一起了解一下指針的高級主題。
對指針的深度剖析
字符指針
在指針的類型中我們知道有一種指針類型爲字符指針char*
一般使用:
char ch = 'w';
char *pc = &ch;
*pc = 'w';
還有一種使用方法:
char* pstr = "hello world";
注意:這段代碼的意思是把這一個常量字符串的首字符地址存放到指針變量pstr中
讓我們做一個小小的練習:
//數組
char str1[] = "hello world";
char str2[] = "hello world";
//指針
char *str3[] = "hello world";
char *str4[] = "hello world";
str1 == str2(錯誤)
用相同常量字符串初始化不同數組時,就會開闢不同的內存塊,所以str1 == str2錯誤。
str3 == str4(正確)
指針指向同一字符串的時候,實際指向同一內存。
指針數組 VS 數組指針
指針數組是數組還是指針?數組指針是指針還是數組?他們有什麼區別呢?你能很好的區分它們嗎?讓我們一起來看看吧!
指針數組
指針數組是一個存放指針的 數組!數組!數組!它的每個元素都爲指針,用來存放指針。
int* arr1[10],char* arr[20],char** arr[30]
數組指針
數組指針是一個指向數組的 指針! 指針!指針!它指向數組。
int (*p)[10]
:[ ]的優先級高於✳,所以必須加上()使 p先和 ✳結合,p爲一個指針變量,指向的是一個大小爲10的整型數組。
數組指針的使用
既然數組指針指向的是數組,那麼數組指針中存放的應該是數組的地址
。
話不多說我們看代碼:
void test(int (*arr)[5])
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}
顯而易見,數組指針用於接收二維數組的傳參,因爲二維數組的首元素爲第一行(一維數組),數組指針來接收這個一維數組的地址。
瞭解了指針數組和數組指針我們來一起看看下面代碼的意思
int (*p[10])[5]
p先和[ ]結合表示數組,這個數組有10個元素,每個元素的類型爲int (*)[5],即每個元素的類型爲數組指針,int (*p[10])[5] 表示p數組裏有10個元素,每個元素的類型都是數組指針,數組指針指向數組的地址,被指向的這個數組裏有5個整型元素。
即爲存放數組指針的數組。
數組名 VS &數組名
int arr[10];
雖然arr和&arr的值是一樣的,但是意義不一樣。
arr:數組首元素的地址,arr+1跳過該指針類型的大小。
&arr:數組的地址,&arr+1跳過整個數組的大小。
數組參數
在實際問題中我們有時會把數組傳給函數,那麼函數的參數因該如何設計呢?
一維數組傳參
arr1中首元素地址是 int *
arr2中首元素地址是 int **
void test1(int *arr)
void test1(int arr[10])
void test1(int arr[])
void test2(int **arr)
void test2(int *arr[20])
int main()
{
int arr1[10] = {0};
int *arr2[20] = {0};
test1(arr1);
test2(arr2);
}
二維數組傳參
上面我們已經說到,二維數組傳參用數組指針接收。
void test(int (*arr)[5]);
void test(int arr[][5]);
int main()
{
int arr[3][5] = {0};
test(arr);
}
sizeof(arr) = 3×5×4 = 60,sizeof(數組名)求數組的大小
sizeof(arr+1) = 4,arr爲首元素地址,類型爲數組指針int (*)[5]
sizeof(arr[2]) =4×5= 20,二維數組第三行 爲一維數組,類型int*
函數指針
經過上面對數組指針的瞭解,我們類比學習函數指針,函數指針是一個指向函數的指針!指針!指針!
void test()
{
printf("welcome");
}
int main()
{
printf("%p\n",test);
printf("%p\n",&test);
return 0;
}
輸出:
013211DB
013211DB
這段代碼輸出的是兩個地址,這連個地址是test函數的地址,我們要向將函數的地址保存起來,怎麼保存?
首先能儲存地址,就要求得是一個指針
即void (*pfun)()這個函數指針來存放函數的地址。
pfun1先和*結合,說明它是一個指針,指針指向的是一個函數,指向的函數返回值是void,無參數。
如果想調用一個函數: 1.函數指針解引用(*pfun)2.用函數名直接調用(test())
函數指針數組
顧名思義,函數指針數組是一個數組,數組中每個元素都是一個函數指針。這個數組中存儲了函數的地址。
函數指針數組的定義int (*parr[10])();
parr先和[ ]結合,說明parr是一個數組,數組的內容是什麼的呢?是int(*)()類型的函數指針。
用途:轉移表
這裏函數指針數組中存儲的是 add,sub,mul,div相對應的加減乘除函數的指針
int(*p[5])(int x,int y) = {0,add,sub,mul,div}
調用時
ret = (*p[input])(x,y);
這樣做可以大大的減少代碼冗餘,不用switch case語句那樣繁瑣。
指向函數指針數組的指針
指向函數指針數組的指針,首先它是一個指針,指向一個數組,數組的每個元素都是一個函數指針。
函數指針:
void (*pfun)(const char*) = test;
函數指針數組:
void (*pfunArr[5])(const char* str)
指向函數指針數組的指針:
void (*(*ppfunArr)[10])(const char*) = &pfunArr
回調函數
回調函數,回調回調就是經過這個函數之後,到了某個特定的條件返回去再調用它。普通函數就是順序調用。
說的官方一點:回調函數就是一個通過函數指針調用的函數,把函數指針的地址作爲參數傳遞給另一個函數。
函數的實現:
這裏將函數int_cmp_less的地址傳給函數bubble_sort,用函數指針來接收。
void bubble_sort(int*arr, int sz,int(*pf_com)(int,int))
這個指針就會被用來調用所指向的函數
if (pf_com(arr[i - 1],arr[i]) > 0)
函數的構建:
int int_cmp_less(int a, int b){//a小於b返回大於0
return b - a;
}
main函數中:
bubble_sort(arr, sizeof(arr) / sizeof(arr[0]),int_cmp_less);
到這裏我們的Blog就結束了,經過這幾個主題的學習和練習,相信我們對指針有了更深層次的理解,在C和C++中指針這個主題是永遠不會褪色的,只有對指針有了更深入的理解,我們才能對C和C++語言有更深的理解。