一种常用的重构方式

重构是一个程序员很好的编程习惯。有时候难免写出一些不好看的代码,空闲时候你就可以用重构这种方式来提升代码质量。

比如类似这种模式:

if (! pDev->m_IsInited)  pDev->Init();

这段代码从性能上来说没有什么问题,意思也很明显,如果“已经初始化”标记为false,则调用Init()这个初始化函数做一些动作。

但是代码实际上可以改进的更好,最大的问题是对于pDev的使用者来说,没有必要知道m_IsInited这个变量。我们可以推出第二版:

if (! pDev->IsInited() )  pDev->Init();

这时候类变量被函数调用替换,好处是你可以在IsInited()函数里面做更多的操作,或者把m_IsInited改个名字而不需要去修改每行类似上面的代码。

当然还有一种更简单的方案:

pDev->Init();

而IsInited()判断则放在Init()里面,这时候pDev的使用者更省事,我只要调用就好了,有没有初始化过,那是pDev自己的责任,跟使用者无关。

需要注意的是,Init()此类函数也许会有输入参数,那时候就需要比较复杂代码来保存状态以免出错。另外这个方式主要用于防止Init反复重入,如果某个函数需要反复调用且每次调用结果可能不同,那就不能使用类似手段了。

这属于非常非常基本但也是很常用的一种重构方式,在这里记录一下。

C语言指针及数组

C语言的指针与数组是一个比较高阶的话题,有些书就是照本宣科,读者看完会认为自己明白了。真要是碰到一些模棱两可的问题,就发现自己了解的还不够深入,那时候就棘手了。我在前面提到的《C语言趣味题目》http://sunxiunan.com/?p=1647就是一个例子,如果你对里面的题目都完成的非常完美,那指针与数组的话题其实也没必要看了,你一定已经是一个C语言方面的高手。

C语言的指针,是C语言里最为灵活最有力量也最容易产生问题的强力武器。数组相对来讲花样少一些,但也有些比较容易出问题的知识点。

如果你想系统深入了解指针,我推荐你完整系统的阅读这几本书《C Programming language》也就是(K&R圣书),第二本是《C专家编程》,里面关于数组与指针的阐述尽管已经过去十多年依然是熠熠生辉,没有其它书籍能赶上,另外还可以看看《C与指针》这本书,其实也是一本C语言系统教材,把指针单独提出了,也体现了指针的强大威力,还有一本是《C陷阱与缺陷》,也是非常值得一读。

如果看完这些书,可以看看几个专门阐述C指针或者包含相关内容的文档,比如:

http://home.netcom.com/~tjensen/ptr/pointers.htm A TUTORIAL ON POINTERS AND ARRAYS IN C

http://publications.gbdirect.co.uk/c_book/ 这本书在线免费阅读

http://boredzo.org/pointers/ Everything you need to know about pointers in C

http://www.cs.cf.ac.uk/Dave/C/node10.html Common Pointer Pitfalls 这是从wiki的pointer页面上发现的

http://www.knosof.co.uk/cbook/cbook.html New C Standard, 云风在他的blog推荐过。

http://learn.akae.cn/media/index.html Linux C编程一站式学习

———————————

C语言中的指针是什么,数组是什么,该如何定义初始化,我在这里不多讲,任何一本C语言的教材或者我前面推荐的K&R都有很详细的解释。

关于指针与数组最经常提到的问题就是在定义为functionA(int * p),然后可以直接把int numArray[5]这样的数组直接作为参数传入,或者声明declaration与定义definition不匹配,如extern定义为char* 但是实际上是char[]。

其实我们只要记住指针与数组的几个不同点,到时候类似问题就很容易搞掂了。在《C专家编程》里面列出一些,我这里简述一下:

第一点也是最关键一点,指针访问是间接的,也就是指针存放的是一个地址的值,存放的是被指内容的地址,其实类似一个中转站或者114的功能,如果想取得指针所指向的内容,必须做提领(deference)操作,实际上类似于两个步骤(先取得指针的内容也就是p存放的地址值,然后取得存放地址里面内容)。而数组里存放的就是数组的值,不是什么间接引用的地址,比如我们要取arr[5]的值,只要从arr开始数5个位置,里面就是a[5]的内容。

另外一点不同是,假如我们有个数组int array[5],数组的地址&array与数组名字array本身代表的地址是不一样的。&array实际上是一个int (*p1)[5]类型的指针,p1每一步递增递减都是sizeof(array),也就是5个int长度。而array相当于&array[0],也就是第一个元素(element)的地址,类型是int *p2,p2每一步递增都是sizeof(int)。这个区别在指向二维数组或者多维数组指针里非常需要注意。

再有一点不同是,一般指针类型(除了int *const p这种)都是没有名字的,可以随意的指来指去的,另外指针可以有加减计算,加上减去一定的值。而数组相当于,定义以后就不可以修改数组地址了,这也是前面一条我都会说有一个p1或者p2指针,而不是数组array本身。尽管数组array有类似指针的行为,也是某种地址,但是它不可以进行加减操作,我们可以认为数组array本身是一个常量。

还有一点不同是,指针可以初始化为NULL,另外可以声明为void指针,还可以声明非常复杂的函数指针、指针的指针、字符串指针等等,但是数组没法定义为函数数组。

什么情况下指针与数组的概念可交换?《C专家编程》总结的以及相当全面,我在这里简单列两条,深入的内容请看书。1)使用a[i]这种形式对数组访问,编译器改写为*(a + i)形式,这也是为何i[a]这样写也编译运行通过的原因。2)作为函数参数时,数组会被修改为指向数组第一个元素的指针。

关于指针还有很多高阶内容,比如复杂的指针声明该如何解读?int const * p1与int * const p2的不同之处?sizeof *ptr 与 sizeof ptr结果?这里就不一一讲述了,毕竟这篇文字是给我自己做一个知识备份。如果大家有指针相关问题,欢迎留言,我会尽量解答。