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