日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

指针进阶(指针与数组传参、数组指针与指针数组、函数指针数组、回调函数的辨析)

發布時間:2024/1/1 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 指针进阶(指针与数组传参、数组指针与指针数组、函数指针数组、回调函数的辨析) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

指針

指針作為C語言中極具代表性的特征之一,也是C語言學習中的一大難點。
簡單來說,指針我們需了解的最基礎的即:

  • 指針是一個用來存放地址的變量,地址唯一標識一塊內存空間。
  • 指針的大小是固定的4/8個字節(32位平臺/64位平臺)。
  • 指針是有類型,指針的類型決定了指針的±整數的步長,指針解引用操作的時候的權限。
  • 指針與數組傳參

    了解指針與數組的傳參,首先了解傳值調用和傳址調用的問題
    傳值調用:除了數組,其他數據實參均以直接拷貝,以傳值形式調用
    傳址調用:數組傳參時不可直接拷貝,需降維為指針;但數據也可以進行傳址調用,只要在它前面加上取地址操作符即可

    在講述這個問題之前,我們先看以下這段代碼:

    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[]) void test1(int arr[10]) void test2(int *arr) void test3(int *arr[20]) void test4(int **arr)

    以上五種寫法都是正確的
    用數組接收傳的是首元素地址,可以指定數組大小,也可以不指定,因為傳到函數內的是地址,故可以用指針接收,第四種情況中,將一個指針數組的數組名傳入,第五種則是傳入指針數組數組名,代表首元素地址,首元素是一個指向數組的指針,再取地址,表示二級指針

    調用函數時實際傳遞的是一個指針,即函數形參為指針,但數組名依然可被表示為數組首元素的地址被調用。兩種調用明顯用指針較為準確,因為實參實際是指針,而函數中一維數組可以不標明數組長度也是這個原因:函數沒有給數組參數分配內存空間,形參指向已經開辟好的空間。指定數組長度,則數組作為一個顯式參數傳給數組

    二維數組傳參

    二維數組的表示方法如下:

    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 add(int a, int b) {return a + b; } int sub(int a, int b) {return a - b; } int mul(int a, int b) {return a*b; } int div(int a, int b) {return a / b; } int main() {int x, y;int input = 1;int ret = 0;while (input){scanf("%d", &input);switch (input){case 1:scanf("%d %d", &x, &y);ret = add(x, y);break;case 2:scanf("%d %d", &x, &y);ret = sub(x, y);break;case 3:scanf("%d %d", &x, &y);ret = mul(x, y);break;case 4:scanf("%d %d", &x, &y);ret = div(x, y);break;default:printf("選擇錯誤\n");break;}printf("ret = %d\n", ret);}return 0; }

    利用函數指針數組

    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);

    總結

    以上是生活随笔為你收集整理的指针进阶(指针与数组传参、数组指针与指针数组、函数指针数组、回调函数的辨析)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。