零基礎(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)建空間,所以無論【】{} //中的值為多少,都能達到目的~
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é)果也是一樣的,所以是否有*都可以達到相同的目的,但是*代表著解引用,容易理解~
下面給出了一個有趣的代碼:
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-10
C/C++實現(xiàn)獲取系統(tǒng)時間的示例代碼
C 標(biāo)準(zhǔn)庫提供了 time() 函數(shù)與 localtime() 函數(shù)可以獲取到當(dāng)前系統(tǒng)的日歷時間。本文將通過一些簡單的示例為大家講講C++獲取系統(tǒng)時間的具體方法,需要的可以參考一下2022-12-12
OpenMP中For Construct對dynamic的調(diào)度方式詳解
在本篇文章當(dāng)中主要給大家介紹 OpenMp for construct 的實現(xiàn)原理,與他相關(guān)的動態(tài)庫函數(shù)分析以及對 dynamic 的調(diào)度方式進行分析,希望對大家有所幫助2023-02-02
C語言數(shù)學(xué)問題與簡單DP01背包問題詳解
這篇文章主要介紹了C語言數(shù)學(xué)問題買不到的數(shù)目、螞蟻感冒、飲料換購與簡單DP01背包問題的解決,屬于藍橋杯省賽中的題目,感興趣的同學(xué)來看看吧2022-04-04
Visual 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-09
C++ 動態(tài)創(chuàng)建按鈕及 按鈕的消息響應(yīng)
這篇文章主要介紹了C++ 動態(tài)創(chuàng)建按鈕及 按鈕的消息響應(yīng)的相關(guān)資料,需要的朋友可以參考下2015-06-06

