最简单的挑战(二):你真的清楚了要干什么吗?(训练营热身课)

来源:互联网 发布:php static创建对象 编辑:程序博客网 时间:2024/05/01 00:52

最简单的挑战(二):你真的清楚了要干什么吗?

<链接到上一讲>

为什么在上一讲里我们给那两段代码都给出零分及零分以下的结论?因为那些程序员犯了一个严重的错误:没搞清楚问题就下手了。这种错误对实际开发来说是不可接受的,因为这意味着你很可能会花费企业的资源做了几个月,最后出来的根本不是需要的东西,不仅仅是浪费点薪水,而是整个项目可能因你而拖延,或者交到用户那里需要不停地维护修改!至少,这也说明你不是一个严密和热爱思考的程序员,所以企业有理由怀疑你的潜力有多大。

拿到任务,先别着急动手,不妨问问自己"我真的知道要干什么吗?"。初学者往往犯的第一个错误就是还没搞清楚问题是什么就动手写代码了,其实也常见到有丰富工作经验的人也同样如此。

回到我们的问题:"写一个函数,实现类似strcat的功能,即把一个字符串拷贝到另一个字符串的后面"。

表面上看上去问题很简单,描述的也很清楚。但是,对于一个训练有素的程序员而言,这样的描述太模糊了,必须先把问题的定义搞清楚才能动手,否则可能写了半天也是无用功,因为你以为你知道了任务是什么,但这一切只是你的"以为",很可能和提问者的"以为"大相径庭。

1. 听到"字符串"这个词的时候,我们应立即想到计算机里的字符编码至少有三种体系:单字节、多字节、UNICODE。所以,我们的第一个问题就是,这个函数支持哪种字符编码?抑或全支持?

2. 虽然C/C++语言里通常所说的字符串都包括一个NULL结束符,但是在这个问题里我们需要确认是否输入输出的字符串都要包括NULL结束符。因为有经验的程序员知道,并不是所有的字符串处理函数输出结果时都给你在末尾自动加一个NULL。

3.这个函数要求的返回值是什么?至少你要告诉调用者是否执行成功吧,或者调用者还希望知道更多,比如生成的新字符串的长度?

4.这个函数的使用范围是什么?是在一个要正式发布的产品中使用还是只在试验性代码中使用?是只在本企业使用还是作为公共库函数提供给第三方使用?只支持某种编译器还是通用的?

5.这个函数遵从什么调用标准?因为函数调用的参数是通过栈来实现的,所以至少我们应该问问谁负责清理栈?是调用者还是被调用者?参数压栈的次序是从左往右还是从右往左?如果是调用者负责清栈,好处是参数个数可变,坏处是编译器要为每个调用者自动加上一段清理栈的代码,所以目标代码会比较大。反之,如果被调用者负责清栈,则正好相反。

6.错误处理方式是什么?如果因为调用者的错误或者其它原因函数检测到错误,应如何处理?是通过返回值告诉调用者,还是抛出异常?如果是在Windows平台上,抛出异常前是否需要设置LastError?

以上这些问题的答案是什么不是关键,而且很多问题需要程序员自己去调查去思考,自己做决定。关键的是对于专业程序员来说,一定要养成把问题搞清楚再动手的习惯,这就是我们第一讲的目的。

好,为了我们后续课程的方便,这里我们先不妨给出以上问题的一下假设需求:

1.我们的函数只支持UNICODE,而且是UTF-16。Why?因为很多老程序员都曾经被多字节、双字节引起的错误痛苦折磨过,往事不堪回首,我们再也不想支持什么单字节、多字节编码了,只支持UNICODE就强迫了调用者也只能同样适用UNICODE,这样不仅减少我们自己出错的概率,也减少了调用者出错的概率。虽然UNICODE又分为UTF-8、UTF-16、UTF-16 Big、Surrogate等,但考虑到最常用的还是UTF-16,我们就只支持这个。(支持不全没关系,以后可以扩展,只要我们知道自己现在在干什么就可以)

2.输入、输出字符串都要有NULL结束符。

3.返回新串的长度,如果目标字符串长度不够,能拷贝多少拷贝多少。

4.为了揭示产品开发对专业程序员的期望,我们把该函数的使用范围定义为将在本企业正式发布的产品中使用。只支持VC8.0及以上编译器。

5.因为此类基本字符串操作函数可能被广泛使用,所以我们希望效率较高,所以要求使用函数自身负责清理调用栈,参数压栈顺序遵守C++默认的从右往左。

6.作为基本函数,效率优先,而且考虑到不能预测调用者错误处理的方式。所以决定通过返回值告诉调用者出错的方式,不采用抛出"异常"的方式处理常规错误。

到这里,作为第一课的成果,我们可以重新写出下面的函数定义:

int __stdcall fool(wchar_t* s1,wchar_t* s2,int maxlen);

我想稍微有点基础的人都可以根据这个定义写出一个运行基本正确的实现了。但是,我们虽然比"零分"的版本前进了一步,但也还在"10分"的水平。

接下来,我准备先讲"这样的代码是人写的吗?",省得我们一直跟着一个看着难受的版本讨论下去。


给参加训练营的朋友留点课外作业,呵呵。

延伸阅读:


Single-byte and Multibyte Character Sets

UNICODE标准简介

Unicode: The Wide-Character Set

Using Generic-Text Mappings

Using TCHAR.H Data Types with _MBCS
A Sample Generic-Text Program
Argument Passing and Naming Conventions


关于字符编码标准,还搜索到了俺在几年前的一篇旧帖"从GB2313-标准字符集、GBK-大字符集到GB18030-超大字符集 "。


探索:
1. 在编程实践中,UNICODE字符类型除了wchar_t表示还有用WCHAR来表示。那么WCHAR到底是什么?跟wchar_t是什么关系?而且wchar_t的最后有个"_t"有什么含义吗?既然两个都行,为什么作者定义函数的时候选择了wchar_t而没选WCHAR?为什么有时大写有时小写(例如char和CHAR都可以表示单字节字符),它们的来源是哪里?什么时候用大写什么时候用小写?

2. 为什么作者倾向使用wchar_t,而没有使用char,也没有用TCHAR这样的routing type?你猜测我的理由除了方便和不容易写错以外还有什么更重要原因?那么你的选择又是什么?为什么?如果不使用UNICDE,你的程序在不同国家/地区的平台上会出现什么情况?

3. 我国的汉字编码标准有什么?GB2312、GBK、GB18030是什么东东?它们属于哪类编码?它们在什么背景下提出的?我们程序员如何处理它们?CJK缩写是什么意思?另外请深入思考GB18030编码标准是否科学合理。

4.看到 这里有一个讨论"设计函数时,如何决定什么时候该抛出异常,什么时候该返回值?",看了他们的讨论,你的意见是什么?

5. 怎么实现其他编码方式和UNICODE的相互转换?这种转换什么情况下会出现错误?在C标准函数里,哪些函数与此相关?在Windows函数里呢?

(本训练营专用讨论组:http://groups.csdn.net/devs)



原创粉丝点击