发表时间:2022-03-26来源:网络
1.指针是什么(可能有点难理解)
指针的是啥?
指针实际上就是地址,地址就是系统给定的编号,编号就是一个个内存单元。
在某种情况来说指针=地址=编号=内存单元。
指针就是地址,顾名思义,就是可以用来寻找目标的。
所以指针变量就是存放地址的变量。
当然我们口头上常说的指针就是指针变量~
那指针是怎么产生的呢,也就是说内存是怎样产生的呢?
而指针的大小也就是4个字节或八个字节组成(与指针的类型无关)
原理:指针的大小由系统决定,比如32位系统,就由32给比特位组成,
比如00000000000000000000000000000001
也就是4个字节~
也就是说无论是char类型还是int类型指针大小都是4或8(在64位系统上)个字节。

2. 指针和指针的类型
int a=10;
int* pa=&a; //此时int*就是指针pa的类型,而pa就是指针变量,来储存地址的~
指针类型分很多种如int,float,double等;
既然大小都是四个字节,那为什么要区分不同类型的指针呢?
这就要说到指针类型的特点:
1.指针类型决定解引用时访问几个字节;一个int类型指针就直接访问4给字节的空间,一个char类型就只能访问一个空间的内存等等
2.指针类型决定了指针向前一步或向后一步能走多远的距离。就比如说int类型的指针加一个单位就相当于走了四个字节的空间。

从图上就可以看到,当指针变量的类型是int型时,指针变量加一,地址就变了四个字节的,当指针是char类型的时候,指针变量加一就变成跳过一个字节的单位了。
可以连续定义多个指针如:
int*pa,*pb;(并不是int*pa,pb);
3.野指针
所谓野指针就是没有地址的指针,系统就会出现错误,都是造成野指针的情况下,系统往往都不会报错,都不能说明,野指针就是正确的,就比如说,一个小偷,没被进警察抓到就能说明他的行为是对的吗?回答是,当然不能。

虽然最终程序依然可以运行,但是最终的结果并不会是预期的那样。这时候就占用了一块未申请的空间了。这块空间的内容是随机的。
那野指针是怎样造成的呢?怎样避免野指针的发生呢?
造成原因:1.由于指针未定义,就开始使用。
比如:int* p;
*p=20;
2.指针的越界访问,这常常体现在数组里。

这就出现溢出的情况了~
3.指针指向的空间释放,在函数中很常见。

既然出现了野指针,那有什么办法可以避免野指针的出现呢?
解决方案
1.给指针初始话
2.小心指针越界
3.指针指向的空间释放后,及时定位NULL,防止被再次使用。
4.避免返回局部变量地址。
5.指针使用前先检测有效性。
4.指针的运算
4.1 指针的加减整数的运算
指针加减一个整数,地址就会有相应的变化;

4.2 指针-指针
这里的指针-指针,最终的结果是中间的元素个数(而不是中间有几个字节)
几个字节由指针的类型决定;
当然,既然由减,肯定有人会问为什么没加呢(问得好,下次别问了)
两指针相减,得到的是两个地址中间的元素个数,那相加就没有什么特别的含义了,所以不存在相加。
举个例子
我们平时计算字符串的长度通常有三种方法(1.计数器 2.递归 3.就是指针-指针)
前两种方法就不讲了~ (前提是两个指针是指向同一块空间)

5.指针与数组
指针是地址,口头上也就是指针变量~
数组是什么:数组就是相同类型数据的集合~
这似乎两者没什么关系,但是实际上我们可以通过指针来访问数组。

事实证明:*(p+i) *(arr+i) arr[i] p[i] 这四种情况的结果是相同的,都可以访问元素
他们代表的意义是相同的!!!
6.二级指针
你可能会担心很难(我指针都不懂,还二级指针,不看了)
别~~~~
其实二级与多级指针都很容易
二级指针指向的就是存放一级指针的地址(指针)
7.指针数组
指针数组是数组还是指针呢?
答案是:数组,是用来存放指针的数组。
除了指针数组外,其实我们还学习了整形数组和浮点型数组;
比如:
int* arr[4]={&a,&b,&c,&d}
&a
&b
&c
&d
int arr[4]={a,b,c,d}
a
b
c
d
对比一下,是不是很容易理解了,数组就是装有相同类型的数据,而指针数组也一样,只是里面装的都是一个个指针,不要想的很难哦~
这是指针的进阶
坚持看完,一定会有很大的收获~~
那接下来—起航
1.字符指针
我们目前知道整形指针,浮点型指针,字符指针跟他俩类型
字符指针—顾名思义就是指针,一个char*类型的指针
在讲解字符指针前,我先提一下怎么连续创建多个指针
连续创建多个指针的方法:
你可能会想到用:
int a ,b;
int* a,b;
或者
#define PINT int*
int main(){
int a ,b;
p a,b;
}
但是实际上这样创建的结果是:
创建一个整形指针int*a与整形变量int b.
创建指针的正确打开方式:
//方案一,直接定义两次
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存放的是字符串abcde的首元素的地址,也就是a的地址
有了首地址,就很容易找到后续元素的地址
例题 1
判断下面代码是否相等
char arr1[] = "abcdef";
char arr2[] = "abcdef";
const char* str1 = "abcdef";
const char* str2 = "abcdef";
#include//证明相等关系 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
首先创建数组,arr1与arr2,先向系统申请两个不同的空间,然后将abcdef放入两个不同的空间里,所以这两个空间的地址当然就不相同
其次是str1与str2的abcdef只存储在只读存储区(这跟const无关,const起到强调的作用,实际上有无const的意思是相同的),就是不能更改其中的元素;
这时候两字符串的内容相同,系统就不会在浪费多余的空间去储存两个不同的内容~
所以就形成str1==str2,实际上这两个变量储存的都是a的地址
2.指针数组
在这之前我们知道整形数组,浮点型数组等
意思就是储存整形与浮点型数子的数组
指针数组就是存放指针的数组,实际上还是数组,里面存放着不同类型的指针


int*arr[5];//整形指针数组
char*arr[5];//一级字符指针数组
char**arr[5];//二级字符指针数组
知道定义,那如何使用呢
例题 2
多组打印字符串
其中的 char* arr[] = { "abcdef","ghi" ,"jklmn" }就是指针数组,存放的就是char类型的指针,
他的作用就相当于:
char arr1[] = "abcdef";
char arr2[] = "ghi";
char arr3[] = "jklmn";
打印的结果就是:

✨✨打印整形的数组

3.数组指针
3.1数组指针的定义
有了指针数组的概念,相信很多人就知道数组指针的概念
跟你们想的一样
数组指针—就是指针,什么指针呢,存放数组的指针
int *pint;//能够指向整形数据的指针
char *p;//能够指向字符数据的指针
char (*p)[10];能够指向数组的指针
它的类型包括int(*)[], char(*)[]
解读一下,其中的(*)就代表是一个指针[]就代表是一个数组的指针,char或者int就代表数组中的数据是啥类型的元素
那么int * p1[10]与 int (*) p2[10]
其中的p1 p2是什么意思呢
前者是一个数组(指针数组)变量
后者是一个指针(数组指针)变量
3.2 &数组名与数组名
我们知道数组名就是首元素的地址(数组名直接与sizeof相连与&数组名除外)
那么&数组名是什么意思呢
实际上&数组名就是整个数组的地址

有打印的结果中很容易看出
arr 与 arr+1隔四个字节,也就是一个整形的大小
而&arr与&arr+1之间隔八十个字节,就是二十个整形的大小
接下来在看一个代码
int *p[10];
int *(*pp)[10];
此时的int *p[10],p就是指针数组变量
int *(*pp)[10]这句的意思就是定义一个指针变量,什么指针?数组指针,而这个数组里的元素都是int*,有十个int*类型的数据。这就是数组指针~~
故arr在一般情况下都是这个数组首元素的地址,&arr就是整个数组的地址
3.3 数组指针的使用
说完数组指针的定义和使用规则,下面讲解数组指针的使用
给定三个数组,分别打印出他们的值
这道题运用了一个二维数组
在这里我将二维数组看作是一维数组,也就是把一行当作一个元素
所以此数字有三个元素
就算是说二维数组的数组名就是第一行的的地址
当然一维数组里也可以用到数组指针的思想,但实际上也不需要,这样反而变得麻烦,反而在二维数组中可以很好的利用。
那下面来总结一下:
int *arr;
int *parr1[10];
int (*parr2)[10];
int(*parr3[10])[10]
int *arr
是一个指针,类型是int*的指针;
int *parr1[10]
是一个数组,数组的类型是int *[],且是一个指针数组
int (*parr2)[10]
是一个指针,指针的类型是int(*)[],且是一个指针数组,数组中有十个整形元素
int(*parr3[10])[10]
是一个数组,此时把parr3去掉就得到nt(*)[],这里就是一个数组指针,所以这是一个存有十个数组指针的数组。
4.数组与指针在函数里的传参
函数传参的前提就是形参与实参是对应的关系,也就是说实参是什么类型,那么形参也是相应的类型,比如说实参是数组,那形参就是数组,参数是指针,那么形参也是指针。
4.1 一维数组的传参
#include test(arr1[]) {} test(arr1[10])//上面两种方法,实参是数组,用数组来接收,因为在函数中本身不创建空间,所以无论【】{} //中的值为多少,都能达到目的~ test(*arr1) {} //前面我提到数组名就是首元素的地址,此时就把实参中的数组名当作首元素的地址,此时用指 //针来接收 test(*arr2[10]) {} //这里也跟第一二两个相同,也是数组传参,数组来接收 test(**arr2) {} //这里采用二级指针来接收,实参是一级指针,那么一级指针的指针就可以用二级指针来接收 int main() { int arr1[10]={0}; int* arr2[10]={0}; test(arr1); test(arr2); return 0; }4.2 二维数组的传参
#include //通过数组 test(int arr[10][10]) //二级指针传参时以数组的形式,【】中的行可以省略,但是列不能省略 {} //列可以让系统知道一行有多少的元素,从而分配多少的空间,便于知道每一 //个元素的地址,故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]) //我们知道实参传的是首元素的地址,这里的首元素的地址就是第一行元素的 {} //地址,也就是数组的地址,所以需要数组指针来接收 //故需要test(int (*arr)[10])来接收,而一级指针与二级指针显然是不可的 test(int** arr) {} int main()//二级指针传参 { int arr[10][10] = { 0 }; test(arr); return 0; }4.3 一级指针的传参
#include test(int *p) {} int main() { int a = 10; int* par = &a; int arr[10]; test(par); test(arr); test(*a); //当一级指针传参时,形参是指针,那么实参就可以用这三种方式传参 return 0; }4.4 二级指针的传参
#include //当二级指针传参时,形参是二级指针,那实参有哪些方式呢 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*()类型指针数组名来传递 return 0; }5.函数指针
数组指针就是数组的地址
那么函数指针也是存放函数地址
那问题是函数有指针吗

这段代码证明,函数也有地址,而且取地址加函数名与函数名的作用是相同的,既然函数有地址就可以通过指针调用这个函数
调用这个函数:
打印的结果:

可以看到,主函数中的pf与*pf的使用效果是一样的,打印的结果也是一样的,所以是否有*都可以达到相同的目的,但是*代表着解引用,容易理解~
下面给出了一个有趣的代码:
void(* signal (int,void (*) (int)) )(int)
这段代码的意思是什么呢,是不是看着有点晕
实际上把signal (int,void (*) (int))提出来剩下的就是void(*)(int),其中的signal是函数声明。
首先这是一个函数指针,有两个参数,一个参数是int,还有一个是函数指针,其返回值就是void(*)(int),也就是一个函数指针。
这个函数可以简化一下,也就是把void(*)(int)重新定义为一个新的变量
你可能认为是typedef void(*)(int) pfun_t
这样的确好理解一些,毕竟跟我们所学习的结构体是一样的
但是真正的结果是typedef void(*pfun_t)(int) ,其中的pfun_t放在中间,我这样学也起到了强调的作用。
那void(* signal (int,void (*) (int)) )(int)简化的结果是pfun-t signal(int,void (*) (int))
pfun-t是类型名,并不是类型名
6. 函数指针数组
前面我们学了整形指针数组,字符指针数组,和他们相同,函数指针数组也是一个数组,只不过数组里的元素是整形指针。
函数指针数组可以一次性实现多个函数的调用

通过简单的数组的调用,就可以实现加减乘除的运算,那函数指针数组有什么用呢
既然函数指针数组可以同时实现加减乘除,那当然可以实现一个计算器
用函数指针数组实现一个计算器
#include 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()//用函数指针来实现一个计算器 { 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("计算机以关闭"); } else if (input >= 1 && input7.回调函数
回调函数就是通过函数指针调用的函数,如果把一个函数的指针(地址)当作参数,传给另一个函数,当这个函数调用所指的函数时,我们就说这是回调函数。
下面来用回调函数来实现上一次用函数指针数组实现的计算器
如果想更好的了解,欢迎关注我的博客
指针初阶
指针进阶
桩桩充电官方版下载v2.4.2 安卓版
80.31MB |生活服务
中国天气通专业版最新版下载v9.1.0.4 官方安卓版
56.95MB |系统工具
新疆联通网上营业厅官方版(又名中国联通)下载v12.8 安卓客户端
118.17MB |生活服务
联通手机营业厅关怀版(又名中国联通)下载v12.8 安卓最新版
118.17MB |生活服务
28hse香港租屋网APP下载v3.14.0 手机版
51.07MB |生活服务
唐山联通掌上营业厅(中国联通)下载v12.8 安卓版
118.17MB |生活服务
新货多app下载v2.6.2 安卓最新版
65.91MB |生活服务
东梨短剧免费正版app下载v4.0.3 安卓版
61MB |影音播放
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-02-15
2022-02-14