这是个很意外的话题,我前两天发布了一篇博文,《C语言学习中的变参处理》(
http://tonyxiaohome.blog.51cto.com/925273/314371
),没想到引起了争论。
这里面,争论最大的就是里面的变参处理宏,为什么没有用do{}while(0)封装,而是直接用大括号{}封装。
应该说,大量这种讨论,有点出乎我的意料。我写这篇博文,本意是讲如何处理变参,用函数型宏来处理,算是一个处理方法,是为处理变参这个中心思想服务的,我心里想的,更多的是大家从这篇博文中,能学到处理变参传参的技巧,以后直接应用到工作和学习中,但是,我还真没想到,很多高手、准高手,没有关注变参处理这个中心思想,而把目光放在了宏的书写本身上。
这让我有点买椟还珠的感觉。
我说句实在话,大家以后看博文,最好还是看看博主真正想说什么,别老是带着自己的思路去看别人的文章,不然,挑了一大堆错误,结果,博主的想说的主要意思没理解到,除了可以可以显得自己比较牛之外,确实不容易学到东西。
我的书《 0bug-C/C++商用工程之道》其实就遇到过这种情况,大多数读者是很爱学习的,但是,确实有少部分人,看书的目的就是找错误,很多时候,甚至根本不管书中原文到底在讲什么,只管按照自己熟悉的方向,找错误。
这里面什么心态都有哈,不排除某些个枪手带着目的刻意而为,但我想至少有相当一部分人,可能是这么想的:“
啊哈,这个书这里有个bug,这个作者在这点不如我,我得写点什么或者说点,显得我比作者高明,作者的水平不过如此!”。
于是乎,拍砖的,喷人的,PK的,就都来了。很多时候话还很难听,让我很不好回答,最后只有删帖了事。
这里呢,我提个建议,听不听在大家。我建议大家以后遇到书籍、博文、演讲什么的,先不忙骂,也不要预设立场,先听听,看人家讲的有没有自己不足之处,有的话,
把人家的东东学过来,而自己的东东,自己又没有讲,人家学不去,你不是赚了嘛。呵呵。
我们程序员,看文档时经常会碰到一个单词,Context,就是上下文,这话呢,翻译到中国话就是前因后果,很多时候,文章中一句话,必须结合着上下文来分析,是有前提条件的,不能单独就一句话来理解,这个道理,我想大家都知道。这里,我也建议啊,大家看博文,耐心点,从头到尾看完再说话,我发现很多人,没耐心,看一点,或者文章中间看到一句话,就开骂,这至少不客观对吧?
就好比年初我演讲《明日世界--云端计算下的程序员需求》,就有人,看了不到十分钟就开骂,说讲得很烂。我晕,我讲了俩小时呢,120分钟,我问他,他看书是不是也只看十分之一就开骂?他就不说话了。
我想我说这番话,倒不全是因为我是博主、作者、演讲者,很多时候,我们从客观的角度出发,要想了解别人的一段话,起码读完再说嘛,这个要求不过分吧?
好吧,言归正传,故事的起因是这样的,我在《C语言学习中的变参处理》中,有这么一段:
- #define TONY_FORMAT(nPrintLength,szBuf,nBufferSize,szFormat) \
- { \
- va_list pArgList; \
- va_start (pArgList,szFormat); \
- nPrintLength+=Linux_Win_vsnprintf(szBuf+nPrintLength, \
- nBufferSize-nPrintLength,szFormat,pArgList); \
- va_end(pArgList); \
- if(nPrintLength>(nBufferSize-1)) nPrintLength=nBufferSize-1; \
- *(szBuf+nPrintLength)='\0'; \
- }
由于我这里没有使用do{}while(0)来封装,因此,后文中我在if{}else{}配对中就必须加大括号:
- #define TONY_LINE_MAX 1024 //最大一行输出的字符数
- //输出到控制台
- inline int TonyPrintf(bool bWithTimestamp, //是否带时间戳标志
- char* szFormat, ...) //格式化字符串
- {
- if(!szFormat) return 0;
- char szBuf[TONY_LINE_MAX];
- int nLength=0;
- if(!bWithTimestamp)
- { //注意,由于内部是函数型宏,if...else这个大括号必不可少
- TONY_FORMAT(nLength,szBuf,TONY_LINE_MAX,szFormat);
- } //注意,由于内部是函数型宏,if...else这个大括号必不可少
- else
- { //注意,由于内部是函数型宏,if...else这个大括号必不可少
- TONY_FORMAT_WITH_TIMESTAMP(nLength,szBuf,TONY_LINE_MAX,szFormat);
- } //注意,由于内部是函数型宏,if...else这个大括号必不可少
- return printf(szBuf);
- }
其实也可以不必加了,把宏调用后面的分号“;”去掉,就可以不加大括号,写成下面这样:
- #define TONY_LINE_MAX 1024 //最大一行输出的字符数
- //输出到控制台
- inline int TonyPrintf(bool bWithTimestamp, //是否带时间戳标志
- char* szFormat, ...) //格式化字符串
- {
- if(!szFormat) return 0;
- char szBuf[TONY_LINE_MAX];
- int nLength=0;
- if(!bWithTimestamp)
- TONY_FORMAT(nLength,szBuf,TONY_LINE_MAX,szFormat) //注意,没有分号
- else
- TONY_FORMAT_WITH_TIMESTAMP(nLength,szBuf,TONY_LINE_MAX,szFormat) //注意,没有分号
- return printf(szBuf);
- }
这本来是个细节问题,不过啊,引发争议很多,因为大家从很多C语言库中,可以看到这里使用do{}while(0)来封装,就没有这个限制。
于是呼,就都来了,呵呵。
后来我没办法,只有给出详细的解释,我的程序中,是不用do{}while(0)的,原因如下:
唉,多说一点吧,我的0bug一书中有讲,严禁一语多义,do{}while这个语句,写循环的时候,完全可以用while()来代替,而且更精准。它在我看来,唯一的作用就是这个写宏的时候用。这说明什么,do{}while唯一的作用,就是做宏用,而不是做循环语句用,这岂止是一语多义,根本就是乱义,我认为对程序员的误导极大。
我们都是通过学习循环语句学会的do{}while,但是,现在看起来,实际使用的时候,它更多被当成宏命令用,而不是循环语句,大家觉得乱不乱?
我很讨厌这种挂羊头卖狗肉的东东,所以,我的程序杜绝使用do{}while(),也严禁我的团队程序员这么使用,我不希望我们的代码,字面上看起来是一个意思,实际上又是另外一个意思。我唯一付出的代价,就是如本文所述,在使用宏时,正好又碰到if{}else{},我需要显式书写大括号而已。
这是以前我和几个做C的朋友讨论了半天,确认的不容易写错和读错的办法。这么多年,差不多15年了,一直坚持这么用,没有出过问题。
C语言很灵活的,没有什么规定的标准,事实上,把do{}while()不当循环语句用,而是作为宏包容符,这本身就是典型的非标准写法,大家想想是不是?
我写这么多,主要是想解释一下,不用do{}while(),更多地是从规范化编程,减少bug,减少团队bug这个出发点来的,并不是我不会用,更不是写不好。是经过仔细评估过后,对C/C++语言做了很多规范性裁剪使用的结果,一个问题,有很多种解法,但是,我趋向于取一种最简单的,最不容易出错的,形成标准,这样团队开发才有效率。
事实上,《0bug-C/C++商用工程之道》这本书,通篇除了讲并行,更多的就是讲这种规范化开发,这虽然看起来,限制了程序员的随意性和灵活度,但是,这保证了团队开发的质量,能帮大家赚到钱,我就认为是好办法。
08年我带团队,做的商用服务器集群,十几万行代码,只有51个bug,属于C/C++部分只有7个,这就是规范化开发的结果。这个故事在书上讲了的。我知道大家都理解do{}while()可以用来做宏,我也知道,但是,我希望大家更多想想,怎么写不容易写错,也不容易读错的代码,这样的代码,bug少,能赚钱,大家想不想写呢?
我想,至少我写程序不是为了炫耀,不是为了体现某些东东自己懂,别人不懂,显得我好像高人一等是的。我想的更多的,是如何简单、直接地输出产品,没有bug,大家赚到钱。我的这种思想,大家能同意不?
do{}while()看似简单,但背后体现程序设计规范化的思想,讲起来很麻烦,大家看我回复这么多,能理解吧?
喏,我想说的就是上面这段,如果还觉得不够详细呢,建议看看《0bug-C/C++商用工程之道》这本书,这也有个上下文问题,我定义的C/C++无错化设计方法,背后是有一个思想和原则的,即在语言裁剪使用的时候,有个取舍的原则,这个书里讲得更详细。看了,我想就能理解为什么我在这里舍掉do{}while()了。
也不一定要买书才能看啊,我的书,各大书店都有,有空的话,逛书店顺便翻翻,看看第三章,就都明白了,我想这么占便宜的事情,大家不会不去做吧,呵呵。
do{}while(0)很误导人的,大家有兴趣,看看这个例子:
- int i=0;
- do
- {
- i++;
- }while(0);
- //i=?
这么说吧,while(0),顾名思义,循环0次,但是,i++被执行过没有?
稍微对C语言不是特别熟悉的人,看到这段代码,怎么猜测i=?
do{}while(0),语义含混,字面意思和实际意思截然相反,所以我不用它。
再看看这个例子:
- void TestDoWhile(void)
- {
- int i=0;
- i=0;
- do
- {
- i++;
- printf("i=%d\n",i);
- } while (0);
- i=0;
- do
- {
- i++; //程序陷入死循环
- printf("i=%d\n",i);
- } while (1);
- }
好家伙,do{}while(0)表示执行了1次,do{}while(1),表示执行无数次,大家觉得这个语义理解有没有歧义,误导人不?
好吧,先到这里,我这里呢,也算说点心里话,很多时候,辛辛苦苦写点东东出来,本来是出于一片好意,把自己一些研究心得share给大家,看能不能帮到人,就有人上来就开骂,看着确实很不舒服。大家说呢?
=======================================================
在线底价购买我的书《0bug-C/C++商用工程之道》
(直接点击下面链接或拷贝到浏览器地址栏)
http://s.click.taobao.com/t_3?&p=mm_13866629_0_0&n=23&l=http%3A%2F%2Fsearch8.taobao.com%2Fbrowse%2F0%2Fn-g%2Corvv64tborsvwmjvgawdkmbqgboq---g%2Cgaqge5lhebbs6qzlfmqmttgtyo42jm6m22xllqa-------------1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2C14%2C15%2C16%2C17%2C18%2C19%2C20---40--coefp-0-all-0.htm%3Fpid%3Dmm_13866629_0_0
在线底价购买我的书《0bug-C/C++商用工程之道》
(直接点击下面链接或拷贝到浏览器地址栏)
http://s.click.taobao.com/t_3?&p=mm_13866629_0_0&n=23&l=http%3A%2F%2Fsearch8.taobao.com%2Fbrowse%2F0%2Fn-g%2Corvv64tborsvwmjvgawdkmbqgboq---g%2Cgaqge5lhebbs6qzlfmqmttgtyo42jm6m22xllqa-------------1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2C14%2C15%2C16%2C17%2C18%2C19%2C20---40--coefp-0-all-0.htm%3Fpid%3Dmm_13866629_0_0
本文转自 tonyxiaohome 51CTO博客,原文链接:http://blog.51cto.com/tonyxiaohome/315416,如需转载请自行联系原作者