让你不再害怕指针

时间:2022-11-07 12:01:08

目录

一、前言

二、变量名

三、指针的介绍

1、指针的类型

2、指针所指向的类型

3、指针的值

4、指针的算术计算

5、运算符 

6、指针表达式

7、指针与数组

8、指针与结构体

9、指针类型转换

四、写在最后


一、前言

一听指针就头大,那就看看基础指针知识吧!

二、变量名

int p p是普通整形变量

int *p;   p是一个返回整形类型的指针。首先从p开始,先与*结合,所以说明p是一个指针,然后再与int结合,说明指针所指向的内容的类型是int型的

int *p[2] p是一个由返回整型数据的指针所组成的数组。首先从p开始,先与[]结合([]的优先级比*d的优先级高),所以p是一个数组;然后再与*结合,说明数组的元素是指针类型;之后再与int 结合,说明指针所指向的内容的类型是整型

int (*p)[2] p是一个由整型数据组成的数组指针。首先从p开始,先与*结合(()的优先级最高),所以p是一个指针;接着再与[]结合,所以指针所指向的内容是一个数组;最后再和int结合,说明数组里的元素类型是整型

int **p二级指针不常用,认识即可。首先从p开始,先与*结合,说明p是一个指针,之后再与*结合,说明该指针所指向的元素是指针,最后再与int结合,说明该指针指向的元素是整型

int p(int) p是一个函数,啥,你不会以为是指针吧,那换种写法int main();  int fun();看懂了吧。(int)说明该函数有一个整型变量的参数,再与外面的int结合,说明函数返回值是一个整型

int (*p)(int);  p是一个指向有整型参数且返回类型为整型的指针。首先从p开始,先与*结合,说明p是一个指针,接着与()结合,说明指针指向的是一个函数,然后与()里的int 结合,说明函数有一个Int型的参数,最后与最外层的int结合,说明该函数的返回类型是整型。

int *(*p(int))[2]p是一个参数为整型数据返回一个指向由指针组成的数组指针变量的函数。首先从p开始,先与()结合,说明指p是一个函数,与int结合,说明函数参数为整型,然后与*结合,说明函数返回的是一个指针。到最外层,先与[]结合,说明返回的指针指向的是一个数组,之后再与*结合,说明数组内的元素是指针,然后再与int结合,说明指针指向的内容是整型。

过于复杂的数据类型我们慎用,可能会大大降低程序的可读性


三、指针的介绍

遇见指针,问问自己:

  • 该指针是什么?
  • 该指针指向的类型是什么?
  • 该指针指向了哪里?

1、指针的类型

只需将声明中的指针名去掉,剩下的部分就是这个指针的类型

int *p 指针所指向的类型是int*
int **p 指针所指向的类型是int**
int (*p)[2] 指针所指向的类型是int (*)[2]
int *(*p(int))[2] 指针所指向的类型是int*(*)[2]

2、指针所指向的类型

决定编译器将你把那片内存区里的内容当做什么类型来看待

只需将声明中的指针名字名字左边的指针声明符*去掉,剩下的就是指针所指向的类型

int *p 指针所指向的类型是int
int **p 指针所指向的类型是int*
int (*p)[2] 指针所指向的类型是int ()[2]
int *(*p(int))[2] 指针所指向的类型是int*()[2]

3、指针的值

——或者叫指针所指向的内存区或者地址,在32位平台里,指针本身占据了4个字节的长度

  • 我们说指针的值是XXX,就相当于说该指针指向了以XXX为首地址的一片内存区域
  • 我们说指针指向了某块内存区域,就相当于说该指针的值是这块内存内存区域的首地址或指针本身所占据的内存区

4、指针的算术计算

指针可以加上或者减去一个整数。以单元为单位,移动单元= 整数*sizeof(指针所指向的类型)

char arr[20];

int *p=(int *)arr;

p++;

三问自己:

  1. 指针是什么:指针 p的类型是 int*,
  2. 指针指向类型:它指向的类型是 int,
  3. 指针指向了哪里:它被初始化为指向整型变量 arr。

程序解释:

  • 第三行中,指针 p被加了 1,编译器是这样处理的:它把指针 p的值加上了 sizeof(int),在 32位程序中,是被加上了 4.(因为在 32位程序中,int占 4个字节。由于地址是用字节做单位的,故 p所指向的地址由原来的变量 arr的地址向高地址方向增加了 4个字节。)由于 char类型的长度是一个字节,所以,原来 p是指向数组 arr的第 0号单元开始的四个字节,此时指向了数组 arr中从第 4号单元开始的四个字节
char arr[20]="I Love You";

int *p=(int*)arr;

p+=5;

三问自己:

  1. 指针是什么:指针 p的类型是 int*
  2. 指针指向类型:它指向的类型是 int
  3. 指针指向了哪里:它被初始化为指向整型变量 arr

程序解释:

  • 第三行中,p被加上了 5,编译器是这样处理的:将指针 ptr的值加上 5乘 sizeof(int),在 32位程序中就是加上了 5乘 4=20。(由于地址的单位是字节,故现在的 p所指向的地址比起加 5前的 p所指向的地址来说,向高地址方向移动了 20个字节)没加 5前的 p指向数组 arr的第 0号单元开始的四个字节,加 5后,p已经指向了数组 arr的合法范围之外了。这种情况在应用上会出问题,出现野指针的情况。
#include<stdio.h>

int main()

{
   char arr[20]="You_love_me";
   char *p=arr;
   char **p2=&p;
   printf("**p2=%c\n",**p2);
   p2++;
   printf("**p2=%c\n",**p2);

   return 0;

}

请问输出是什么,害怕我们先入为主,就先正确解释

三问自己:

  1. 指针是什么:指针 p的类型是 char*,指针p2的类型是char**
  2. 指针指向类型:p指向的类型是 char,p2指向的类型是char*
  3. 指针指向了哪里:p指向数组arr首元素地址,p2指向的p的地址

程序解释:

  • 正解:当执行 p2++;时,会使指针加一个 sizeof(char*)(前面已经提及在32位平台中,指针变量的大小占4个字节),即&p+4;那*(&p+4)指向哪呢?仔细想想p的类型,这可不是指针数组能存许多指针,所以最后的输出会是一个随机的值,或许是一个非法操作
  • 错解一输出结果Y和o。   p2是一个二级指针,当执行p2++时,会使指针加上sizeof(char ),违背了指针运算的规则,移动单元= 整数*sizeof(指针所指向的类型)
  • 错解二输出结果Y和l 。  p2所指向的类型是char*,当执行p2++时,会使指针加上sizeof(char *),有人会认为sizeof(char *)的值为1,违背了在32位平台中,指针变量的大小占4个字节 ,还有人会认为按照4个字节,那么进行一次取值运算不就指向数组中的第五个元素吗?其实不然,详见上面的正解。

特别注意:

  • 两个指针不能进行加法运算。因为得到的结果指向一个未知的方向
  • 两个指针可以进行减法运算,但是类型必须相同

5、运算符 

浅显理解:有点欢喜冤家,双向奔赴的感觉

  • &功能是取地址 ,把一个变量转成地址,&a运算结果是指针,类型是a的原类型再加个*,所指向的类型是a 的类型,指向的地址是a的地址
  • *的功能是解引用,通过地址找回变量 ,*p运算结果不定,类型是p指向的类型,占用的地址是p所指向的地址       
int a=6,*p,**p2;

p=&a;
*p=12;
p2=&p;

三问自己:

  • p的类型是int*,指向的类型是int,指向的内容是a的地址
  • *p的类型是int,指向的类型是int,指向的内容是变量a=12
  • p2的类型是int**,指向的类型是int*,指向的内容是p的地址

6、指针表达式

一个表达式的结果如果是一个指针,那表达式就是指针表达式

int a, b, arr[10];
int *p1;
p1=&a   //&a是一个指针表达式

int **p2=&p1;//&p1是一个指针表达式
*p2=&b       //*p2和&b都为指针表达式

7、指针与数组

int arr[8]={7,6,5,4,3,2,1,0};
arr[0]等价于*arr
arr[3]等价于*(arr+3)

数组名可以看成首元素地址,前面已经讲过了指针的加减整数的运算,在这里

  • arr 类型是int*,
  • 指向类型是int(数组的单元类型),
  • 指向数组中的第0个单元

8、指针与结构体

struct student
{
   int a;
   int b;
   int c;
};
struct student st={1,2,3};//对结构体中的a,b,c初始化

struct student *p=&st;
//声明一个指针,类型是student*
//             指向类型student
//             指向结构对象st

通过指针访问:

  • p->a;  或者  (*p).a;
  • p->b;  或者  (*p).b;
  • p->c;  或者  (*p).c;

9、指针类型转换

float a=0.123;

float *pa1=&a;//正确使用

int *pa2;

在上面的例子中,pa1是正确的,假如我们想让指针pa2指向实数a,应该怎么办?是用下面的语句吗?

pa2=&a;

不对。因为指针 pa2的类型是 int*,它指向的类型是 int。表达式&a的结果是一个指针,指针的类型是 float*,它指向的类型是 float。(前面有讲解)两者不一致,直接赋值的方法是不行的。

为了实现我们的目的,需要进行"强制类型转换":pa2=(int*)&a; 强制类型转换相当于新产生一个指针,并不影响改变原来的各项属性

四、写在最后

????????点赞,你的认可是我创作的动力!

⭐收藏,你的青睐是我努力的方向!

✏️评论,你的意见是我进步的财富!