零基礎(chǔ)詳解C語言指針進階
前言
這是指針的進階,如果想入門指針的朋友可以關(guān)注我的另外一篇文章—c語言 指針零基礎(chǔ)講解
堅持看完,一定會有很大的收獲~~
那接下來—起航
1.字符指針
我們目前知道整形指針,浮點型指針,字符指針跟他倆類型
字符指針—顧名思義就是指針,一個char*類型的指針
??在講解字符指針前,我先提一下怎么連續(xù)創(chuàng)建多個指針
連續(xù)創(chuàng)建多個指針的方法:
你可能會想到用:
int a ,b; int* a,b;
或者
#define PINT int* int main(){ int a ,b; p a,b; }
但是實際上這樣創(chuàng)建的結(jié)果是:
創(chuàng)建一個整形指針int*a與整形變量int b.
創(chuàng)建指針的正確打開方式:
//方案一,直接定義兩次 int a,b; int*a; int*b; //方案二,采用typedef重定義 typedef int* pint { int a,b; pint pa, pb;//此時就是定義int *pa與int *pb都是指針變量 return 0; }
下面給出一個簡單的代碼:
char ch='w'; char* p=ch; char* p="abcde";
定義一個char類型的變量ch,將ch地址放在指針變量p中
此時p存放的就是字符w的地址
這個很容易理解,那么char* p="abcde"是什么意思呢
實際上這次的p存放的是字符串a(chǎn)bcde的首元素的地址,也就是a的地址
有了首地址,就很容易找到后續(xù)元素的地址
例題 1
判斷下面代碼是否相等 char arr1[] = "abcdef"; char arr2[] = "abcdef"; const char* str1 = "abcdef"; const char* str2 = "abcdef";
#include<stdio.h>//證明相等關(guān)系 int main() { char arr1[] = "abcdef"; char arr2[] = "abcdef"; const char* str1 = "abcdef"; const char* str2 = "abcdef"; if (arr1 == arr2) printf("arr1==arr2\n"); else printf("arr1!=arr2\n"); if (str1 == str2) printf("str1==str2\n"); else printf("str1!=str2\n"); return 0; }
為什么arr1!=arr2; str1==str2
首先創(chuàng)建數(shù)組,arr1與arr2,先向系統(tǒng)申請兩個不同的空間,然后將abcdef放入兩個不同的空間里,所以這兩個空間的地址當(dāng)然就不相同
其次是str1與str2的abcdef只存儲在只讀存儲區(qū)(這跟const無關(guān),const起到強調(diào)的作用,實際上有無const的意思是相同的),就是不能更改其中的元素;
這時候兩字符串的內(nèi)容相同,系統(tǒng)就不會在浪費多余的空間去儲存兩個不同的內(nèi)容~
所以就形成str1==str2,實際上這兩個變量儲存的都是a的地址
2.指針數(shù)組
在這之前我們知道整形數(shù)組,浮點型數(shù)組等
意思就是儲存整形與浮點型數(shù)子的數(shù)組
指針數(shù)組就是存放指針的數(shù)組,實際上還是數(shù)組,里面存放著不同類型的指針
int*arr[5];//整形指針數(shù)組 char*arr[5];//一級字符指針數(shù)組 char**arr[5];//二級字符指針數(shù)組
知道定義,那如何使用呢
例題 2
多組打印字符串
#include<stdio.h> int main()//指針數(shù)組 { char* arr[] = { "abcdef","ghi" ,"jklmn" }; //打印 int i = 0; int sz = sizeof (arr) / sizeof (arr[0]); for (i = 0; i < sz; i++) { printf("%s\n",arr[i]); } return 0; }
其中的 char* arr[] = { "abcdef","ghi" ,"jklmn" }就是指針數(shù)組,存放的就是char類型的指針,
他的作用就相當(dāng)于:
char arr1[] = "abcdef"; char arr2[] = "ghi"; char arr3[] = "jklmn";
打印的結(jié)果就是:
??打印整形的數(shù)組
#include<stdio.h> int main() { //打印整形數(shù) int arr1[] = { 1, 2, 3, 4, 5}; int arr2[] = { 2, 3, 4, 5, 6}; int arr3[] = { 3, 4, 5, 6, 7}; int* arr[] = { arr1,arr2,arr3 }; int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { printf("%d", arr[i][j]); } printf("\n"); } return 0; }
3.數(shù)組指針
3.1數(shù)組指針的定義
有了指針數(shù)組的概念,相信很多人就知道數(shù)組指針的概念
跟你們想的一樣
數(shù)組指針—就是指針,什么指針呢,存放數(shù)組的指針
int *pint;//能夠指向整形數(shù)據(jù)的指針 char *p;//能夠指向字符數(shù)據(jù)的指針 char (*p)[10];能夠指向數(shù)組的指針
它的類型包括int(*)[], char(*)[]
解讀一下,其中的(*)就代表是一個指針[]就代表是一個數(shù)組的指針,char或者int就代表數(shù)組中的數(shù)據(jù)是啥類型的元素
??那么int * p1[10]與 int (*) p2[10]
其中的p1 p2是什么意思呢
前者是一個數(shù)組(指針數(shù)組)變量
后者是一個指針(數(shù)組指針)變量
3.2 &數(shù)組名與數(shù)組名
我們知道數(shù)組名就是首元素的地址(數(shù)組名直接與sizeof相連與&數(shù)組名除外)
那么&數(shù)組名是什么意思呢
實際上&數(shù)組名就是整個數(shù)組的地址
#include<stdio.h> int main() { int arr[20] = { 0 }; printf("%p\n", arr); printf("%p\n", arr+1); printf("%p\n", &arr); printf("%p\n", &arr+1); return 0; }
有打印的結(jié)果中很容易看出
arr 與 arr+1隔四個字節(jié),也就是一個整形的大小
而&arr與&arr+1之間隔八十個字節(jié),就是二十個整形的大小
??接下來在看一個代碼
int *p[10]; int *(*pp)[10];
此時的int *p[10],p就是指針數(shù)組變量
int *(*pp)[10]這句的意思就是定義一個指針變量,什么指針?數(shù)組指針,而這個數(shù)組里的元素都是int*,有十個int*類型的數(shù)據(jù)。這就是數(shù)組指針~~
故arr在一般情況下都是這個數(shù)組首元素的地址,&arr就是整個數(shù)組的地址
3.3 數(shù)組指針的使用
說完數(shù)組指針的定義和使用規(guī)則,下面講解數(shù)組指針的使用
給定三個數(shù)組,分別打印出他們的值
#include<stdio.h> print(int (*p)[5],int r,int c)//此時的int (*p)[5]就是一個數(shù)組指針 { int i = 0; int j = 0; for(i=0;i<r;i++) { for(j=0;j<c;j++) { printf("%d", *(*(p+i) + j)); //*(p+i)就相當(dāng)于拿到第一行的地址 } printf("\n"); } } int main() { //打印數(shù)字 int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} }; print(arr,3,5); return 0; }
打印的結(jié)果:
這道題運用了一個二維數(shù)組
在這里我將二維數(shù)組看作是一維數(shù)組,也就是把一行當(dāng)作一個元素
所以此數(shù)字有三個元素
就算是說二維數(shù)組的數(shù)組名就是第一行的的地址
當(dāng)然一維數(shù)組里也可以用到數(shù)組指針的思想,但實際上也不需要,這樣反而變得麻煩,反而在二維數(shù)組中可以很好的利用。
那下面來總結(jié)一下:
int *arr; int *parr1[10]; int (*parr2)[10]; int(*parr3[10])[10]
int *arr
是一個指針,類型是int*的指針;
int *parr1[10]
是一個數(shù)組,數(shù)組的類型是int *[],且是一個指針數(shù)組
int (*parr2)[10]
是一個指針,指針的類型是int(*)[],且是一個指針數(shù)組,數(shù)組中有十個整形元素
int(*parr3[10])[10]
是一個數(shù)組,此時把parr3去掉就得到nt(*)[],這里就是一個數(shù)組指針,所以這是一個存有十個數(shù)組指針的數(shù)組。
4.數(shù)組與指針在函數(shù)里的傳參
函數(shù)傳參的前提就是形參與實參是對應(yīng)的關(guān)系,也就是說實參是什么類型,那么形參也是相應(yīng)的類型,比如說實參是數(shù)組,那形參就是數(shù)組,參數(shù)是指針,那么形參也是指針。
4.1 一維數(shù)組的傳參
#include<stdio.h> test(arr1[]) {} test(arr1[10])//上面兩種方法,實參是數(shù)組,用數(shù)組來接收,因為在函數(shù)中本身不創(chuàng)建空間,所以無論【】{} //中的值為多少,都能達(dá)到目的~ test(*arr1) {} //前面我提到數(shù)組名就是首元素的地址,此時就把實參中的數(shù)組名當(dāng)作首元素的地址,此時用指 //針來接收 test(*arr2[10]) {} //這里也跟第一二兩個相同,也是數(shù)組傳參,數(shù)組來接收 test(**arr2) {} //這里采用二級指針來接收,實參是一級指針,那么一級指針的指針就可以用二級指針來接收 int main() { int arr1[10]={0}; int* arr2[10]={0}; test(arr1); test(arr2); return 0; }
4.2 二維數(shù)組的傳參
#include<stdio.h> //通過數(shù)組 test(int arr[10][10]) //二級指針傳參時以數(shù)組的形式,【】中的行可以省略,但是列不能省略 {} //列可以讓系統(tǒng)知道一行有多少的元素,從而分配多少的空間,便于知道每一 //個元素的地址,故test(int arr[10][10])與test(int arr[][10])是可以 //進行傳參的,但是test(int arr[][])顯然是不可以的。 test(int arr[][]) {} test(int arr[][10]) {} //通過指針 test(int *arr) {} test(int (*arr)[10]) //我們知道實參傳的是首元素的地址,這里的首元素的地址就是第一行元素的 {} //地址,也就是數(shù)組的地址,所以需要數(shù)組指針來接收 //故需要test(int (*arr)[10])來接收,而一級指針與二級指針顯然是不可的 test(int** arr) {} int main()//二級指針傳參 { int arr[10][10] = { 0 }; test(arr); return 0; }
4.3 一級指針的傳參
#include<stdio.h> test(int *p) {} int main() { int a = 10; int* par = &a; int arr[10]; test(par); test(arr); test(*a); //當(dāng)一級指針傳參時,形參是指針,那么實參就可以用這三種方式傳參 return 0; }
4.4 二級指針的傳參
#include<stdio.h> //當(dāng)二級指針傳參時,形參是二級指針,那實參有哪些方式呢 test(int** p) {} int main() { //二級指針傳參 char a = 'w'; char* p = &a; char** pp = &p; char* arr[10] = { 0 }; test(&p); //通過一級指針p的取地址 test(pp); //通過二級指針傳參,用二級指針來接收 test(arr); //通過char*()類型指針數(shù)組名來傳遞 return 0; }
5.函數(shù)指針
數(shù)組指針就是數(shù)組的地址
那么函數(shù)指針也是存放函數(shù)地址
??那問題是函數(shù)有指針嗎
#include<stdio.h> int Add(int x,int y) { int sum = 0; sum = x + y; } int main() { //證明函數(shù)有地址 int a = 0; int b = 0; printf("%p", &Add); printf("%p\n", Add); //定義一個函數(shù)指針變量 int (*pf)(int,int)=&Add; //此時的pf就是函數(shù)的指針變量,用pf就可以調(diào)用函數(shù) return 0; //括號中是參數(shù)的類型 }
這段代碼證明,函數(shù)也有地址,而且取地址加函數(shù)名與函數(shù)名的作用是相同的,既然函數(shù)有地址就可以通過指針調(diào)用這個函數(shù)
調(diào)用這個函數(shù):
#include<stdio.h> int Add(int x,int y) { int sum = 0; sum = x + y; return sum; } int main() { int (*pf)(int,int) = &Add; int ret1=(*pf)(2, 3); //括號別忘了 int ret2 = (pf)(2, 3);//這里的括號可以省略 printf("%d\n", ret1); printf("%d\n", ret2); return 0; }
打印的結(jié)果:
可以看到,主函數(shù)中的pf與*pf的使用效果是一樣的,打印的結(jié)果也是一樣的,所以是否有*都可以達(dá)到相同的目的,但是*代表著解引用,容易理解~
下面給出了一個有趣的代碼:
void(* signal (int,void (*) (int)) )(int)
這段代碼的意思是什么呢,是不是看著有點暈
實際上把signal (int,void (*) (int))
提出來剩下的就是void(*)(int)
,其中的signal是函數(shù)聲明。
首先這是一個函數(shù)指針,有兩個參數(shù),一個參數(shù)是int,還有一個是函數(shù)指針,其返回值就是void(*)(int)
,也就是一個函數(shù)指針。
這個函數(shù)可以簡化一下,也就是把void(*)(int)
重新定義為一個新的變量
你可能認(rèn)為是typedef void(*)(int) pfun_t
這樣的確好理解一些,畢竟跟我們所學(xué)習(xí)的結(jié)構(gòu)體是一樣的
但是真正的結(jié)果是typedef void(*pfun_t)(int)
,其中的pfun_t
放在中間,我這樣學(xué)也起到了強調(diào)的作用。
那void(* signal (int,void (*) (int)) )(int)
簡化的結(jié)果是pfun-t signal(int,void (*) (int))
pfun-t
是類型名,并不是類型名
6. 函數(shù)指針數(shù)組
前面我們學(xué)了整形指針數(shù)組,字符指針數(shù)組,和他們相同,函數(shù)指針數(shù)組也是一個數(shù)組,只不過數(shù)組里的元素是整形指針。
函數(shù)指針數(shù)組可以一次性實現(xiàn)多個函數(shù)的調(diào)用
#include<stdio.h> 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; } int main() { //int (*pf1)(int, int)=&Add; //int (*pf2)(int, int)=⋐ //int (*pf3)(int, int)=&Mul; //int (*pf4)(int, int)=&Div; int (*pfAdd[4])(int, int) = { &Add ,&Sub,&Mul,&Div };//數(shù)組指針可以很好的實現(xiàn)多次的定義 int i = 0; for (i = 0; i < 4; i++) { printf("%d\n", *(pfAdd[i])(8, 4)); } return 0; }
通過簡單的數(shù)組的調(diào)用,就可以實現(xiàn)加減乘除的運算,那函數(shù)指針數(shù)組有什么用呢
既然函數(shù)指針數(shù)組可以同時實現(xiàn)加減乘除,那當(dāng)然可以實現(xiàn)一個計算器
用函數(shù)指針數(shù)組實現(xiàn)一個計算器
#include<stdio.h> 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; } menu() { printf("*****************************************\n"); printf("******** 1.Add 2.Sub ***********\n"); printf("******** 3.Mul 4.Div ***********\n"); printf("******** 0.exit ***********\n"); printf("*****************************************\n"); } int main()//用函數(shù)指針來實現(xiàn)一個計算器 { int x = 0; int y = 0; int input = 0; int ret = 0; int (*pfArr[5])(int, int) = { 0, &Add ,& Sub,& Mul,& Div }; do { menu(); printf("請選擇:>"); scanf_s("%d", &input); if (input == 0) { printf("計算機以關(guān)閉"); } else if (input >= 1 && input <= 4) { printf("請輸入x與y:>"); scanf_s("%d %d", &x, &y); ret = pfArr[input](x, y); printf("%d\n", ret); } else printf("選擇錯誤"); } while (input); return 0; }
7.回調(diào)函數(shù)
????回調(diào)函數(shù)就是通過函數(shù)指針調(diào)用的函數(shù),如果把一個函數(shù)的指針(地址)當(dāng)作參數(shù),傳給另一個函數(shù),當(dāng)這個函數(shù)調(diào)用所指的函數(shù)時,我們就說這是回調(diào)函數(shù)。
下面來用回調(diào)函數(shù)來實現(xiàn)上一次用函數(shù)指針數(shù)組實現(xiàn)的計算器
#include<stdio.h> 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; } menu() { printf("*****************************************\n"); printf("******** 1.Add 2.Sub ***********\n"); printf("******** 3.Mul 4.Div ***********\n"); printf("******** 0.exit ***********\n"); printf("*****************************************\n"); } void calc(int (*pf)(int, int)) { int x = 0; int y = 0; printf("請輸入兩個數(shù):>"); scanf_s("%d %d", &x, &y); int ret = pf( x, y); printf("%d\n", ret); } int main() { int input = 0; do { printf("請選擇:\n"); menu(); scanf_s("%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("退出計算器"); break; default: printf("選擇錯誤"); break; } } while (input); return 0; }
回調(diào)函數(shù)很好的省略了case內(nèi)部的重復(fù)代碼過程,此時的加減乘除函數(shù)的指針當(dāng)作參數(shù)傳入calc函數(shù)里,這就是回調(diào)函數(shù)。
結(jié)語:這一章,c語言的指針相關(guān)的知識就結(jié)束了
到此這篇關(guān)于零基礎(chǔ)詳解C語言指針進階的文章就介紹到這了,更多相關(guān)C語言 指針內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VC中SendMessage和PostMessage的區(qū)別
這篇文章主要介紹了VC中SendMessage和PostMessage的區(qū)別,較為全面的分析了SendMessage和PostMessage運行原理及用法上的不同之處,非常具有實用價值,需要的朋友可以參考下2014-10-10C/C++實現(xiàn)獲取系統(tǒng)時間的示例代碼
C 標(biāo)準(zhǔn)庫提供了 time() 函數(shù)與 localtime() 函數(shù)可以獲取到當(dāng)前系統(tǒng)的日歷時間。本文將通過一些簡單的示例為大家講講C++獲取系統(tǒng)時間的具體方法,需要的可以參考一下2022-12-12OpenMP中For Construct對dynamic的調(diào)度方式詳解
在本篇文章當(dāng)中主要給大家介紹 OpenMp for construct 的實現(xiàn)原理,與他相關(guān)的動態(tài)庫函數(shù)分析以及對 dynamic 的調(diào)度方式進行分析,希望對大家有所幫助2023-02-02C語言數(shù)學(xué)問題與簡單DP01背包問題詳解
這篇文章主要介紹了C語言數(shù)學(xué)問題買不到的數(shù)目、螞蟻感冒、飲料換購與簡單DP01背包問題的解決,屬于藍(lán)橋杯省賽中的題目,感興趣的同學(xué)來看看吧2022-04-04Visual Studio Code (VSCode) 配置搭建 C/C++ 開發(fā)編譯環(huán)境的流程
記得N年前剛開始接觸編程時,使用的是Visual C++6.0,下面這個可愛的圖標(biāo)很多人一定很熟悉。不過今天想嘗鮮新的工具 Visual Studio Code 來搭建C/C++開發(fā)環(huán)境,感興趣的朋友一起看看吧2021-09-09C++ 動態(tài)創(chuàng)建按鈕及 按鈕的消息響應(yīng)
這篇文章主要介紹了C++ 動態(tài)創(chuàng)建按鈕及 按鈕的消息響應(yīng)的相關(guān)資料,需要的朋友可以參考下2015-06-06