微信平臺公眾號開發(fā)廊坊網(wǎng)站seo
目錄
一、字符指針
二、指針數(shù)組
三、數(shù)組指針
數(shù)組指針的定義
&數(shù)組名 與?數(shù)組名
數(shù)組指針的使用
四、數(shù)組參數(shù)
一維數(shù)組傳參
二維數(shù)組傳參
五、指針參數(shù)
一級指針傳參
二級指針傳參
六、函數(shù)指針
七、函數(shù)指針數(shù)組
八、指向函數(shù)指針數(shù)組的指針
九、回調(diào)函數(shù)
一、字符指針
在指針的類型中我們知道還有一種指針類型為字符指針char*
簡單的使用:
int main()
{char ch = 'w';char* pc = &ch;*pc = 'w';return 0;
}
還有一種使用方式如下:
int main()
{const char* pstr = "hello world";//這里是把一個字符串放到pstr指針變量里了嗎?printf("%s\n", pstr);return 0;
}
代碼const char* pstr = "hello world";特別容易讓同學以為是把字符串 hello world放到字符指針 pstr
里了,但是本質(zhì)是把字符串 hello world首字符的地址放到了pstr中。
上面代碼的意思是把一個常量字符串的首字符?h?的地址(0x0012ff44)存放到指針變量?pstr?中。
有這樣一道題目:
#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";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;
}
我們不妨先來猜猜結(jié)果,然后來看答案:
是否與你的想法有所差異呢?
原因是這樣:
這里str3和str4指向的是同一個常量字符串。C/C++會把常量字符串存儲到單獨的一個內(nèi)存區(qū)域,當幾個指針指向同一個字符串的時候,他們實際會指向同一塊內(nèi)存。但是用相同的常量字符串去初始化不同的數(shù)組的時候就會開辟出不同的內(nèi)存塊。所以str1和str2不同,str3和str4不同。
二、指針數(shù)組
已經(jīng)學習過指針數(shù)組。這里簡單回憶一下:
int* arr1[10]; //整型指針的數(shù)組
//描述:
//arr1數(shù)組有10個元素,且每個元素都為int*類型char* arr2[4]; //一級字符指針的數(shù)組
//描述:
//arr2數(shù)組有4個元素,且每個元素都為char*類型char** arr3[5];//二級字符指針的數(shù)組
//描述:
//arr3數(shù)組有5個元素,且每個元素都為char**類型
三、數(shù)組指針
數(shù)組指針的定義
數(shù)組指針是指針?還是數(shù)組?答案是:指針。
我們已經(jīng)熟悉:整形指針: int * pint ; 能夠指向整形數(shù)據(jù)的指針。浮點型指針: float * pf ; 能夠指向浮點型數(shù)據(jù)的指針。
那數(shù)組指針應該是:能夠指向數(shù)組的指針。
那么下面代碼哪個是數(shù)組指針?
int* p1[10];
int(*p2)[10];
//p1, p2分別是什么?
答案是下面的代碼。那么我來解釋一下為什么數(shù)組指針長這個樣子:
int (*p)[10];
//解釋:p先和*結(jié)合,說明p是一個指針變量,然后指著指向的是一個大小為10個整型的數(shù)組。
//所以p是一個指針,指向一個數(shù)組,叫數(shù)組指針。
//這里要注意:[]的優(yōu)先級要高于*號的,所以必須加上()來保證p先和*結(jié)合。
&數(shù)組名 與?數(shù)組名
對于下面的數(shù)組:
int arr[10];
arr 和& arr 分別是啥?
我們知道arr是數(shù)組名,數(shù)組名表示數(shù)組首元素的地址。
那&arr數(shù)組名到底是啥?
我們看一段代碼:
#include <stdio.h>
int main()
{int arr[10] = { 0 };printf("%p\n", arr);printf("%p\n", &arr);return 0;
}
運行結(jié)果如下:
可見數(shù)組名和&數(shù)組名打印的地址是一樣的。
難道兩個是一樣的嗎?
我們再看一段代碼:
#include <stdio.h>
int main()
{int arr[10] = { 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;
}
運行之后:
根據(jù)上面的代碼我們發(fā)現(xiàn),其實&arr和arr,雖然值是一樣的,但是意義應該不一樣的。
實際上: &arr 表示的是 數(shù)組的地址 ,而不是數(shù)組首元素的地址。(細細體會一下)
本例中 &arr 的類型是: int(*)[10] ,是一種數(shù)組指針類型
數(shù)組的地址 +1 ,跳過整個數(shù)組的大小,所以 &arr+1 相對于 &arr 的差值是 40
數(shù)組指針的使用
那數(shù)組指針是怎么使用的呢?
既然數(shù)組指針指向的是數(shù)組,那數(shù)組指針中存放的應該是數(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ù)組指針使用實例:
#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{int i = 0;int j = 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;int j = 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 };//這是一個二維數(shù)組print_arr1(arr, 3, 5);//數(shù)組名arr,表示首元素的地址//但是二維數(shù)組的首元素是二維數(shù)組的第一行//所以這里傳遞的arr,其實相當于第一行的地址,是一維數(shù)組的地址//可以數(shù)組指針來接收printf("-----------------\n");print_arr2(arr, 3, 5);return 0;
}
這里有非常重要的一點,我們已經(jīng)知道數(shù)組名就是數(shù)組首元素的地址(有兩個例外),那么對于二
維數(shù)組來說,二維數(shù)組的首元素是什么呢?其實就是第一行數(shù)組。
如圖所示,我們給arr+1之后,它的地址跳過了一個一維數(shù)組的大小。所以我們可以通俗的把二維
數(shù)組arr理解為,arr數(shù)組包含3個元素,每個元素都是元素個數(shù)為5、元素類型為int類型的數(shù)組。
我們甚至可以換一種方式來定義二維數(shù)組:
int arr1[5]={1,2,3,4,5};
int arr2[5]={6,7,8,9,10};
int arr3[5]={0};
int ( * (arr[3]) ) [5] = { arr1,arr2,arr3 };
arr[3]//首先arr是一個數(shù)組,且包含3個元素,所以先和[3]結(jié)合int (*) [5]//這是一個數(shù)組指針的類型,表明該指針指向一個數(shù)組
//且數(shù)組包含5個元素,每個元素是int類型int (*(arr[3]))[5] //表明arr數(shù)組包含3個元素,且每個元素的類型都是數(shù)組指針
接下來,我們嘗試認識并解釋下面代碼:
//整型數(shù)組:
int arr[5];
//arr是一個數(shù)組,包含5個元素,且每個元素的類型是int//指針數(shù)組:
int *parr1[10];
//parr1是一個數(shù)組,包含10個元素,且每個元素的類型是int*//數(shù)組指針:
int (*parr2)[10];
//parr2是一個指針,指向的是一個數(shù)組,數(shù)組包含10個元素,且每個元素的類型是int//數(shù)組指針數(shù)組
int (*parr3[10])[5];
//parr3是一個數(shù)組,包含10個元素,且每個元素的類型是數(shù)組指針
//數(shù)組指針指向的是一個數(shù)組,包含5個元素,且每個元素的類型是int
四、數(shù)組參數(shù)
一維數(shù)組傳參
如果我們要將如下兩個數(shù)組作為參數(shù)傳遞給test函數(shù)
#include<stdio.h>void test1()//參數(shù)該如何設計?void test2()//參數(shù)該如何設立?int main()
{int arr1[10] = { 0 };int* arr2[20] = { 0 };test1(arr1);test2(arr2);
}
下面展示的幾種設計方式都是正確的:
void test(int arr[])//可以省略10
{}
void test(int arr[10])
{}
void test(int* arr)
{}
void test2(int* arr[20])
{}
void test2(int** arr)
{}
我們調(diào)用test函數(shù)時,都將數(shù)組名傳遞過去,而數(shù)組名又是首地址,當然可以用一個指針變量來接收。
對于arr1,是一個整型數(shù)組,元素類型為int ,所以用int*的指針來接收;
對于arr2,是一個指針數(shù)組,元素類型為int* ,所以用int**的二級指針來接收。
二維數(shù)組傳參
#include<stdio.h>
int main()
{int arr[3][5] = {0};test(arr);return 0;
}
如果我們要將二維數(shù)組arr傳遞給test函數(shù),那么test函數(shù)又該如何如何設計呢?
void test(int arr[3][5])
{}
//void test(int arr[][])
//{}
void test(int arr[][5])
{}
//void test(int* arr)
//{}
//void test(int* arr[5])
//{}
void test(int(*arr)[5])
{}
void test(int** arr)
{}
如上面代碼所示,除了被注釋掉的代碼,其他的都是行得通的。
對于第二種格式為什么是錯的,是因為有這樣的規(guī)定:
總結(jié):二維數(shù)組傳參,函數(shù)形參的設計只能省略第一個 [] 的數(shù)字。
因為對一個二維數(shù)組,可以不知道有多少行,但是必須知道一行多少元素。 這樣才方便運算。
第四、五兩種形式是接收一維數(shù)組的設計。
那么最后兩種又為什么行呢?
參照第三節(jié)最后的二維數(shù)組與數(shù)組指針就不難理解了。
原因就是,二維數(shù)組的數(shù)組名就是首元素地址。而其實二維數(shù)組的每個元素又是一個數(shù)組。所以
我們可以用數(shù)組指針 int(*arr)[5] 或是 二級指針來接收。
五、指針參數(shù)
一級指針傳參
一級指針傳參相對簡單,就用一個指針變量接收即可。
一級指針運用實例:
#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;
}
二級指針傳參
同樣的,二級指針傳參,也只需一個二級指針即可。
例如:
#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;
}
思考一下:
通過前面的二級指針與二維數(shù)組的關系,分析一下當函數(shù)的參數(shù)為二級指針時,可以接收什么參
數(shù)?大致有這么幾種:
void test(char** p)
{}
int main()
{char c = 'b';char* pc = &c;char** ppc = &pc;char* arr[10];test(&pc);test(ppc);test(arr);//Ok?return 0;
}
六、函數(shù)指針
之前學到,&+變量名可以得到變量的地址,&+數(shù)組名可以取出數(shù)組的地址(其實是數(shù)組首元素的地址)。那么當我們第一次聽到函數(shù)指針這個概念時,有沒有首先想到函數(shù)也會有地址嗎?怎么得到函數(shù)的地址?難道&+函數(shù)名就可以得到函數(shù)的地址嗎?
其實還真是,看以下操作:
#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}
結(jié)果如下:
我們發(fā)現(xiàn),不僅&+函數(shù)名可以得到函數(shù)的地址,就連函數(shù)名本身也是函數(shù)的地址。
既然得到了函數(shù)的地址,我們?nèi)绾螌⒑瘮?shù)的地址保存起來呢?
這就要用到函數(shù)指針了。下面我們看代碼:
void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪個有能力存放test函數(shù)的地址?
void (*pfun1)();
void* pfun2();
首先,能給存儲地址,就要求 pfun1 或者 pfun2 是指針,那哪個是指針?
答案是:
pfun1可以存放。pfun1先和*結(jié)合,說明pfun1是指針,指針指向的是一個函數(shù),指向的函數(shù)無參
數(shù),返回值類型為void。
下面我們多練習如何認識函數(shù)指針:
int (*pfun3)(int a, int b);//pfun3先和*結(jié)合,說明pfun3是一個指針,指向一個函數(shù),//函數(shù)有兩個參數(shù),參數(shù)的類型都是int,函數(shù)的返回值是int類型char* (*pfun4)(int* pa, int* (*parr)[10]);//pfun4先和*結(jié)合,說明pfun4是一個指針,指向一個函數(shù),//函數(shù)有兩個參數(shù),參數(shù)的類型一個是int*,一個是數(shù)組指針,函數(shù)的返回值是char*類型
//函數(shù)指針
int Add(int x, int y)
{return x + y;
}int main()
{//pf 是一個存放函數(shù)地址的指針變量 - 函數(shù)指針int (*pf)(int, int) = &Add;//可以理解為&Add給pfint ret = (*pf)(2,3);//*pf相當于*&Add==Add(僅助于理解,這樣符合語法理解而已)//其實(*pf)中的*只是一個號擺設,加多少個*都一樣(*****pf)也照樣可以使用函數(shù)//但是加上了*號必須要加括號(*pf)//&函數(shù)名和函數(shù)名都是函數(shù)的地址int (*pf)(int, int) = Add;//int ret = Add(2, 3);int ret = pf(2, 3);printf("%d\n", ret);return 0;
}
七、函數(shù)指針數(shù)組
首先提問,函數(shù)指針數(shù)組是一個指針還是數(shù)組?答案是是一個數(shù)組。
數(shù)組是一個存放相同類型數(shù)據(jù)的存儲空間,那我們已經(jīng)學習了指針數(shù)組,
比如:
int *arr[10];
//數(shù)組的每個元素是int*
那要把函數(shù)的地址存到一個數(shù)組中,那這個數(shù)組就叫函數(shù)指針數(shù)組,那函數(shù)指針的數(shù)組如何定義呢?
//首先,函數(shù)指針數(shù)組是數(shù)組parr[];//數(shù)組的每個元素是函數(shù)指針,以無參函數(shù),且無返回值的函數(shù)為例void (*) ();//結(jié)合后void (*) () parr[];//規(guī)范的寫法void (*parr[])()
再多舉幾個例子吧:
int (*parr1[5]) (int a,int b);char (*parr2[10]) (int* a,int* b);double (*parr3[10]) (double a.double* b);int* (*parr4[10]) (int (*pfun) (int a,int b).int* a);//....
函數(shù)指針數(shù)組有什么用途呢?
例如,我們現(xiàn)在要實現(xiàn)一個計算器,包含加減乘除四種功能。我們可以將這四種功能分別用四個函數(shù)來實現(xiàn)。然后將四個函數(shù)對應的函數(shù)指針保存到一個函數(shù)指針數(shù)組里。當我們運行計算器的程序時,可以根據(jù)選擇不同功能的選項,來找到函數(shù)指針數(shù)組不同下標所對應的不同函數(shù),進行運算。
#include <stdio.h>
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;int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //轉(zhuǎn)移表while (input){printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);if ((input <= 4 && input >= 1)){printf("輸入操作數(shù):");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);}elseprintf("輸入有誤\n");printf("ret = %d\n", ret);}return 0;
}
八、指向函數(shù)指針數(shù)組的指針
首先,指向函數(shù)指針數(shù)組的指針是一個指針。
指針指向一個數(shù)組,數(shù)組的每個元素都是函數(shù)指針。那么該如何定義一個指向函數(shù)指針數(shù)組的指針呢?
#include<stdio.h>
void test(const char* str)
{printf("%s\n", str);
}
int main()
{//函數(shù)指針pfunvoid (*pfun)(const char*) = test;//函數(shù)指針的數(shù)組pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] = test;//指向函數(shù)指針數(shù)組pfunArr的指針ppfunArrvoid (*(*ppfunArr)[5])(const char*) = &pfunArr;return 0;
}
如果給我們一個指向函數(shù)數(shù)組的指針,我們不會辨別怎么辦?
沒關系,我們逐步來分解:
//指向函數(shù)指針數(shù)組pfunArr的指針ppfunArrvoid (*(*ppfunArr)[5])(const char*) = &pfunArr;//首先我們看到*與ppfunArr結(jié)合//所以ppfunArr是一個指針,我們將*ppfunArr記作avoid (*a[])(const char*)//現(xiàn)在我們看到,a與[]先結(jié)合
//說明指針a指向的是一個數(shù)組
//接下來將a[]移除void (*) (const char*)//剩下的部分我們已經(jīng)學過,是一個函數(shù)指針
//說明數(shù)組的元素類型是函數(shù)指針//總結(jié),ppfunArr是一個指針,指向一個數(shù)組,數(shù)組的每個元素都是函數(shù)指針
//所以,ppfunArr稱為指向函數(shù)指針數(shù)組的指針
九、回調(diào)函數(shù)
回調(diào)函數(shù)就是一個通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個
函數(shù),當這個指針被用來調(diào)用其所指向的函數(shù)時,我們就說這是回調(diào)函數(shù)?;卣{(diào)函數(shù)不是由該函數(shù)
的實現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時由另外的一方調(diào)用的,用于對該事件或條件進
行響應。
void menu()
{printf("*******************************\n");printf("****** 1. add 2. sub *****\n");printf("****** 3. mul 4. div *****\n");printf("****** 0. exit *****\n");printf("*******************************\n");
}int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}void calc(int (*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("請輸入兩個操作數(shù):>");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do {menu();printf("請選擇:>");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出計算器\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;
}