C語言進階學(xué)習(xí)之指針
1.指針概念回顧
指針的基本概念:
指針是一個變量,用來存放地址,地址唯一標(biāo)識一塊內(nèi)存空間。指針的大小是固定的4/8個字節(jié)(32位平臺/64位平臺)。指針是有類型,指針的類型決定了指針的±整數(shù)的步長,指針解引用操作的時候的權(quán)限。不能對沒有初始化的指針或者是空指針進行間接訪問
2.字符指針
指針類型為 char*
的指針就是字符指針
字符指針指向的對象的類型為字符型
一般使用方法:
char ch = 'a'; char* p = &ch; //取ch的地址放到指針變量p中
另外一種常見使用方法:
char* p2 = "bcdef";
對于第二種使用方法我們需要注意的是
p2實際存放的只是字符串首字符的地址,即p2存放的是字符b的地址。
我們來看一道經(jīng)典的面試題:
#include <stdio.h> int main() { char str1[] = "hello yang."; char str2[] = "hello yang."; char* str3 = "hello yang."; char* str4 = "hello yang."; 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; }
str3和str4指向的都是字符串H的地址
當(dāng)指針指向同一個字符串的時候,它們實際會指向同一塊內(nèi)存。但是用相同的常量字符串去初始化不同的數(shù)組的時候就會開辟出不同的內(nèi)存塊。
因此它的答案為
3.數(shù)組指針和指針數(shù)組
指針數(shù)組是一個存放指針的數(shù)組
例如
int* arr1[10]; //整形指針的數(shù)組 char *arr2[4]; //一級字符指針的數(shù)組 char **arr3[5]; //二級字符指針的數(shù)組
3.1數(shù)組指針的含義
數(shù)組指針是指針?還是數(shù)組?
答案是:指針。
int *p1[10]; int (*p2)[10]; //p1, p2分別是什么?
這里就不得不談?wù)摰絻?yōu)先級了,優(yōu)先級從大到小的順序為: “( )” > “[ ]” > “ * ”
首先看int *p1[10];
, 由于[ ]的優(yōu)先級大于*,因此p1先與“[” 結(jié)合,故而p1首先它是一個數(shù)組,它是一個存放了10個指針變量的數(shù)組。
再來看int (*p2)[10];,
由于()的優(yōu)先級大于 [ ], 先看()里的,p2與 * 結(jié)合,故而篇是一個指針,它指向了一個存放了10個整型的數(shù)組
3.2&數(shù)組名vs數(shù)組名
對于下面這個代碼
#include <stdio.h> int main() { int arr[5] = {0}; printf("%p\n", arr); printf("%p\n", &arr); return 0; }
arr 和 &arr 有什么區(qū)別呢?
其運行結(jié)果如下:
如果你認(rèn)為它們代表的含義是正確的,不妨試著運行下面這個代碼
#include <stdio.h> int main() { int arr[5] = { 0 }; printf("arr = %p\n", arr); printf("&arr= %p\n", &arr); printf("arr+1 = %p\n", arr + 1); printf("&arr+1= %p\n", &arr + 1); return 0; }
我的編譯器下運行結(jié)果如下:
如果 arr 和 &arr 代表的含義一樣,那么后面兩個的結(jié)果應(yīng)該也是一致的,但是卻不一樣。
實際上: &arr 表示的是整個數(shù)組的地址,而不是數(shù)組首元素的地址。
數(shù)組的地址+1,跳過整個數(shù)組的大小,所以 &arr+1 相對于 &arr 的差值是40
3.3數(shù)組指針
數(shù)組指針就是一個指向數(shù)組的指針,意味著,這個指針內(nèi)存放的是數(shù)組的地址
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,0}; int (*p)[10] = &arr;//把數(shù)組arr的地址賦值給數(shù)組指針變量p return 0; }
我們來使用下指向一維數(shù)組的數(shù)組指針
//使用函數(shù)打印數(shù)組內(nèi)的內(nèi)容 # include <stdio.h> void print_arr(int(*p)[10], int sz) //傳過來的是數(shù)組的地址,用指針來接收 { int i = 0; for (i = 0; i < sz; i++) { printf("%d ", (*p)[i]); //先解引用找到數(shù)組 } } int main(void) { int arr[10] = { 1,2,3,4,5,6,7,8,9,0 }; int sz = sizeof(arr) / sizeof(arr[0]); //求出數(shù)組的元素個數(shù) print_arr(&arr, sz); return 0; }
4.數(shù)組傳參和指針傳參
在寫代碼的過程中,為了更好地利用模塊化的特點,我們會使用大量的函數(shù)。但是在將一個功能封裝成函數(shù)的過程中,我們就不可避免的會傳過去數(shù)組和指針,那么函數(shù)是如何接受的呢?
4.1一維數(shù)組傳參
以下是常見的一維數(shù)組傳參
#include <stdio.h> void test(int arr[]) //直接用數(shù)組接收,空間大小可以省略 {} void test(int arr[10]) //直接用數(shù)組接收,空間大小可以指明 {} void test(int *arr) //接收數(shù)組首元素的地址 {} void test2(int *arr[20]) //直接用數(shù)組接收 {} void test2(int **arr) //傳過來一級指針的地址,使用二級指針接收 {} int main() { int arr[10] = {0}; int *arr2[20] = {0}; //存放了20個int*的數(shù)組 test(arr); //傳過去的是首元素的地址 test2(arr2); }
4.2二維數(shù)組傳參
當(dāng)我們在談?wù)摱S數(shù)組首元素的時候指的是第一行!
數(shù)組名是首元素的地址,在二維數(shù)組中指的是第一行的地址
void test(int arr[3][5])//直接用二維數(shù)組接收 {} void test(int arr[][])//錯誤的接收!列不能省略 {} void test(int arr[][5])//行是可以省略的,但是列一定不能省略 {} //總結(jié):二維數(shù)組傳參,函數(shù)形參的設(shè)計只能省略第一個[]的數(shù)字。 //因為對一個二維數(shù)組,可以不知道有多少行,但是必須知道一行多少元素。 //這樣才方便運算。 void test(int *arr)//錯誤接收!傳過來的是第一行一維數(shù)組的地址 {} void test(int* arr[5])//錯誤接收!這是一個存放int*類型的數(shù)組,而數(shù)組內(nèi)的元素是int {} //由于二維數(shù)組的首元素是第一行,每一行是一個一維數(shù)組 //因此可以寫成指向一維數(shù)組的指針 void test(int (*arr)[5]) {} void test(int **arr) //錯誤! {} int main() { int arr[3][5] = {0}; test(arr); }
4.3一級指針傳參
#include <stdio.h> void print(int *p, int sz) { int i = 0; for(i=0; i<sz; i++) { printf("%d\n", *(p+i)); } } 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,傳給函數(shù) print(p, sz); return 0; }
4.4二級指針傳參
#include <stdio.h> void test(int** ptr) { printf("num = %d\n", **ptr); } int main() { int n = 10; int* p = &n; int** pp = &p; test(pp); test(&p); return 0; }
5.函數(shù)指針
在之前的學(xué)習(xí)當(dāng)中不會每天使用函數(shù)指針,但是請不要忘記它的存在。
什么是函數(shù)指針?顧名思義,函數(shù)指針就是指向一個函數(shù)的指針。既然函數(shù)指針是指針,那么它也具有指針的一些特性。比如:在對指針進行間接訪問之前必須要先初始化
//代碼一 int f(int);//f函數(shù) int (*pf)(int) = &f;
這是函數(shù)初始化的一種方法。
//代碼二 int f(int); int (*pf)(int) = f;
代碼一和代碼二是等效的。我們用下面一個代碼查看,發(fā)現(xiàn)其打印出來的地址是一樣的。
在數(shù)組中,數(shù)組名是數(shù)組首元素的地址。而函數(shù)名則是函數(shù)的首地址。在函數(shù)名使用的時候我們所用的編譯器會將函數(shù)名轉(zhuǎn)化為一個函數(shù)指針,&操作符只是顯示地說明了函數(shù)將隱式執(zhí)行的任務(wù)。
如果你需要將函數(shù)的地址存儲起來,這個時候你就可能會需要用到函數(shù)指針了
void test() { printf("hehe\n"); } //下面的哪一個語句有能力存放函數(shù)test的地址呢? void (*pfun1)(); //語句1 void *pfun2(); //語句2
存儲地址應(yīng)該需要用到的是指針
語句1:
pfun1首先和 * 操作符結(jié)合,那么它是一個指針。后面有一個函數(shù)調(diào)用操作符,表明它是一個函數(shù)指針。它所指向的函數(shù)
返回值類型是void
語句2:
pfun2首先和()結(jié)合,那么它是一個函數(shù)。 *和前面的void結(jié)合,表明這個函數(shù)的返回值類型是void*
所以選擇語句1。
6.函數(shù)指針數(shù)組
我們知道數(shù)組是存儲一組相同類型的數(shù)據(jù),在此之前我們再來回顧一下指針數(shù)組
int *arr[10]; //數(shù)組的每個元素是int*
這是一個數(shù)組,數(shù)組存放了10個int*類型的數(shù)據(jù)。
按照這個思路,我們把很多函數(shù)的地址存放在數(shù)組中,那么它就是一個函數(shù)指針數(shù)組。
下面我們一起來實現(xiàn)它
首先它是一個數(shù)組 [ ], 存放的數(shù)據(jù)類型為函數(shù)指針,int (*)()類型的是函數(shù)指針
int (*parr1[10])(); int *parr2[10](); int (*)() parr3[10];
7.指向函數(shù)指針數(shù)組的指針
經(jīng)過我們上面這么多的推導(dǎo),這個也就很容易就能表示出來
指向函數(shù)指針數(shù)組的指針是一個 指針, 指針指向一個 數(shù)組 ,數(shù)組的元素都是 函數(shù)指針
void test(const char* str) { printf("%s\n", str); } int main() { //函數(shù)指針pfun void (*pfun)(const char*) = test; //函數(shù)指針的數(shù)組pfunArr void (*pfunArr[5])(const char* str); pfunArr[0] = test; //指向函數(shù)指針數(shù)組pfunArr的指針ppfunArr void (*(*ppfunArr)[10])(const char*) = &pfunArr; return 0; }
8.回調(diào)函數(shù)
回調(diào)函數(shù):回調(diào)函數(shù)就是將一個函數(shù)的函數(shù)指針作為參數(shù)傳遞給另一個函數(shù),而這個函數(shù)就是回調(diào)函數(shù)。
回調(diào)函數(shù)不是由該函數(shù)的實現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時由另外的一方調(diào)用的,用于對該事件或條件進行響應(yīng)。
庫函數(shù)中的qsort函數(shù)就是一個典型的回調(diào)函數(shù)
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C語言數(shù)據(jù)結(jié)構(gòu)樹的雙親表示法實例詳解
這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)樹的雙親表示法實例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06json error: Use of overloaded operator [] is ambiguous錯誤的解決方
今天小編就為大家分享一篇關(guān)于json error: Use of overloaded operator [] is ambiguous錯誤的解決方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-04-04