通俗易懂:C/C++ 指针基础知识和最常见的应用

        最近在研究cuda代码,发现自己对C语言的指针已经有些模糊,于是在B站听了江科大自化协老师的指针讲解,不得不说老师讲的确实到位,收获很大,于是写下这篇文章分享,其中也夹杂了我的一些理解和思考。


        指针与底层硬件内存联系紧密,使用指针可以操作数据的地址,实现数据的间接访问(直接根据变量名操作数据叫做直接访问)。

        一个整型变量int需要用四个字节去存储,在内存中连续存储。数组在内存空间一定是连续存储的,中间不能断开,这些是我们后续讨论的基础。

        指针变量也是一种变量,也有自己的地址,也要在内存空间中占据位置,只不过指针变量中存放的是某个或某段数据的首地址,所以每一个指针变量在同一个系统中所占的字节数是一样的。x具体是多少取决于系统的内存编码是多少位的,比如64位系统,其系统位宽为64,其内存最大可以编码到2^64位, 所以地址编码要至少是64位,才可以存储内存空间中任意位置数据的地址,这个指针才能万无一失,才有意义。

        下面是指针相关的两个操作符 &(对变量取地址) *(取指针指向的内容),还附有一些代码示例:

        特别指出数组越界和指针指向非法位置其实在本质上是相同的错误。

指针应用场景概览

        这里来到了最精华的部分,其实上面指针的基础知识很多人都知道,但是却不会用。也就是说,不知道在什么场合下要想到用指针,也不知道很多优质代码中的某处使用指针的意义在哪里。这本质上就是没有花时间来梳理一下指针的常见的应用场景的缘故。下图中基本列出了指针可能出现的几个场景(掌握前几个即可,最后一个直接访问物理地址下的数据由于基本只在嵌入式开发中使用到,可以先不做了解):

1.借助指针传递参数

        在普通的值传递中,主函数中的实参和子函数中的形参实际上是存放在内存中两个不同的地方的:在主函数向子函数传递参数时,子函数先要在内存中开辟一个新的变量,然后会将主函数的参数中的数据复制给新开辟的变量(也就是说,在传递参数时,主函数只是给了子函数自己数据的副本,所以,实参和形参完全是独立的),在子函数内部做的所有操作都是作用在这个新变量上的,丝毫影响不到主函数的变量本身。值得一提的是,子函数的这个新建的变量,是一个临时变量,其作用域只在子函数内部,一旦子函数执行完毕,这个变量就被销毁了,所以将子函数中的局部变量本身或者其地址作为返回值返回出去都没有任何意义。(下图表明实参a和形参param实际上是位于不同的地址,是完全独立的两个变量,没有任何关系,只是存在一个数值复制的过程而已)

        这样做的好处是隔离了主函数和子函数各自的数据,保证了主函数中的数据的安全性,对于子函数来说,主函数传入的参数相当于只是可读的,并没有任何办法对其做出修改。但是当要传递的参数容量很大时(比如是一个巨大的数组),依然采用值传递就会出现问题,由于传参时要进行参数数据内容的复制,所以这样做既耗时,又耗费内存空间。所以为了解决这个弊端,我们不得不做出妥协,放弃值传递的安全性,而采用指针传递。由于指针传递是将待传数据的首地址传入子函数,所以子函数完全可以根据这段首地址信息和一些额外的附加信息来找到全部的待传数据本身,这样主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制。

        但是由于指针传递直接将主函数参数数据的真实地址告诉了子函数,所以如果不加以限制的话,子函数内部完全可以对传入的主函数参数进行修改和破坏,这又对主函数参数的安全性造成了威胁,解决办法是在子函数的接收参数表中将指针变量用const关键字修饰,这样就可以保证传入的参数对于子函数来说是只读的,而无法对主函数的参数做出修改。(const关键字的保护作用:const int *array是一个常量指针)

        这里很多人可能分不太清指针常量和常量指针的概念,我来简单举个例子:

  • const int *array 是一个指向整数的常量指针。这意味着这个指针指向的整数是常量,不能通过这个指针来修改它所指向的值。但是,你可以改变指针本身,让它指向另一个整数。当需要通过指针访问数据但不允许修改数据时,使用常量指针。这在处理只读数据或敏感数据时非常有用,可以防止不小心修改数据。
  • 指针常量int * const p = &a;,这里的p是一个指针常量,它指向变量a的内存地址。虽然p的值(即它所指向的地址)不能被更改,但是可以通过*p来改变a的值。也就是说,*p = 20;是正确的操作,而p = &b;则是错误的,因为这样会尝试改变p的值,违反了指针常量的定义.    指针常量提供了一种方式来确保指针本身不会改变其指向的对象,这对于需要确保数据不被错误地覆盖或保持对特定数据的持续引用的场景非常有用。

2.借助指针输出多个参数

        C语言的函数至多只能有一个返回值,无法做到像python那样一次输出任意多的返回值。所以当遇到某些问题时(比如找出一个数组中的最大值,并统计该最大值在数组中出现的次数),按照传统的思路就无能为力了。但是借助指针可以轻松解决这一问题。只需要将要返回的参数的地址提前传入子函数中(子函数用相应数据类型的指针变量来接受这些参数的地址),然后在子函数中修改其值,就可以实现任意多返回参数的函数。

3.传递返回值

        将模内的公有部分返回,让主函数持有模块的句柄,这句话中的句柄handle可以简单理解成首地址,只要我们拿到了一段数据的首地址,我们就可以对其进行操作。

FILE *f=fopen("file_path","r");//这个函数返回传入文件的句柄handle,有了handle,就什么都有了,就可以操作这个文件了
fputc('A',f);
fputs("helloworld",f);
char a=fgetc(f);

        这里 f 其实是一个结构体指针变量,指向一个官方定义的结构体FILE,这个结构体有很多自己的成员变量。但是为了好理解,我们可以直接将 f 理解成为要操作的文件的句柄,只要拿到了它的句柄,就完全知道了这块数据的地址和结构,就可以对其进行操作了(只要抓到了它的把柄,就可以拿捏它了)。

        以上就是全部内容,感谢大家的阅读,望批评指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值