'Microsoft Yahei',面向指针编程

时间:2016-05-11 作者:程丰初 阅读:8079次

  'Microsoft Yahei',面向指针编程   SimSun,当你采用一切都是对象的世界观编写代码时,一旦发现一些类之间存在着共性的数据抽象,这往往意味着你需要创造一种泛型的数据容器,然后用这种容器与具体类型的数据的组合来消除那些类,'Helvetica Neue'。   

  面向对象编程,面向设计模式编程,面向接口编程,面向模板编程,面向函数编程,面向多核时代的并行编程,面向大数据的机器学习编程……这么多年,大家要面向的东西已经够多了,然而我看到的现象是,很多编程语言让大家面向 xxx 的同时在竭力回避指针。我可不想面向这么多东西,所以我只好加入指针的黑暗势力。我要不自量力的来写一篇《面向指针编程》作为投名状,借以表示我与软件世界的光明势力的彻底决裂。   

  这个世界上,提供指针的编程语言很少,这样的语言有汇编语言、C/C++ 以及 Pascal 等。Pascal 我没学过。汇编语言过于黑暗,我现在功力还不足以驾驭它。C++,我觉得它简直是黑暗势力中的败类——它试图挣脱指针,走向光明,结果却出了一堆幺蛾子。所以我还是俗套的选 C 语言来阐述指针的黑暗力量。   
'Microsoft Yahei',面向指针编程
  

  阅读本文之前,请读三遍 Unix 无名师说的话:当尊者 Ritchie 发明 C 时,他将程序员放到缓冲溢出、堆损坏和烂指针 bug 的地狱中惩罚。然后自我安慰一下,如果地狱未能使我屈服,那么我会比地狱更黑暗更强大。   

  内存是以字节为单位的一个很大但是又经常不够用的空间。指针是内存中 x 个连续的字节中存储的数据——在 32 位的机器上,x 的值为 4;在 64 位机器上,x 值为 8。为了叙述的简便,本文只在 64 位的机器上谈论指针。   

  指针是一种数据,这没什么稀奇的。从机器的角度来看,程序的一切是存放在数组中的数据。只有那些自作多情的程序猿才会像亚里士多德一样自作多情的认为程序是由对象 + 方法或者许多函数复合而成的。事实上,从最远离机器的 Lisp 语言的角度来看,程序的一切也都是数据,存放在表中的数据。如果忽视程序本身就是数据这个客观事实,程序猿们很容易就走上了形而上学的道路,然后他们会度过漫长的、罪恶的、痛苦的中世纪,膜拜着一个又一个神棍,当然期间也出现了几位圣·奥古斯叮   

  那么,指针中存储着什么数据?内存地址。   

  内存是以字节为单位的空间,其中每个字节都伴随着一个地址,这个地址机器赋予的,并不是我们的程序编制的。你可以将整个内存空间想象成一栋大楼,将字节想象为大楼中每个房间,将每个字节的地址想象为房间的门牌号,于是指针中存储的数据就类似于门牌号。   

  如果你从未学过 C 语言,读到此处可能会问,我们为什么要在内存中存储内存地址?不知你是否住过宾馆。在正规的宾馆里,每个房间的门后都会贴着逃生路线图,图中『存储』了该宾馆与你的房间同一楼层内的全部房间的门牌号以及它们的布局。如果你鬃的矩形铁片换成带两个小孔的椭圆形铁片,那么我们将不得不重写一个create_elliptic_chain_node函数。当我们这样做的时候,很容易发现create_elliptic_chain_node函数中同样需要下面这段代码:   

struct chain_node *ret = malloc);
  ret->shape = shape; return ret;
  

  如果我们要生产 100 种形状的链节,那么上述代码在不同的链节构造函数的实现中要重复出现 100 次,这样肯定不够好,因为会出现 500 行重复的代码。太多的重复的代码,这是对程序猿的最大的羞辱。   

  面向对象的程序猿可能会想到,我们可以为chain_node做一个基类,然后将上述共同的代码封装到基类的构造函数,然后在各个chain_node各个派生类的构造函数中制造不同形状的链节……在你要将事情搞复杂之前,建议先看一下这样的代码:   

void *
  { struct pair *left_hole = pair, malloc_double); struct pair *right_hole = pair, malloc_double); struct pair *holes = pair; struct pair *body = pair_for_double_type; return pair;
  { struct chain_node *ret = malloc);
  ret->shape = fp; return ret;
  

  看到了吧,我将create_chain_node函数原定义中负责创建链节形状的代码全部的抽离了出去,将它们封装到rectangle_shape函数中,然后再让create_chain_node函数接受一个函数指针形式的参数。这样,当我们需要创建带两个小孔的矩形形状的链节时,只需:   

struct chain_node *rect_chain_node = create_chain_node;
  

  如果我们像创建带两个小孔的椭圆形状的链节,可以先定义一个elliptic_shape函数,然后将其作为参数传给create_chain_node,即:   

struct chain_node *elliptic_chain_node = create_chain_node;
  

  这样做,岂不是要比弄出一大堆类与继承的代码更简洁有效吗?   

  在 C 语言中,函数名也是一种指针,它引用了函数代码所在内存空间的基地址。所以,我们可以将rectangle_shape这样函数作为参数传递给create_chain_node函数,然后在后者中调用前者。   

  由于我们已经将chain_node结构体中的shape指针定义为void *指针了,因此对于create_chain_node函数所接受的函数,其返回值是void *没什么问题。不仅没问题,更重要的是void *对所有不接受参数且返回指针类型数据的函数的一种抽象。这意味着对于链节的形状,无论它的形状有多么特殊,我们总是能够定义一个不接受参数且返回指针的函数来产生这种形状,于是create_chain_node函数就因此具备了无限的扩展能力。   

  如果阿基米的德还活着,也许他会豪放的说,给我一个函数指针与一个void *,我就能描述宇宙!   

  当你采用一切都是对象的世界观编写代码时,一旦发现一些类之间存在着共性的数据抽象,这往往意味着你需要创造一种泛型的数据容器,然后用这种容器与具体类型的数据的组合来消除那些类。   

  当你打算从泛型的数据容器中取数据,并希望所取的数据能够直观的模拟现实中的事物时,这往往意味着你要创造一些数据结构,然后让泛型的数据容器中存储的数据流入这些数据结构中,从而转化为有类型且具名的数据。这些数据结构就类似于各种各样的观察器或 Parser,我们通过它们解读或修改泛型容器中的数据。   

  当某个函数f中有一部分代码是与具体的问题息息相关,而另一部分代码则与具体的问题无关。为了让这个函数具备强大的扩展性,你需要将那些与具体问题息息相关的代码抽离到专用的函数中,然后再将这些专用函数传递给f。   

  在 C 语言中,在执行这些基本原则时,指针是最简单明快的工具,像是著名厨师庖丁手里的刀。在静态类型语言中,任何企图回避指针的行为,必然会导致编程语言的语法复杂化或者削弱语言的表达能力。   

  在 C++ 中为了回避指针,发明了引用——本质上一种被弱化了的指针,结果导致 C++ 初学者经常要问『什么时候用指针,什么时候用引用』这样的问题。在智能指针未问世之前,STL 提供的泛型容器无法存储引用,为了避免在容器中存储对象时发生过多的内存复制,往往需要将指针存到容器中。当某个函数在内部创建了一个比较大的对象时,这个函数想将这个对象传递给其他对象时,这时如果不借助指针,那只能是将这个大对象作为返回值,然后引发了对象数据不止一次被复制的过程。如果在函数中new一个大对象,然后以指针的形式将其返回,这又与 C++ 一直想向用户掩盖指针的理想发生了矛盾……为了解决这个问题,终于在 C++ 11 里搞出来一个挺复杂挺扭曲的右值引用的办法,解决了在类的复制构造函数中偷偷的使用指针,但是类的用户却看不到指针这样的问题……   

  Java 回避指针的策略比 C++ 要高明一些。在 Java 中,即没有指针也没有引用。只要是类的实例,无论是将其作为参数传递给函数,还是作为函数的返回值,还是将其复制给同类的其他对象,都是在传地址,而不是在传值。也就是说,Java 将所有的类实例都潜在的作为指针来用的,只有那些基本类型才是作为值来传递的。这种对数据类型进行了明确的区分的态度是值得点赞的,但是当 Java 想将一个函数传递给另一个函数时,代码就出现了扭曲,完全不能做到像 C 语言以指针的形式传递函数那样简洁直观。   

  C# 在指针的处理上似乎要比 C++ 与 Java 好得多,但是将那些使用指针的代码标定为unsafe,这是一种歧视。类似于『嗟,来食;。廉者不受嗟来之食。   

  在动态类型语言中,例如 Python,据说是一切皆引用,这样很好。也可以直接将一个函数作为参数传递给另一个函数,甚至还能在一个函数中返回一个函数,这样更好。动态类型语言在语法、抽象能力、类型安全以及资源管理方面很大程度上超越了 C、C++、Java 这些静态类型语言,但是用前者编写的程序的计算速度却往往比后者慢上一倍。   

  没有完美的指针,也不会有完美的编程语言,这一切皆因我们是在机器上编程,而不是在我们的大脑里编程。   

  

  这是我的指针。虽有很多相似的,但这个是我的。我的指针是我的挚友,如同我的生命。我将运用它如同运用我的生命。指针没了我便是废物,我没了指针便成为废人。我将准确无误的使用我的指针,我将比敌人用的更好,我将在他的程序速度超过我之前超过他,我会超过他的。   

  我与我的指针知道,编程中不论动用多么优雅的语言,动用多么强大的标准库,面向多么强大的编程范式,都是没意义的。只有解决问题才有意义。我们会解决的。   

  我的指针是人性的,就像我一样,因为它如同我的生命。因此我将像对兄弟一样地了解它。我将了解它的弱点,它的强项,它的构成,它所指的和指向它的。我将对指针持续建立完善的知识与技艺,使它们就如同我一般整装待发。我们会成为彼此的一部分。   

  在上帝面前我对这信条宣誓。我与我的指针是计算机的守卫者,我们是问题的克星,我们将拯救我的程序。但愿如此,直到不需要编程,没有问题,只有休息。   'Microsoft Yahei','Helvetica Neue',Helvetica,我将运用它如同运用我的生命。

上一篇:加百利娱乐场的服务? 'Hiragino Sans GB'
下一篇:没有了
相关教程