指针的魅力

指针说:love me,love me!

但是他对指针说:I hate u,I hate u!

……

 

指针仅仅是作为指针,我们可以把它当做有用的工具,为我们提供便利与好处。说起工具不得不让我想起一样东西——锄头,因为原人类有了锄头才使人类文明进入了开荒造田的农业时代,解决了温饱,开启了人类新纪元。可以这么说吧锄头使人类文明得到进步,没有锄头也就没有今天的我们,其地位与重要性可想而知。那么我们的指针何以能发挥像锄头那样惊人的魅力呢?

 

魅力1 算法之找我的名字——简单,灵活,快捷

以下算法功能是在一个字符串中查找长度为8的一个字符子串,比如我的名字“ZhanHang”就是一个8长度的字串。算法解释:因为字串myname的长度为8,也就是它是一个8字节的内存连续的数组,而myname指向这一段内存。又Long long 指针类型是一个指向8字节内存的类型,因此就可以将myname转换成long long 类型指针,如此在进行子串的比较时,就可以直接比较两个long long 类型的变量即可,免去了对子串进行遍历的麻烦。详情请看代码。

view plain
  1. #include <stdio.h>  
  2.   
  3. int find_my_name(const char* str, int str_len, const char* myname)  
  4. {  
  5.     long long *pkey = (long long*)myname;  
  6.     long long *curr_str = NULL;  
  7.     str_len = str_len - sizeof(long long); //此处是为了for循环作的优化处理,因为后面7个字符不需要遍历检测了  
  8.     for(int i=0; i<=str_len; i++)  
  9.     {  
  10.         curr_str = (long long*)&str[i]; //得到一个新的子串  
  11.         if( *pkey ==  *curr_str ) //判断两个字串是否相等  
  12.             return i+1;  //返回子串在字串中的位置  
  13.     }  
  14. }  
  15. /** 
  16. Author:花心龟 
  17. Blog:http://blog.csdn.net/zhanxinhang 
  18. **/   
  19. int main()  
  20. {  
  21.     char str[]="alZhanhangadf";  
  22.     char myname[]="Zhanhang";  
  23.     printf("my name at %d\n",find_my_name(str,sizeof(str)/sizeof(char),myname));//输出结果是3  
  24.       
  25.     return 0;  
  26. }  
小结:指针实际上指向一内存空间,其内存空间的大小由指针类型而定。灵活的应用指针是能够为编程带来便利的。

(题外语:作者使用此实例旨在通过灵活应用指针的例子,帮助理解指针,并通过理解指针作为本文介绍指针魅力的起点,实际工程中并不赞同此方法,因为本方法存在扩展性差,移植性差等问题。)



魅力2 迭代器——高效

“迭代”一词在汉语里面是“更替”的意思,也就是说“迭代器”的意思就是指用来做更替操作的工具。实例:实现算法使一个字符串颠倒顺序,实现步骤:构建两个迭代器p 和 q ,在一次遍历中,p的位置从字串开头向中间不断更替,q从字串末尾向中间不断更替,

然后每次交换p和q所指向的内容即可,直到p和q在中间相遇,这时循环次数刚好等于字串的长度_l/2。

实现代码:

view plain
  1. void reverse(char *_str,int _l) //反转函数,_l指要反转字串的长度  
  2. {  
  3.  char *p=_str,*q=_str+_l-1,temp;  
  4.   
  5.  while(q>p)  
  6.    {   
  7.      temp=*p;  
  8.      *p=*q;  
  9.      *q=temp;  
  10.    
  11.      p++;  
  12.      q--;  
  13.    }  
  14. }  

我若不用迭代器p和q呢?用两个变量i和j作为str的下标,也就是说访问元素的方式变为:str[i],str[j],如下

view plain
  1. void reverse(char *_str,int _l) //反转函数,_l指要反转字串的长度  
  2. {  
  3.   int i=0,j=_l-1,temp;  
  4.    
  5.  while(j>i)  
  6.    {   
  7.      temp=str[i];  
  8.      str[i]=str[j];  
  9.      str[j]=str[i];  
  10.    
  11.      i++;  
  12.      j--;      
  13.    }  
  14. }  
这样并不比上面用迭代器的情况好,而且要糟很多,因为这样用str[i],str[j]的下标的方式访问元素时,需要先对str所存的数组首地址进行一次加减运算才能正确得到第i个、第j个值(读者可在任何一款编译器上进行反汇编查看),上面一共出现了5次下标访问str元素,情况可想而知。小结:迭代器p和q在这里起到了不可更替的作用,正是因为p和q使算法效率得到了提升。

 

 

魅力3 函数指针——高扩展性

何为函数指针:函数指针就是指向某个函数的指针,指针变量存储的是函数的地址。

如何定义一个函数指针:定义为 [ 返回类型 (*pfun) (形参,…) ]。

如何使用函数指针:pfun(实参...) 或者(*pfun)(实参...)。

现看一个实例:

view plain
  1. #include <stdio.h>  
  2. int fun0(const int &a)//定义谓词1 (我们把作为函数参数的函数称为谓词)  
  3. {  
  4.     if(a==1)  
  5.         return 1;  
  6.     else  
  7.         return 0;  
  8. }  
  9. int fun1(const int &a)//定义谓词2  
  10. {  
  11.     if(a<10)  
  12.         return 1;  
  13.     else  
  14.         return 0;  
  15. }  
  16. /*根据whatCondition函数指针所指的函数(谓词)设置的条件打印数组*/  
  17. void print_arr_if(const int *arr, int alen, int(*whatCondition)(const int&))  
  18. {  
  19.     for(inti=0; i<alen; i++)  
  20.     {  
  21.         if( whatCondition(arr[i]) )  
  22.             printf("%d ",arr[i]);  
  23.     }  
  24.     printf("\n");  
  25. }  
  26. /** 
  27. Author:花心龟 
  28. Blog:http://blog.csdn.net/zhanxinhang 
  29. **/  
  30. int main()  
  31. {    
  32.     int arr[]={1,1,2,1,11,12,3,10};  
  33.     print_arr_if(arr,sizeof(arr)/sizeof(int),fun0); //打印结果为1 1 1  
  34.     print_arr_if(arr,sizeof(arr)/sizeof(int),fun1); //打印结果为1,1,2,1,3  
  35.    
  36.       
  37.     return 0;  
  38. }  

小结:以上利用whatCondition函数指针大大提高了print_arr_if函数的扩展性,通过对谓词的定义几乎可打印你想要的任何数组元素。

 

魅力4 函数传递——高效,实用

c/c++有三种函数传递方式,它们分别是值传递,指针传递和引用传递,其中指针传递与引用传递都是将地址传进函数,基本上没什么区别。

指针及引用作为函数传递类型,与值传递相比,是高效的。为什么高效呢?请看如下:

现有如下结构体:

view plain
  1. typedef struct structType  
  2. {  
  3. int i;  
  4. char arr[100];  
  5. }structType;  
  6. //一个print函数的定义:  
  7. void print0(const structType data)  
  8. {  
  9.        //printf something about data  
  10. }  
  11. //另一个print函数的定义:  
  12. void print1(const structType *pdata)  
  13. {  
  14.        //printf something about data  
  15. }  

通过比较发现,print1比print0有明显的效率优势,因为print0是值传递,当值传进去时,必须要开辟一个structType那么大的内存空间来乘装这些值,这就要相当大的一部分资源消耗,而print1是指针传递,传进去的是地址,一个地址只需4字节内存空间,使用时解析其指针即可,因此它比print0更高效更实用。

再看我们如何用一个函数交换两个变量的值:

view plain
  1. swap(int &a, int &b)  
  2. {  
  3.      int temp = a;  
  4.      a= b ;  
  5.      a = temp;  
  6. }  

交换函数只有使用引用传递或指针传递才能改变形参a和b所指向的内存的值。

小结:函数的指针传递与引用传递提供了函数传递的一个高效的途径,另外,若要使用某个函数来改变某一变量的值唯有使用指针传递或引用传递给该函数,这体现了实用性。



魅力5 链——实用

在链表,二叉树等数据结构中,指针作为链接两个节点的“链”,时刻牵绊着它们的逻辑关系。指针为这些数据结构提供了高效实现的可能,因此使我们能够在内存世界里完成对自然事物的逻辑构造,使我们的计算机更具实用价值。

 


魅力6 动态内存分配——实用

动态内存分配离不开指针,假如把内存空间当做容器,每个程序都有它自己的容器,它如同一个容器补给器,可随时搭载从外界而来的一个个容器,随时为您排忧解难,为程序分忧。不仅如此,你可以通过该补给器将申请而来的容器送回去,让它为别地应用程序所利用。

 

……


另:如何善用指针

首先看什么是野指针和空指针

空指针: 如 int *p = NULL 这就定义了一个指针,通常NULL是一个零值,操作系统定义内存64kb以下的内存单元是不可访问的,所以像如 *p = 9 这样给他赋值是系统不允许的,将会发生内存报错。

野指针: 如 int *p就是一个野指针,可以看到它在创建时没有赋初值,所以它的值是一个随机数,也就是乱指一气,通常都是指向了不合法的内存段,所以使用它也会内存报错。还有指针p被free或者delete之后也会成为野指针,因为它所指的内存空间被释放之后,变成了一个不合法内存段。野指针,它顾名思义它就是一个野指针,它是没有主人领养的野兽,凶猛残暴,用它你就得自食其果。

指针防灾措施:一,养成好习惯,在每声明一个指针时便对它赋初值NULL。二、养成好习惯,在指针被free或者delete之后,对其赋值为NULL。三、在一定做好一和二的基础下,就可以在使用指针之前只用if语句判断指针是否为空,以做到防错。四、避免强制类型转换指针,除非能确保转换前后类型所占用的空间大小是一致的。详情可看此文:http://blog.csdn.net/zhanxinhang/article/details/6719387。


总结:指针可以当做一很好的工具,只要我们好好理解它善用它,它就能发挥它所具有的魅力。也许要真正理解指针需要些许时间去磨练去思考,但是有付出总有回报,回报过后都是值得的。现如今由于java,c#等语言的盛行,似乎指针的用武之地变少了。那么是不是有了java,c#等理解指针就不重要了呢?非也,非也。我从高中就开始接触编程,接触指针,上到大学才开始接触java,c#等,从中我发现这些语言没了指针确实是会少了些烦恼,尤其是对初手。但,对指针的理解(注意指针与内存息息相关)有助于您了解java等的底层世界,如垃圾回收机制,java虚拟机等,我认为了解这些对一个走专业化道路的java程序员是必须的。多了解点底层,多一点自由,少一点束缚。虽然要说让指针发挥得像锄头那样的惊人魅力,有点大,但至少在计算机世界里,它是的,因为有了它才有了像java这样神奇的东西。

 


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/254555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python多进程

2019独角兽企业重金招聘Python工程师标准>>> python多进程 进程简介 进程是程序在计算机上的一次执行活动。当你运行一个程序&#xff0c;你就启动了一个进程。显然&#xff0c;程序是死的(静态的)&#xff0c;进程是活的(动态的)。进程可以分为系统进程和用户进程。…

halcon彩色图像颜色处理算子,持续更新

目录apply_color_trans_lutcfa_to_rgbtrans_to_rgbclear_color_trans_lutcreate_color_trans_lutgen_principal_comp_translinear_trans_colorprincipal_comprgb1_to_grayrgb3_to_graytrans_from_rgbapply_color_trans_lut 功能&#xff1a;申请使用颜色查找表。 cfa_to_rgb …

夺命雷公狗---node.js---20之项目的构建在node+express+mongo的博客项目5mongodb在项目中实现添加数据...

我们上一步就引入了mongodb了&#xff0c;那么下一步就要开始写添加数据了&#xff0c;不过有个前提是先将表单的数据处理好&#xff1a; 最基本的这部现在已经成功了&#xff0c;因为最基本的这步就是先将表单处的提交方式和提交地址给处理好&#xff0c;这里和PHP的基本上是一…

重新绑定ItemsSource先设置ItemsSource = null;的原因

即报错信息为&#xff1a;在使用 ItemsSource 之前&#xff0c;项集合必须为空。原因&#xff1a;Items和ItemSource&#xff0c;只能有一个生效&#xff0c;想用其中一个&#xff0c;另一个必须是空。重新绑定ItemSource&#xff0c;虽然绑定的集合对象Clear了&#xff0c;但是…

敏捷开发学习

Scrum 敏捷开发&#xff0c;绩效管理&#xff0c;团队管理&#xff0c;企业管理&#xff0c;ASP.net MVC 敏捷开发 培训|咨询 工具开发 课题研讨 http://blog.csdn.net/cheny_com/article/category/794542 http://blog.csdn.net/vincetest/article/category/650747 http://blog…

Git commit后,本地代码丢失解决方法

问题描述&#xff1a; 提交代码时&#xff0c;rebase了两次&#xff0c;本地代码丢失了&#xff0c;吓得我差点跳起来。解决方法如下&#xff1a; 1、执行命令&#xff1a; git reflog d6ea731 (HEAD -> dev, origin/dev, master) HEAD{0}: checkout: moving from master to…

Edges图像边缘处理halcon算子,持续更新

目录close_edgesclose_edges_lengthderivate_gaussdiff_of_gaussedges_coloredges_color_sub_pixedges_imageedges_sub_pixfrei_ampfrei_dirhighpass_imageinfo_edgeskirsch_ampkirsch_dirlaplacelaplace_of_gaussprewitt_ampprewitt_dirrobertsrobinson_amprobinson_dirsobel_…

Android存储数据方式

可以查看Android开发文档中的&#xff1a;/docs/guide/topics/data/data-storage.html Android provides several options for you to save persistent application data. The solution you choose depends on your specific needs, such as whether the data should be privat…

防止cpu 一直被占用 sleep(0) 和 yield

在java的Thread类中有两个有用的函数&#xff0c;sleep和yield&#xff0c;sleep就是线程睡眠一定的时间&#xff0c;也就是交出cpu一段时间&#xff0c;yield用来暗示系统交出cpu控制权。这两个函数在多线程开发的时候特别有用&#xff0c;可以合理的分配cpu&#xff0c;提高程…

做一个有胆识的有为青年

1、一个年轻人&#xff0c;如果在这四年的时间里&#xff0c;没有任何想法&#xff0c;他这一生&#xff0c;就基本这个样子&#xff0c;没有多大改变了。 2、成功者就是胆识加魄力&#xff0c;曾经在火车上听人谈起过温州人的成功&#xff0c;说了这么三个字&#xff0c;“胆…

jstack应用-查找CPU飚高的原因

场景 在系统上线后&#xff0c;经常会遇到运维的同学跑过来说&#xff1a;“这次发版后&#xff0c;cpu线程使用率到一场&#xff0c;到100%了”。这时候不要慌&#xff0c;可以使用堆转储来分析到底是哪个线程引起的。 查找元凶 [rootjava_mofei_01 test]# top Mem: 16333644…

Enhancement增强图形halcon算子,持续更新

目录coherence_enhancing_diffemphasizeequ_histo_imageilluminatemean_curvature_flowscale_image_max_shock_filtercoherence_enhancing_diff 功能&#xff1a;执行一个图像的一个一致性增强扩散。 emphasize 功能&#xff1a;增强图像对比度。 equ_histo_image 功能&am…

音频中采样位数,采样率,比特率的名词解释(转)

采样位数&#xff08;采样大小&#xff09;&#xff1a; 采样位数可以理解为采集卡处理声音的解析度。这个数值越大&#xff0c;解析度就越高&#xff0c;录制和回放的声音就越真实。我们首先要知道&#xff1a;电脑中的声音文件是用数字0和1来表示的。所以在电脑上录音的本质就…

WebSocket实时异步通信

WebSocket实时异步通信 【一】WebSocket简介 WebSocket是HTML5推出一个协议规范&#xff0c;用来B/S模式中服务器端和客户端之间进行实时异步通信。 众所周知&#xff0c;传统的HTTP协议中&#xff0c;服务器端和客户端通信只能是在客户端发送一个请求之后&#xff0c;服务器端…

多线程和多进程的区别(小结)

分类&#xff1a; linux 2009-06-19 09:33 11501人阅读 评论(15) 收藏 举报 很想写点关于多进程和多线程的东西&#xff0c;我确实很爱他们。但是每每想动手写点关于他们的东西&#xff0c;却总是求全心理作祟&#xff0c;始终动不了手。 今天终于下了决心&#xff0c;写点东西…

redis-cli使用密码登录

redis-cli使用密码登录 注意IP地址要写正确&#xff01; 学习了: https://blog.csdn.net/lsm135/article/details/52932896 https://blog.csdn.net/zyz511919766/article/details/42268219 https://zhidao.baidu.com/question/756651357338691604.html 登录后 auth pass 或者 r…

FFT快速傅式变换算法halcon算子,持续更新

目录convol_fftconvol_gaborcorrelation_fftdeserialize_fft_optimization_dataenergy_gaborfft_genericfft_imagefft_image_invgen_bandfiltergen_bandpassgen_derivative_filtergen_filter_maskgen_gaborgen_gauss_filtergen_highpassgen_lowpassgen_mean_filtergen_sin_band…

仿照vue实现简易的MVVM框架(一)

代码github地址&#xff1a; https://github.com/susantong/myMVVM 主要的方法有&#xff1a; compile 深度遍历前端界面的节点&#xff0c;将其复制进一个addQuene队列中pasers 遍历所有的节点&#xff0c;并将节点包装成一个含有本节点、自定义属性及属性值的对象。要想实现双…

tomcat 启动时内存溢出

在tomcat_home/bin目录下找到catalina.bat&#xff0c;用文本编辑器打开&#xff0c;加上下面一行&#xff1a; set JAVA_OPTS -Xms1024M -Xmx1024M -XX:PermSize256M -XX:MaxNewSize256M -XX:MaxPermSize256M 解释一下各个参数&#xff1a; -Xms1024M&#xff1a;初始化堆内存…

@angular/platform-browser-dynamic

/** experimental */ export declare class JitCompilerFactory implements CompilerFactory {createCompiler(options?: CompilerOptions[]): Compiler; }export declare const platformBrowserDynamic: (extraProviders?: StaticProvider[] | undefined) > PlatformRef;…