欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

指针?就那么回事儿

时间:2023-04-30

作者简介:大家好,我是Ceylan_,可以叫我CC ❣️    
个人主页:Ceylan_的博客
博主信息:平凡的大一学生,有着不平凡的梦

        专栏

备战蓝桥杯    力扣每日一题PTA天梯赛

⚡希望大家多多支持一起进步~❤️
若有帮助,还请关注➕点赞➕收藏,不行的话我再努努力

前言

说到指针,想必大家都不陌生,指针的最大特点就是难以理解,它是编程中很基础也是很重要的概念,在这里我们将一步一步,逐渐揭开指针神秘的面纱。

 

目录

前言

一、指针是什么?

内存与地址

 存取内存单元数据的方法

       直接访问——按变量名存取变量的值

        间接访问——通过变量的地址来存取变量的值。

  指针变量使用注意事项

二、指向指针的指针

三、指针变量作为函数参数

按值传递

按地址传递

四、指针与数组

指针与一维数组

指针与多维数组

五、指针与字符数组

一维字符数组

二维字符数组

六、练习

实现printf函数

实现strlen函数

实现strcpy函数

每日金句


一、指针是什么? 内存与地址

在了解指针是什么之前,我们需要先了解什么是计算机的内存,什么是地址。

计算机内存大部分时候指的是随机存储器也就是RAM,用于存放当前正在执行的数据或程序对象。

内存中每一个字节都有对应的一个地址,就像一个小区中每间房子都有门牌号一样, 只有找到正确的门牌号才能找到对应的房子,通过地址可以找到内存中的字节。

当我们声明一个整型类型的变量  i  时,计算机会为这个特定的变量分配一定的空间,具体分配多少 空间取决于数据类型。

数据类型与空间关系 c语言所占字节数char1int4float4double8 存取内存单元数据的方法        直接访问——按变量名存取变量的值

                   例如:   int  x;
                                 x = 10;
                                 printf( “%d ”, x );

        间接访问——通过变量的地址来存取变量的值。

这一方法也称为「 指针访问 」,实现这一方法有两个问题需要我们解决。

 QS:    如何得到变量的地址?
              如何取得该地址下的数据呢?

为了解决这一问题,我们需要认识两种运算符。

“ & ” —— 取地址运算符。地址为一个16进制的整数。%d(10进制)或 %x(16进制)


  “ * ”—— 间接访问运算符。作用:返回该地址所指向存储单元的数据。

通过这两种运算符我们就能实现间接访问。

#includeint main(){int a; //我们声明一个变量a,假设它的地址为200int *p; //声明一个指向整形的变量p,假设它的地址为400p=&a; //p现在指向a,拥有了a的地址 a=5; //如果我们对a进行赋值,再输出p会输出什么呢?printf("%d",p); //我们会得到 200,是a的地址。&a同样表示a的地址 //那么如果我们输出&a会得到什么呢? printf("%d",&a); //我们也会得到 200,如果输出&p会得到什么呢?printf("%d",&p); //p是一个变量,它同样存在在内存中,对它进行取地址,将会得到那个变量的地址 //不难想出,我们会得到 400printf("%d",*p); //得到5*p=8; //我们将*p赋值为8,*p表示p指向地址的值这时我们输出aprintf("%d",a); //得到8 printf("&a=%d", &a );//200printf(" *(&a) = %d", *(&a) );//得到8}

  指针变量使用注意事项

(1)指针变量中只能存放地址

int *p;   p=2001;       //非法赋值


(2)不能对未赋值的指针变量进行 “ * ” 运算

   int *p;   *p=10;        //非法赋值

二、指向指针的指针

我们是否可以创建一个指针指向指针变量呢?答案是肯定的。

当我们创建一个变量  q,它是用来存放  p  的地址 ,那么这个  q  是什么类型的呢?我们需要一个特定类型的指针来存放特定类型的地址,也就是需要一个指向指针的指针。我们在变量前面加两个  **,那么现在  q  就可以存放  p  的地址了。一个特定类型的指针,为了存储p的地址,需要一个指向int*类型的指针,为此再放一个*,表示这个指针指向的是int*。

我们可以一直这样套娃下去。比如我们想要声明一个指向指针的指针的指针 ,int**是指向指针的指针,我们再放一个*,放三个***在关键词int后面  int***r   。详细如下方代码

#includeint main(){int x=5; //假设x的地址为200int *p=&x; //假设p的地址为220*p=6;int **q=&p; //q是存放指针p地址的指针,假设q的地址为240int ***r=&q;printf("%d",*p); //通过指针来对地址进行解引用和写数据,此时x=6,输出6printf("%d",*q); //输出220printf("%d",*(*q)); //输出6 printf("%d",*r); //输出240 printf("%d",*(*r)); //输出220 printf("%d",*(*(*r))); //输出6 }

三、指针变量作为函数参数

在前面,我们学会了定义指针变量,也学会了如何操作指针,在程序中如何使用指针,但是我们还没有讨论实际的指针用例,在什么情况下我们会想要使用指针变量。在这里我们将讨论指针的一个用例,这个用例就是使用指针,把指针作为函数参数,进行使用。

按值传递

我们来看这个场景

#includevoid increment(int a){a=a+1;}int main(){int a=10;increment(a);printf("a=%d",a);}

 我们声明了一个变量a,在main函数中初始化,通过调用函数对a这个变量的值+1,

但是它运行了之后发现输出a=10。为什么呢?

在函数里面声明一个变量,我们把它叫做局部变量,这样一来,我们只能在这个函数里面使用这个变量,上面例子中在函数里面increment的a,和函数main中的a,他们实际上不是同一个a。

特点:函数中对形参的改变不影响实参的值。

按地址传递

#includevoid increment(int *p){*p=*p+1;}int main(){int a=10;increment(&a);printf("a=%d",a);}

        当我们调用increment函数时,我们传过去的是a的地址,通过修改地址内存放的值来达成加一效果。

特点:函数中利用形参指针变量可直接操作实参。

四、指针与数组 指针与一维数组

在c或c++程序中指针和数组的概念经常一起出现,两者之间有很强的关系。

当我们声明数组的时候int a[5],我们就会创建5个整形的变量,分别为a[0] a[1] a[2] a[3] a[4]这5个整型数会作为一个整体,类似于这样的连续的5个整型数存储在内存中,就像这样。

地  址数组值200a[0]2204a[1]3208a[2]4212a[3]5216a[4]6

         如果我声明一个整形指针p,然后把这数组的第一个元素a[0]的地址赋给p,当我们输出p时,就会得到200,输出*p就会得到2。当我们输出p+1时我们会得到204,如果我们解引用p+1,也就是输出*(p+1),会得到3。以此类推,解引用p+2会得到第三个元素的值。

        数组还有一个特点,如果我们使用数组名a,会得到一个指向数组首元素的指针,因此我们可以这么表达语句p=a,我们甚至不需要写&,如果我们输出a,会得到数组首元素的地址200,如果对他进行解引用,输出*a,会得到它的值2。输出a+1会得到地址204,*(a+1)会得到3。以此类推,解引用a+2会得到第三个元素的值。

对于数组a中索引值(也就是下标)是i的元素,为了取得这个特定的元素的地址,可以使用&a[i],或者是简单地使用a+i,两者都可以得到a[i]的地址。

简单做个总结:

        int a[10] ,*p;      

        若     p = &a[i]           

        则     *p 等价于 a[i]

                a+i 等价于 &a[i]

                p+i 等价于 a+i 等价于 &a[i]

 我们来看一些代码示例,来巩固一下。

#includeint main(){int a[5]={2,3,4,5,6};for(int i=0;i<5;i++){printf("%d",&a[i]); //得到地址 printf("%d",a+i); //得到地址 printf("%d",a[i]);//得到值 printf("%d",*(a+i)); //得到值}}

指针与多维数组

指针与二维数组或者三维数组,甚至更多维的数组之间如何产生联系,为了理解这个,首先我们需要理解多维数组在计算机内存中的组织形式。我们先回到一维数组在内存中的组织形式,当我们声明一个一维数组的时候,比如声明一个5个元素的数组a[5],基本上相当于是在一块连续的内存上创建了「   a[0] a[1] a[2] a[3] a[4]    」这样五个整形变量。假设数组a存放在这块内存区间,a的起始地址是200,我们知道内存中的每个字节都有一个地址,整形变量是以4字节存储的,那么从200开始的4字节属于a[0],从204开始的4字节属于a[1] ,从208开始的4字节属于a[1],从212开始的4字节属于a[2],从216开始的4字节属于a[3],从220开始的4字节属于a[4]。

假设我们要创建一个二维数组b,「  int b[2][3] 」,实际上这是在创建数组的数组,我们创建了两个一维数组,每个一维数组中有三个整形数据。

我们说过,数组名返回数组首元素的指针,这一次,元素不是一个类型,而是具有3个整形的一维数组因此如果我这样写「  int*p=b 」,会有编译错误,因为b返回的是一个指向一维数组的指针,而不是一个整形的指针,因此指针的类型是很重要的。

我们可以定义一个指向一维数组的指针「 int(*p)[3]=b 」当我们使用b[0]时会返回b[0]中第一个整形数n[0][0]的指针。

a[i][j]   等价于  *(a[i]+j)   等价于   *(*(a+i)+j)

 我们来看一些代码示例,来巩固一下。

#includeint main(){int B[2][3]={2,3,4,5,6,7};int (*p)[3]=B;printf("%d",B);//400printf("%d",*B);//400printf("%d",B[0]);//400printf("%d",&B[0]);//400printf("%d",&B[0][0]);//400printf("%d",B+1);//412printf("%d",&B[1]);//412printf("%d",*(B+1));//412printf("%d",B[1]);//412printf("%d",&B[1][0]);//412printf("%d",*(B+1)+2);//420printf("%d",B[1]+2); //420printf("%d",&B[1][2]);//420printf("%d",*(*B+1)); //3//*B==B[0]//B[i][j]=*(B[i]+j)=*(*(B+i)+j)}

五、指针与字符数组 一维字符数组

当我们声明一个大小为6的字符数组C1「char C1[6]="Hello"  」用这个字符串字面值对它进行初始化假设它在内存中是这样存储的。

 Hell0200201202203204205

数组在内存中是连续存储的,假设第一个字符的地址是200,一个字符占据一个字符,所以下一个字符的地址是201,再下一个是202,以此类推。

现在我们声明一个指向字符的指针C2「char *C2=str」C2是一个指向字符的指针,仅仅使用数组的名字,实际上返回数组首元素的地址。这个表达式所做的就是,c2的值变成了200,所以C2就指向了数组的首元素。我们可以使用这个字符指针C2,就像使用C1一样,来对数组进行读写 。

例如   当我们输出C2[1]时,就会得到'e'。我们还可以直接修改这个数组,比如我们想修改索引位置是0的字符值变为'A',就可以直接运行「c2[0]='A'」。

当我们写c2[i]的时候,它其实就是「*(c2+i)」,c2是基地址,(c2+i)是会把你带到第i个元素的地址,比如C2+2会是202,如果我们加一个*,那么就是对这个地址的元素进行解引用,所以这两种写法是等效的。

二维字符数组

我们声明一个二维字符数组「char  num_1[5][6] = {"One","Two", "Three", "Four","Five"}」他在内存中是怎么样存放的呢?

 我们用不同的方法来声明这个二维数组看看有什么不同。

「char  * num_2[5] ={"One","Two","Three", "Four","Five"}」

六、练习 实现printf函数

#includevoid print(char *C)//接受数组的地址作为函数参数 {//函数并不知道这个特定的数组大小为20,只知道数组的基地址 while(*C !='')//当检测到NULL字符时退出,不在输出 {printf("%c",*C);C++;} }int main(){char C[20]="Hello";//我在里面存放了长度为5的一个字符串,使用字符串字面值的时候隐式包含了一个NULL字符 //我们不使用printf函数,来写一个自己的printf函数 ,然后把这个字符数组传过去print(C);}

实现strlen函数

#includeint StringLen (char *s){int n=0;char *p=s;while( *p != '' ) { n++; p++; }return n;}int main(){ char str[20]="how do you do?";printf("str=%s",str); printf("len=%d",StringLen(str) );}

实现strcpy函数

#includevoid CopyString(char *s1,char *s2){char *p1,*p2;for(p1=s1,p2=s2; *p2!=''; p1++,p2++){*p1=*p2; }*p1='';}int main(){char *a="I am a boy.";char b[30];CopyString(b,a);printf("b=%s",b);}

每日金句

留下恒心,恒做到底,定能成功

        本人不才,如有错误,欢迎各位大佬在评论区讨论。有帮助的话还请【关注➕点赞➕收藏】,不行的话我再努努力

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。