指针的魅力

[置顶] 指针的魅力

分类: 1.c/c++   19689人阅读  评论(100)  收藏  举报
java 数据结构 算法 null fun 编译器


指针的魅力


指针说: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 类型的变量即可,免去了对子串进行遍历的麻烦。详情请看代码。

[cpp]  view plain copy
  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。

实现代码:

[cpp]  view plain copy
  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],如下

[cpp]  view plain copy
  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)(实参...)。

现看一个实例:

[cpp]  view plain copy
  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++有三种函数传递方式,它们分别是值传递,指针传递和引用传递,其中指针传递与引用传递都是将地址传进函数,基本上没什么区别。

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

现有如下结构体:

[cpp]  view plain copy
  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更高效更实用。

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

[cpp]  view plain copy
  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这样神奇的东西。

 

…………………………………………………………………………………………………………………………………………………………………………………………………………

本人水平有限,若有什么不对的地方还望不吝指出。 欢迎email:[email protected] ^_^

<版权所有,转载不忘注明出处:http://blog.csdn.net/zhanxinhang,及作者:花心龟的扮演者ZhanHang>

本文已于2011年9月2日重新修正完毕。各测试代码(伪代码除外)已修正优化并在编译器上编译通过,欢迎查阅。^_^


你可能感兴趣的:(1.c/c++)