锋利的指针

       谈及指针,想到的应是C语言,C语言的灵魂就在于指针,也是C语言最难的知识之一,要想很好的掌握指针,一定要从内存字节的角度去思考问题.指针其实就是代表了一个内存地址,当然是虚拟内存。我在初学c语言到指针这一部分的时候,根本不知道这是一个什么东西,不明白int *p = &x是什么意思。知道后来通过数据结构的练习和对操作系统以及内存的理解,对指针有了一个认识,不禁感叹指针真是太强悍了,但需要十分小心的使用,用之不当就会带来很严重的后果,所以说指针是很锋利的,就在于你是否可以用好指针。

      先说指针的大小,无论任何类型的指针,其大小都是4字节(32位下)。为什么呢?以前我想这个问题的时候就认为这应该规定吧,但是仔细学习理解后发现不然,32位系统最大支持的内存是4G,那么其内存地址编码范围是0x0000000~0xFFFFFFFF,而用4字节就完全可以表示这些内存地址了,一个字节有8位,1byte=8bits,4字节就可以表示32位二进制,例如我们在C语言中申请的变量其内存地址可能是这样的:0028FF3C,四字节是00  28  FF 3C

其展开成二进制为:0000 0000   0010 1000   1111 1111   0011 1100

所以指针的大小都是4字节,它只是表示一个内存地址,4字节就足够表示所有的地址了。

     再说指针的指类,这是一个很头疼的问题,指类起到的作用是:指针可以自加++自减--运算,那么进行这些运算进行是,指针是移动多少个字节呢?这就是指类所起到的作用,例如int *p,这就是一个指向int数据类型的指针,其指类就是4字节,每一次p指针自加1,那么它的内存地址就增加四个字节,而char *p,同样每次指针自加则是内存地址增加一个字节。而void *指针,没有指定类型,每次进行自加是增加一个字节,内存的最小单位。void *指针也意味着它可以指向任何类型的指针,进行强转,这似乎有点像java中的泛型,或者说泛型的底层实现是否依靠的void *指针,这是我的一个猜想。void *指针放在文章后面再说。

前些日子看的一本书《软件随想录》,书中的文章十分受用。像现在的热门语言java,Python等等,python可以说封装的非常好了,工具非常全面了,没有指针,这些语言都不用考虑内存泄漏问题,只管用,这些语言的出现的确是帮程序员做了很多事情,以前没有这些语言的时候,实现一个功能可能需要自己写几十行代码,现在的新生语言可能就一两行,里面都帮你封装好。你如果只是学会用而不愿意深入了解其中的原理,就好像hashmap的底层实现是什么?那么随着你后面问题的深入,一定会卡住,真的就是这样,我以前是非常不愿意看类如:操作系统、计算机原理,这一类的书籍的,觉得很枯燥完全看不进去,但是后来遇到的问题弄清楚底层的原理之后,理解问题的深度完全不一样了。说java比c难是因为java中一些框架的难,不容易理解,拿java语言的语法和c相比,java要简单的多了。c++才是最难的。

   

指针数组和数组指针,分清楚一个是数组,一个是指针。在另一篇文章任何二维数组的实现中说过。

指针数组,“存储指针的数组”,int *q[3],q是一个数组,每一个元素是指针,可以指向任意一个一维数组,其实是指向了一个内存地址即是数组的首地址,这个就很凶悍了,我有三个指针放在一个数组里面,打个比方,第一个指针q[0]我想指向长度为5的数组,第二个指针q[1]我想指向长度为2的数组,第三个指针q[2]我想指向长度为6的数组,就像这样:

int *q[3]; 
int a1[] = {1,2,3,4,5};
int a2[] = {6,7};
int a3[] = {8,9,10,11,12,13};
 
q[0] = a1;
q[1] = a2;
q[2] = a3;

数组指针:    int (*p)[4], p它是一个指针,指向数组的指针,指向一个长度为4的一维数组,这个4你也换成更大的数组,它的指类就是int[4],所以p的步长不再是一个int大小而是4个int大小的长度,p+1要扩过4个int元素。可以让其指向一个二维数组,p每加一,相当于在二维数组里面跨过一行。注意此时指针的指类不是int!

int (*p)[4];
int array3[3][4] = {5,2,6,6,2,5,1,5,6};
p = array3;     //这里报警告的话 强转一下

野指针:你只是一个没有对象的野指针!这个问题经常在动态分配内存的地方遇到,释放内存后没有将指针置空,导致指针仍旧指向一个已经释放掉的内存空间,还有就是定义指针未初始化。如果你经常malloc,calloc肯定有经历过这个野指针的痛苦。

关于void*指针,void是无类型,它是没有类型的指针,那么意味着void*指针可以指向任何类型数据,通过强转来达到,强转的过程实际上是一个多少字节为一个单位数据的过程。

 看下面的几行代码,这个输出的结果是1684234849

char str[] = {"abcdcdedfefe"};
void *p1;

p1 = str;
printf("%d\n", *(int *)(p1 + 0));

分析如何得到这个过程还是要分析如何在内存中分布,又是如何从内存中将数据取出来的。存放数据是高高低低分低低高高:

高高低低--内存中的高位存放数据的高位,内存中的低位存放数据的低位

高低低高--内存中的高位存放数据的低位,内存中的低位存放数据的高位

这张图来自西安微易码科技-内存映像图

对str这个字符串强转int*之后那么此时的这个void*指针就可以看做是指类为int的一个指针,指向一段内存是那个字符串的内存

printf函数输出的时候,输出void*指针首地址,是按照%d输出,所以就连续四个字节的数据。

1684234849转化成16进制是:64 63 62 61(H),存放在内存里面按照高位数据放在内存高字节处,低位数据放在内存低字节处:就是:61 62 63 64刚好对应的是abcd的ASCLL码,这也就是这个数字由来的过程。

void* 指针有的用法太多了,我可以写一个链表是void*类型,这个链表可以处理的数据类型覆盖面就多了,可以认为这是一种泛型的实现手段。可以写一个快排,接受数据类型是void*,stdlib库实现的快排就是这样的参数,深深佩服这一点。

void k_qsort(void *base, size_t len, size_t width, int (*cmp)(const void *a, const void *b)) ;

这个快排自己简单的实现了,最难的地方,就是在于排序中你怎么解决交换数据,用什么类型去交换数据,最后想到的是最小的单位是字节,就用char类型挨个交换每个数据的字节。

已放至github:快排

       指针在c++中更是提高了难度,我认为c++最强悍的地方就在于,有指针、引用还可以面向对象编程。这远比java强大,但是难度也大大增大了,在c++中管理内存是一件很麻烦的事情,你要明白this和*this是不一样的,如果没有c基础的人去直接接触c++是要走很多弯路的,这在java里面是体会不到的,就好像你吃完饭直接擦嘴走人就行了有人帮你把餐盘收拾好,或者说java里面一切都是对象,都是指针,所以经常也会看到空指针异常,但是java它没有函数传递参数时候用指针或者引用直接传递,你要在函数里面改变一个变量的值,必须要将其装在一个对象里面才能改变,要么包裹在一个对象里面,要么放数组里面,否则它就是一个形参,永远不会改变值。

      对于指针的理解一定要站在内存的角度去思考,一门高级语言,提供的功能少了,自己做的事情就多了,提供的功能多,自己的任务就相对来少一点。如果你愿意深入底层了解程序运行的过程,去了解计算机,指针一定是不容错过学习的内容之一。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值