摘要:所以是數組指針,而是指針數組。因為對一個二維數組,可以不知道有多少行,但是必須知道一行多少元素。當二維數組數組名傳參,形參接收時,數組的行可以省略,列不能省略,如果省略了列,我們就無法知道當指針加減跳過幾個字節。
大家對于指針恐怕都不陌生!
沒學過C語言那也一定聽過指針吧,指針是C
最強的優勢,學不明白也就成了劣勢!大家不必害怕,指針并沒有那么恐怖,掌握了指針,讓你的C語言更上一層樓!
bug郭和你一起將指針進階學習一遍,一起加油!
可能有伙伴就要問了,咋一來就進階指針!
不要慌問題不大,bug郭之前就寫個一篇博客,介紹指針基礎知識!
有興趣的伙伴可以點擊查看C語言指針,樓下大爺都能學會的小細節(和bug郭一起學C系列),建議收藏!
大家都復習完了指針基礎吧,那我們就開始指針進階的學習吧!
指針基礎的一些概念:
4/8
個字節(32
位平臺/64
位平臺)。+-
本章重點
6. 字符指針
7. 數組指針
8. 指針數組
9. 數組傳參和指針傳參
10. 函數指針
11. 函數指針數組
12. 指向函數指針數組的指針
13. 回調函數
14. 指針和數組面試題的解析
字符指針顧名思義就是一個指針變量,指針指向的空間存放的是一個字符!
char*
字符指針
//基本用法int main(){ char ch = "c"; char* pc = &ch; *pc = "w"; return 0;}
這種基本的用法,bug就不介紹了,相信大家都會!
//進階int main(){ char* pstr = "abcdef"; //pstr字符指針存了字符串,第一個字符(a)的地址 printf("%s",pstr); return 0;}
代碼char* pstr = "abcdef";
特別容易讓我們以為是把字符串abcedf
放到字符指針pstr
里了,但是本質是把字符串abcdef
首字符的地址放到了pstr
中。
我們可以知道通過字符指針pstr
我們可以找到字符串abedef
。
為啥我們不直接創建一個字符串變量,而要用這種方式,有何不同呢?
//測試#include int main(){ char str1[] = "hello world."; char str2[] = "hello world."; char *str3 = "hello world."; char *str4 = "hello world."; if(str1 ==str2) printf("str1 and str2 are same/n"); else printf("str1 and str2 are not same/n"); if(str3 ==str4) printf("str3 and str4 are same/n"); else printf("str3 and str4 are not same/n"); return 0;}
輸出結果
可以看到,字符數組str1
和str2
不相同,因為str1
和str2
是數組名,而數組名就是第一個數組的地址。str1
和str2
分別開辟了兩個數組空間,只不過它們存放的內容一樣而已!
而str3
和str4
它們都是指向的同一塊空間,因為它們指向的是字符串常量hello world.
這里
str3
和str4
指向的是一個同一個常量字符串。C/C++
會把常量字符串存儲到多帶帶的一個內存區域,
當幾個指針。指向同一個字符串的時候,他們實際會指向同一塊內存。但是用相同的常量字符串去初始化不同的數組的時候就會開辟出不同的內存塊。所以str1
和str2
不同,str3
和str4
不同。
數組指針,我們首先要明確的就是,數組指針是指針而不是數組,它是指向數組的指針!
//判斷數組指針int* arr1[3]; //指針數組int (*arr2)[3]; //數組指針
我們之前學過C語言操作符,建議收藏,我們知道操作符[]
的優先級高于*
。
所以arr2
是數組指針,而arr1
是指針數組。
int (arr2*)[3]
:arr2
是一個指針,指向的對象是整型數組,數組的元素個數為3
#include int main(){ int arr[4] = { 1,2,3,4 }; int(*parr)[4] = &arr; //數組指針存放數組arr的地址! return 0;}
大家肯定很少見代碼這么寫吧,數組指針很少這樣使用!
我們已經知道了數組名就是,數組的首元素地址,而取地址數組名是數組的地址 。
那&arr
和arr
有啥區別呢?
#include int main(){ int arr[4] = { 1,2,3,4 }; int(*parr)[4] = &arr; printf("arr :%p/n",arr); printf("&arr:%p/n", &arr); return 0;}
居然都是第一個元素的地址!
但是我們知道,指針的類型決定了指針加減的步長!
#include int main(){ int arr[4] = { 1,2,3,4 }; int(*parr)[4] = &arr; printf("arr :%p/n",arr); printf("&arr:%p/n", &arr); printf("arr+1 :%p/n", arr+1); //整型指針加1,加一個整型類型大小 printf("&arr+1:%p/n", &arr+1);//數組指針加1,加一個數組類型大小 return 0;}
可以看到,數組指針和首元素地址,指針的類型不同
數組名
arr
:指針類型是整型 指針加減1
,步長為整型大小(4bit)
&
數組名:指針類型是數組 指針加減1
,步長為數組大小(16bit)
數組指針正確使用
#include void print_arr1(int arr[3][5], int row, int col){ int i = 0; for(i=0; i<row; i++) { for(j=0; j<col; j++) { printf("%d ", arr[i][j]); } printf("/n"); }}void print_arr2(int (*arr)[5], int row, int col){ int i = 0; for(i=0; i<row; i++) { for(j=0; j<col; j++) { printf("%d ", arr[i][j]); } printf("/n"); }}int main(){ int arr[3][5] = {1,2,3,4,5,6,7,8,9,10}; print_arr1(arr, 3, 5); //數組名arr,表示首元素的地址 //但是二維數組的首元素是二維數組的第一行 //所以這里傳遞的arr,其實相當于第一行的地址,是一維數組的地址 //可以數組指針來接收 print_arr2(arr, 3, 5); return 0;}
學了數組指針,是不是發現有點懵了!
//捋一捋int *arr1[3]; //指針數組//數組個數是3,元素是int指針類型的數據int (*arr2)[3];//數組指針//指針,指向數組,且數組的類型是int類型,且元素個數為3int* (*arr3)[3]; //數組指針//指針,指向數組,數組元素是int*類型,且元素個數為3int (*arr4[3])[3]; //數組指針指針//指針,指向一個數組指針,數組指針的類型是int(*) [3] 指向數組且為為int類型,元素個數為3......
就捋到吧,再捋下去就更懵了,兄弟們慢慢學,你可以了的!
在寫代碼的時候難免要把數組或者指針傳給函數,那函數的參數該如何設計呢?
#include void test(int arr[])//ok?{} //int arr[] 接收就是以int * arr形式接收,因為*arr等價與 arr[]void test(int arr[10])//ok?{} //int arr[10] 同上在形參中都是一個整型指針,形參中的數組長度無意義void test(int* arr)//ok?{} //整型指針接收數組名就是首元素地址也就是整型指針void test2(int* arr[20])//ok?{} //int* arr[20]等價于 int* arr[]等價于 int**arr 即二級指針 //而實參就是一個指向整型指針的指針也就是二級指針void test2(int** arr)//ok?{} //二級指針接收int main(){ int arr[10] = { 0 }; int* arr2[20] = { 0 }; test(arr); test2(arr2);}
void test(int arr[3][5])//ok?{} //二維數組傳參二維數組接收void test(int arr[][])//ok?{} //error 不知道二維數組中一維數組中元素個數void test(int arr[][5])//ok?{} //可以省略行不能省略列//總結:二維數組傳參,函數形參的設計只能省略第一個[]的數字。//因為對一個二維數組,可以不知道有多少行,但是必須知道一行多少元素。//這樣才方便運算。void test(int *arr)//ok?{} //error 二維數組的數組名就是首元素地址,即一維數組的地址,// 也就是數組指針,應該用數組指針接收 void test(int* arr[5])//ok?{} //error,指針數組,實參是數組指針void test(int (*arr)[5])//ok?{} //實參為數組指針與形參類型相同void test(int **arr)//ok?{} //error int main(){ int arr[3][5] = {0}; test(arr);}
我們來總結一下!
- 二維數組的數組名就是首元素地址,而二維數組的元素就是一維數組,所以數組名的類型就是數組指針。
- 當二維數組數組名傳參,形參接收時,數組的行可以省略,列不能省略,如果省略了列,我們就無法知道當指針加減跳過幾個字節。
#include void print(int *p, int sz) //一級指針傳參,一級指針接收{ int i = 0; for(i=0; i<sz; i++) { printf("%d/n", *(p+i)); }}//void print(int p[],int sz) //數組接收,也即一級指針接收,不提倡這樣寫int main(){ int arr[10] = {1,2,3,4,5,6,7,8,9}; int *p = arr; int sz = sizeof(arr)/sizeof(arr[0]); //一級指針p,傳給函數 print(p, sz); return 0;}
思考:
當一個函數的參數部分為一級指針的時候,函數能接收什么參數?
//以int型指針為例void test(int* p){}int main(){ int x=0; int* px=&x; int arr[10]; test(&x);//整型地址 test(px);//一級指針 test(arr);//一維數組名,即首元素地址,int* return 0;}
void test(char** p ){}int main(){ char ch = "c"; char* pc = &ch; char* *ppc = &pc; char* arr[3]; test(&pc); //一級指針的地址,即二級指針 test(ppc); //二級指針 test(arr); //數組名,首元素地址,首元素為一級指針,所以為二級指針 return 0;}
思考:
當函數的參數為二級指針的時候,可以接收什么參數?
其實和上面一樣,你們可以思考一下!
首先看一段代碼:
#include void test(){ printf("hehe/n");}int main(){ printf("%p/n", test); //函數名 就是函數地址 printf("%p/n", &test); //&函數名 也是函數地址 return 0;}
運行結果
那么如何將test()
函數指針保存起來呢?
void test(){ printf("hehe/n");}//下面pfun1和pfun2哪個有能力存放test函數的地址?void (*pfun1)(); //函數指針類型void *pfun2(); //函數,函數的返回值是void*
函數指針類型
指針都是有類型的
整型指針int*
數組指針int (*)[]
函數指針返回值 (*)(參數....)
#include int Add(int x, int y){ return x + y;}int main(){ int (*pf)(int, int) = Add; int sum = (*pf)(3, 5); //對函數指針解引用 printf("sum = %d", sum); sum = pf(3, 5); //因為 Add和&Add相同,即Add等價于 pf printf("sum = %d", sum); return 0;}
有趣的代碼
//代碼1(*(void (*)())0)(); //void (*)()為函數指針//(void (*)())0 將0強制類型抓換成函數指針的地址//(*(void (*)())0)() *地址,調用0地址處的這個函數//函數的返回值空,參數為空
//代碼2void (*signal(int , void(*)(int)))(int);//void(*)(int) 函數指針,返回值void,參數int //void (*signal(int , void(*)(int)))(int)// signal是函數名 //返回值是void(*)(int)// 參數int 和函數指針 void(*)(int)//這是一個函數的聲明
當我們看到代碼2很難看懂這個代碼!
可以簡化嗎?
void (*signal(int , void(*)(int)))(int);//既然這個函數的返回值類型是 void(*)(int) //那我們可以寫成// void(*)(int) signal(int , void(*)(int));//但是這樣會語法錯誤 error
函數指針類型重命名
簡化
typedef void(*ptr_t) (int); //正確的類型重命名 ptr_t signal(int, ptr_t); //簡化
上面的代碼出自《C陷阱和缺陷》
有興趣的伙伴可以嘗試閱讀!
數組是一個存放相同類型數據的存儲空間,那我們已經學習了指針數組
比如:
int* arr[10]; //整型指針數組
那要把函數的地址存到一個數組中,那這個數組就叫函數指針數組,那函數指針的數組如何定義呢?
int (*parr1[10]])(); //int *parr2[10]();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/122185.html
摘要:因為指針指向的是整個數組,所以它的類型是數組指針,所以我們在它的前面進行強制類型轉換,把它轉換為類型,然后再存放到指針變量內部。 前言 通過8道指針筆試題的解析,可以充分的復習到指針的相關知識,并且題目中會結合許多之前的相關知識,希望通過本篇文章,對大家所學的知識進行一個復習。 提示:以下...
摘要:結尾有關這四道經典的指針筆試題講解就到此結束了,如果覺得文章對自己有所幫助,歡迎大家多多點贊收藏 ?前言 : 今天博主來講解4道經典的指針筆試題,很多朋友沒有深刻理...
摘要:故使用無具體類型,又稱通用類型,即可以接收任意類型的指針,但是無法進行指針運算解引用,整數等。求指針所占字節而不是解引用訪問權限大小。數組就是整個數組的大小,數組元素則是數組元素的大小,指針大小都為。 ...
摘要:之所以這樣說不要認為學就不需要學語言,是因為一味的只學而沒有語言等這些基礎語言的支撐,是很難深入理解的很多東西的。 之所以這樣說不要認為學PHP就不需要學C語言,是因為一味的只學PHP而沒有C語言等這些基礎語言的支撐,是很難深入理解PHP的很多東西的。 這樣的例子其實很多,這里我就舉這個例子吧:PHP的數組和C語言的數組的區別和聯系。 學過C語言的朋友當然知道C語言里有數組; PHP里...
閱讀 2231·2021-11-22 09:34
閱讀 1342·2021-10-11 10:59
閱讀 4442·2021-09-22 15:56
閱讀 3296·2021-09-22 15:08
閱讀 3411·2019-08-30 14:01
閱讀 781·2019-08-30 11:16
閱讀 1135·2019-08-26 13:51
閱讀 2915·2019-08-26 13:43