官术网_书友最值得收藏!

第0章 導(dǎo)讀

我的第一個(gè)計(jì)算機(jī)程序?qū)懹?966年,是用Fortran語言開發(fā)的。該程序需要完成的任務(wù)是計(jì)算并打印輸出10000以內(nèi)的所有Fibonacci數(shù),也就是一個(gè)包括1,1,2,3,5,8,13,21,…等元素的數(shù)列,其中第2個(gè)數(shù)字之后的每個(gè)數(shù)字都是前兩個(gè)數(shù)字之和。當(dāng)然,寫程序代碼很難第一次就順利通過編譯:

   I = 0
   J = 0
   K = 1
1 PRINT 10,K
   I = J
   J = K
   K = I + J
   IF (K - 10000) 1, 1, 2
2 CALL EXIT
10 FORMAT(I10)

Fortran程序員很容易發(fā)現(xiàn)上面這段代碼遺漏了一個(gè)END語句。當(dāng)我添上END語句之后,程序還是不能通過編譯,編譯器的錯(cuò)誤消息也讓人迷惑不解:ERROR 6。

通過仔細(xì)查閱編譯器參考手冊中對錯(cuò)誤消息的說明,我最后終于明白了問題所在:我使用的Fortran編譯器不能處理4位數(shù)以上的整型常量。將上面這段代碼中的10000改為9999,程序就順利通過了編譯。

我的第一個(gè)C程序?qū)懹?977年。當(dāng)然,第一次還是沒有得到正確結(jié)果:

#include <stdio.h>

main()
{
            printf("Hello world");
}

這段代碼雖然在編譯時(shí)一次通過,但是程序執(zhí)行的結(jié)果看上去有點(diǎn)奇怪。終端輸出差不多就是下面這樣:

% cc prog.c
% a.out
Hello world%

這里的%字符是系統(tǒng)提示符,操作系統(tǒng)用它來提示用戶輸入。因?yàn)樵诔绦蛑袥]有寫明“Hello world”消息之后應(yīng)該換行,所以系統(tǒng)提示符%直接出現(xiàn)在輸出的“Hello world”消息之后。這個(gè)程序中還有一個(gè)更加難以察覺的錯(cuò)誤,將在3.10節(jié)加以討論。

上面提到的兩個(gè)程序中所出現(xiàn)的錯(cuò)誤,是有著實(shí)質(zhì)區(qū)別的兩種不同類型的錯(cuò)誤。在Fortran程序的例子中出現(xiàn)了兩個(gè)錯(cuò)誤,但是這兩個(gè)錯(cuò)誤都能夠被編譯器檢測出來。而C程序的例子從技術(shù)上說是正確的,至少從計(jì)算機(jī)的角度來看它沒有錯(cuò)誤。因此,C程序順利通過了編譯,沒有報(bào)告任何警告或錯(cuò)誤消息。計(jì)算機(jī)嚴(yán)格地按照我寫明的程序代碼來執(zhí)行,但結(jié)果并不是我真正希望得到的。

本書所要集中討論的是第二類問題,也就是程序并沒有按照程序員所期待的方式執(zhí)行。更進(jìn)一步,本書的討論限定在C語言程序中可能產(chǎn)生這類錯(cuò)誤的方式。例如,考慮下面這段代碼:

int i;
int a[N];
for (i = 0; i <= N; i++)
           a[i] = 0;

這段代碼的作用是初始化一個(gè)N元數(shù)組,但是在很多C編譯器中,它將會陷入一個(gè)死循環(huán)!3.6節(jié)討論了導(dǎo)致這種情況的原因。

程序設(shè)計(jì)錯(cuò)誤實(shí)際上反映的是程序與程序員這兩者對該程序的“心智模式”的相異之處。就程序錯(cuò)誤的本性而言,我們很難給它們進(jìn)行恰當(dāng)?shù)姆诸悺τ谝粋€(gè)程序錯(cuò)誤,可以從不同層面采用不同方式進(jìn)行考察。根據(jù)程序錯(cuò)誤與考察程序的方式之間的相關(guān)性,我嘗試著對程序錯(cuò)誤進(jìn)行了劃分。


 

譯注①:

心智模式(mental model)在彼得·圣吉的《第五項(xiàng)修煉——學(xué)習(xí)型組織的藝術(shù)與實(shí)務(wù)》(上海三聯(lián)書店,1998年第2版)中也有提到,被解釋為“人們深植心中,對于周遭世界如何運(yùn)作的看法和行為”。Howard Gardner在研究認(rèn)知科學(xué)的一本著作《心靈的新科學(xué)》(The Mind’s New Science)中認(rèn)為,人們的心智模式?jīng)Q定了人們?nèi)绾握J(rèn)識周遭世界。《列子》一書中有個(gè)典型的故事,說有個(gè)人遺失了一把斧頭,他懷疑是鄰居孩子偷的,暗中觀察他的行為,怎么看怎么像偷斧頭的人;后來他在自己家中找到了遺失的斧頭,再碰到鄰居的孩子時(shí),怎么看也不像會是偷他斧頭的人了。


 

從較低的層面考察,程序是由符號(token)序列所組成的,正如一本書是由一個(gè)一個(gè)字詞所組成的一樣。將程序分解成符號的過程,稱為“詞法分析”。第1章考察在程序被詞法分析器分解成各個(gè)符號的過程中可能出現(xiàn)的問題。

組成程序的這些符號,又可以看成是語句和聲明的序列,就好像一本書可以看成是由單詞進(jìn)一步結(jié)合而成的句子所組成的集合。無論是對于書而言,還是對于程序而言,符號或者單詞如何組成更大的單元(對于前者是語句和聲明,對于后者是句子)的語法細(xì)節(jié)最終決定了語義。如果沒有正確理解這些語法細(xì)節(jié),將會出現(xiàn)怎樣的錯(cuò)誤呢?第2章就此進(jìn)行了討論。

第3章處理有關(guān)語義誤解的問題:程序員的本意是希望表示某種事物,而實(shí)際表示的卻是另外一種事物。在這一章中我們假定程序員對詞法細(xì)節(jié)和語法細(xì)節(jié)的理解沒有問題,因此著重討論語義細(xì)節(jié)。

第4章注意到這樣一個(gè)事實(shí):C程序經(jīng)常是由若干個(gè)部分組成,它們分別進(jìn)行編譯,最后再整合起來。這個(gè)過程稱為“鏈接”,是程序和其支持環(huán)境之間關(guān)系的一部分。

程序的支持環(huán)境包括某組庫函數(shù)(library routine)。雖然嚴(yán)格說來庫函數(shù)并不是語言的一部分,但是它對任何一個(gè)有用的程序都非常重要。尤其是,有些庫函數(shù)幾乎會在每個(gè)C程序中都要用到。對這些庫函數(shù)的誤用可以說是五花八門,因此值得在第5章中專門討論。

在第6章,我們還注意到,由于C預(yù)處理器的介入,實(shí)際運(yùn)行的程序并不是最初編寫的程序。雖然不同預(yù)處理器的實(shí)現(xiàn)存在或多或少的差異,但是大部分特性是各種預(yù)處理器都支持的。第6章討論了與這些特性有關(guān)的有用內(nèi)容。

第7章討論了可移植性問題,也就是為什么在一個(gè)實(shí)現(xiàn)平臺上能夠運(yùn)行的程序卻無法在另一個(gè)平臺上運(yùn)行。當(dāng)牽涉到可移植性時(shí),哪怕是非常簡單的類似整數(shù)的算術(shù)運(yùn)算這樣的事情,其困難程度也常常會出人意料。

第8章提供了有關(guān)預(yù)防性程序設(shè)計(jì)的一些建議,還給出了其他章節(jié)的練習(xí)解答。

最后,附錄討論了3個(gè)常用的卻普遍被誤解的庫函數(shù)。

練習(xí)0-1 你是否愿意購買廠家所生產(chǎn)的一輛返修率很高的汽車?如果廠家聲明對它已經(jīng)做出了改進(jìn),你的態(tài)度是否會改變?用戶為你找出程序中的bug,你真正損失的是什么?

練習(xí)0-2 修建一個(gè)100英尺(約30.5米)長的護(hù)欄,護(hù)欄的欄桿之間相距10英尺(約3.05米),需要用到多少根欄桿?

練習(xí)0-3 在烹飪時(shí)你是否失手用菜刀切傷過自己的手?怎樣改進(jìn)菜刀會讓使用更安全?你是否愿意使用這樣一把經(jīng)過改良的菜刀?

主站蜘蛛池模板: 烟台市| 保康县| 股票| 读书| 泌阳县| 延庆县| 沂水县| 旬邑县| 巫溪县| 饶阳县| 平顺县| 南陵县| 双牌县| 自治县| 施甸县| 锦屏县| 吉木乃县| 鹿邑县| 蕉岭县| 宁阳县| 广德县| 同江市| 北宁市| 门头沟区| 永川市| 通许县| 建平县| 道真| 来宾市| 东明县| 依安县| 仙游县| 海原县| 揭阳市| 阜阳市| 伽师县| 奉新县| 宁武县| 保靖县| 大埔区| 宁陵县|