指针进阶(指针与数组传参、数组指针与指针数组、函数指针数组、回调函数的辨析)
指針
指針作為C語言中極具代表性的特征之一,也是C語言學習中的一大難點。
簡單來說,指針我們需了解的最基礎的即:
指針與數組傳參
了解指針與數組的傳參,首先了解傳值調用和傳址調用的問題
傳值調用:除了數組,其他數據實參均以直接拷貝,以傳值形式調用
傳址調用:數組傳參時不可直接拷貝,需降維為指針;但數據也可以進行傳址調用,只要在它前面加上取地址操作符即可
在講述這個問題之前,我們先看以下這段代碼:
int main() {char str1[] = "hello.";char str2[] = "hello.";char *str3 = "hello.";char *str4 = "hello.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n"); if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0; }以上一段代碼的輸出結果為"str1 and str2 are not same" ,"str3 and str4 are same"
因為str3和str4指向的是一個同一個常量字符串。C/C++會把常量字符串存儲到單獨的一個內存區域,當多個指針指向同一個字符串的時候,他們實際會指向同一塊內存。但是用相同的常量字符串去初始化不同的數組的時候就會開辟出不同的內存塊。所以str1和str2不同,str3和str4相同。
指針和數組作為參數傳入函數時,傳入的是地址,即指向變量的地址和數組的首地址,可以在函數中改變指針或數組的值,但本質上還是值的傳遞(區別于變量的值傳遞的是:變量值傳遞不會改變實參原來的值。),我們無法對指針和數組的地址進行操作(如:地址賦值,分配內存等),要進行地址操作需要使用指針引用或二級指針。
一維數組傳參
數組名作為函數參數傳遞時,傳遞數組首元素的地址,函數接收的實際為原參數的一份拷貝,使得函數操縱時不影響實際值
對于一維數組傳參,有以下幾種表示方法:
以上五種寫法都是正確的
用數組接收傳的是首元素地址,可以指定數組大小,也可以不指定,因為傳到函數內的是地址,故可以用指針接收,第四種情況中,將一個指針數組的數組名傳入,第五種則是傳入指針數組數組名,代表首元素地址,首元素是一個指向數組的指針,再取地址,表示二級指針
調用函數時實際傳遞的是一個指針,即函數形參為指針,但數組名依然可被表示為數組首元素的地址被調用。兩種調用明顯用指針較為準確,因為實參實際是指針,而函數中一維數組可以不標明數組長度也是這個原因:函數沒有給數組參數分配內存空間,形參指向已經開辟好的空間。指定數組長度,則數組作為一個顯式參數傳給數組
二維數組傳參
二維數組的表示方法如下:
void test(int arr[3][5]) //void test(int arr[][]) void test(int arr[][5]) //二維數組傳參,函數形參的設計只能省略第一個[]的數字以上表示方法第二種有錯誤
從實參傳遞來的是數組的起始地址,在內存中按數組排列規則存放(按行存放),而并不區分行和列,如果在形參中不說明列數,則系統無法決定應為多少行多少列,不能只指定一維而不指定第二維,即多維數組傳參要指定第二維或者更高維的大小,可以省略第一維的大小
一級、二級指針傳參
一級指針傳參和二級指針傳參較為簡單,傳參后,對指針參數執行間接訪問操作使函數修改原本變量的值,值得一提的是:
函數參數部分為一級指針時,函數可接收的參數有以下幾種
函數參數部分為二級指針時,函數可接收的參數有以下幾種
數組指針與指針數組
數組指針
定義
int (*p)[10];//p先和*結合,說明p是一個指針變量,然后指著指向的是一個大小為10個整型的數組。所以p是一個指針,指向
一個數組,叫數組指針。
數組指針的運用
void print_arr1(int arr[3][5], int row, int col) {int i = 0;int j;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;int j;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 *p[10];//[]的優先級要高于*,說明p是一個數組,結合*,即存儲指針的數組,叫指針數組
函數指針數組
函數指針
void *pfun();pfun先和*結合,說明pfun是指針,指針指向的是一個函數,指向的函數無參數,返回值類型為void。
函數指針數組
把函數的地址存到一個數組中,這個數組叫函數指針數組
int (*parr[10])();//[]的優先級要高于*,說明paar是一個數組,數組的內容為int (*)()類型的函數指針
函數指針數組一般用于:轉移表
舉個例子:實現計算器操作的代碼
普通寫法
利用函數指針數組
int main() {int x, y;int input = 1;int ret = 0;int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //轉移表while (input){scanf("%d", &input);if ((input <= 4 && input >= 1)){scanf("%d %d", &x, &y);ret = (*p[input])(x, y);}elseprintf("輸入有誤\n");printf("ret = %d\n", ret);}return 0; }相較之下,簡便了很多
回調函數
回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。舉個例子:
void bubble(void *base, int count , int size, int(*cmp )(void *, void *))看到兩道很有意思的題和大家分享一下,可以驗證是否掌握了指針的大部分知識:
int main() {char *str[] = { "welcome", "to", "fortemedia", "nanjing" };char **p = str + 1;str[0] = (*p++) + 2;str[1] = *(p+1);str[2] =p[1] + 3;str[3] = p[0]+(str[2]-str[1]);printf("%s\n", str[0]); // printf("%s\n", str[1]); //nanjingprintf("%s\n", str[2]); //jingprintf("%s\n", str[3]); //greturn 0; }進化版…
int main() {char *c[] = { "ENTER", "NEW", "POINT", "FIRST" };char**cp[] = { c + 3, c + 2, c + 1, c };char***cpp = cp;printf("%s\n", **++cpp); //POINTprintf("%s\n", *--*++cpp + 3); //ERprintf("%s\n", *cpp[-2] + 3); //STprintf("%s\n", cpp[-1][-1] + 1); //EWreturn 0; }還有這兩段代碼的名稱,可以嘗試讀一下(出自《C陷阱與缺陷》)
//代碼1 (*(void (*)())0)(); //代碼2 void (*signal(int , void(*)(int)))(int);總結
以上是生活随笔為你收集整理的指针进阶(指针与数组传参、数组指针与指针数组、函数指针数组、回调函数的辨析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一种输电线路视频图像在线监测装置
- 下一篇: 提高英语 - 口语和思维