久久99精品久久久久久琪琪,久久人人爽人人爽人人片亞洲,熟妇人妻无码中文字幕,亚洲精品无码久久久久久久

C語言陷阱和缺陷(精選五篇)

時間:2019-05-12 21:03:45下載本文作者:會員上傳
簡介:寫寫幫文庫小編為你整理了多篇相關(guān)的《C語言陷阱和缺陷》,但愿對你工作學(xué)習(xí)有幫助,當(dāng)然你在寫寫幫文庫還可以找到更多《C語言陷阱和缺陷》。

第一篇:C語言陷阱和缺陷

0 簡介

C語言及其典型實現(xiàn)被設(shè)計為能被專家們?nèi)菀椎厥褂谩_@門語言簡潔并附有表達力。但有一些限制可以保護那些浮躁的人。一個浮躁的人可以從這些條款中獲得一些幫助。

在本文中,我們將會看到這些未可知的益處。正是由于它的未可知,我們無法為其進行完全的分類。不過,我們?nèi)匀煌ㄟ^研究為了一個C程序的運行所需要做的事來做到這些。我們假設(shè)讀者對C語言至少有個粗淺的了解。

第一部分研究了當(dāng)程序被劃分為記號時會發(fā)生的問題。第二部分繼續(xù)研究了當(dāng)程序的記號被編譯器組合為聲明、表達式和語句時會出現(xiàn)的問題。第三部分研究了由多個部分組成、分別編譯并綁定到一起的C程序。第四部分處理了概念上的誤解:當(dāng)一個程序具體執(zhí)行時會發(fā)生的事情。第五部分研究了我們的程序和它們所使用的常用庫之間的關(guān)系。在第六部分中,我們注意到了我們所寫的程序也許并不是我們所運行的程序;預(yù)處理器將首先運行。最后,第七部分討論了可移植性問題:一個能在一個實現(xiàn)中運行的程序無法在另一個實現(xiàn)中運行的原因。詞法缺陷

編譯器的第一個部分常被稱為詞法分析器(lexical analyzer)。詞法分析器檢查組成程序的字符序列,并將它們劃分為記號(token)一個記號是一個由一個或多個字符構(gòu)成的序列,它在語言被編譯時具有一個(相關(guān)地)統(tǒng)一的意義。在C中,例如,記號->的意義和組成它的每個獨立的字符具有明顯的區(qū)別,而且其意義獨立于->出現(xiàn)的上下文環(huán)境。

另外一個例子,考慮下面的語句:

if(x > big)big = x;

該語句中的每一個分離的字符都被劃分為一個記號,除了關(guān)鍵字if和標(biāo)識符big的兩個實例。

事實上,C程序被兩次劃分為記號。首先是預(yù)處理器讀取程序。它必須對程序進行記號劃分以發(fā)現(xiàn)標(biāo)識宏的標(biāo)識符。它必須通過對每個宏進行求值來替換宏調(diào)用。最后,經(jīng)過宏替換的程序又被匯集成字符流送給編譯器。編譯器再第二次將這個流劃分為記號。

在這一節(jié)中,我們將探索對記號的意義的普遍的誤解以及記號和組成它們的字符之間的關(guān)系。稍后我們將談到預(yù)處理器。

1.1 = 不是==

從Algol派生出來的語言,如Pascal和Ada,用:=表示賦值而用=表示比較。而C語言則是用=表示賦值而用==表示比較。這是因為賦值的頻率要高于比較,因此為其分配更短的符號。

此外,C還將賦值視為一個運算符,因此可以很容易地寫出多重賦值(如a = b = c),并且可以將賦值嵌入到一個大的表達式中。

這種便捷導(dǎo)致了一個潛在的問題:可能將需要比較的地方寫成賦值。因此,下面的語句好像看起來是要檢查x是否等于y:

if(x = y)

foo();

而實際上是將x設(shè)置為y的值并檢查結(jié)果是否非零。再考慮下面的一個希望跳過空格、制表符和換行符的循環(huán):

while(c == ' ' || c = '/t' || c == '/n')

c = getc(f);

在與'/t'進行比較的地方程序員錯誤地使用=代替了==。這個“比較”實際上是將'/t'賦給c,然后判斷c的(新的)值是否為零。因為'/t'不為零,這個“比較”將一直為真,因此這個循環(huán)會吃盡整個文件。這之后會發(fā)生什么取決于特定的實現(xiàn)是否允許一個程序讀取超過文件尾部的部分。如果允許,這個循環(huán)會一直運行。

一些C編譯器會對形如e1 = e2的條件給出一個警告以提醒用戶。當(dāng)你確實需要先對一個變量進行賦值之后再檢查變量是否非零時,為了在這種編譯器中避免警告信息,應(yīng)考慮顯式給出比較符。換句話說,將: if(x = y)

foo();改寫為:

if((x = y)!= 0)

foo();

這樣可以清晰地表示你的意圖。

1.2 & 和 | 不是 && 和||

容易將==錯寫為=是因為很多其他語言使用=表示比較運算。其他容易寫錯的運算符還有&和&&,以及|和||,這主要是因為C語言中的&和|運算符于其他語言中具有類似功能的運算符大為不同。我們將在第4節(jié)中貼近地觀察這些運算符。

1.3 多字符記號

一些C記號,如/、*和=只有一個字符。而其他一些C記號,如/*和==,以及標(biāo)識符,具有多個字符。當(dāng)C編譯器遇到緊連在一起的/和*時,它必須能夠決定是將這兩個字符識別為兩個分離的記號還是一個單獨的記號。C語言參考手冊說明了如何決定:“如果輸入流到一個給定的字符串為止已經(jīng)被識別為記號,則應(yīng)該包含下一個字符以組成能夠構(gòu)成記號的最長的字符串”([譯注]即通常所說的“最長子串原則”)。因此,如果/是一個記號的第一個字符,并且/后面緊隨了一個*,則這兩個字符構(gòu)成了注釋的開始,不管其他上下文環(huán)境。

下面的語句看起來像是將y的值設(shè)置為x的值除以p所指向的值: y = x/*p

/* p 指向除數(shù) */;

實際上,/*開始了一個注釋,因此編譯器簡單地吞噬程序文本,直到*/的出現(xiàn)。換句話說,這條語句僅僅把y的值設(shè)置為x的值,而根本沒有看到p。將這條語句重寫為: y = x / *p

/* p 指向除數(shù) */;或者干脆是

y = x /(*p)

/* p指向除數(shù) */;它就可以做注釋所暗示的除法了。

這種模棱兩可的寫法在其他環(huán)境中就會引起麻煩。例如,老版本的C使用=+表示現(xiàn)在版本中的+=。這樣的編譯器會將 a=-1;視為 a =-1;或

a = a> a

是不合法的。它和

p-> a

不是同義詞。

另一方面,有些老式編譯器還是將=+視為一個單獨的記號并且和+=是同義詞。

1.5 字符串和字符

單引號和雙引號在C中的意義完全不同,在一些混亂的上下文中它們會導(dǎo)致奇怪的結(jié)果而不是錯誤消息。

包圍在單引號中的一個字符只是編寫整數(shù)的另一種方法。這個整數(shù)是給定的字符在實現(xiàn)的對照序列中的一個對應(yīng)的值。因此,在一個ASCII實現(xiàn)中,'a'和0141或97表示完全相同的東西。而一個包圍在雙引號中的字符串,只是編寫一個有雙引號之間的字符和一個附加的二進制值為零的字符所初始化的一個無名數(shù)組的指針的一種簡短方法。

下面的兩個程序片斷是等價的:

printf(“Hello world/n”);

char hello[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '/n', 0 };printf(hello);

使用一個指針來代替一個整數(shù)通常會得到一個警告消息(反之亦然),使用雙引號來代替單引號也會得到一個警告消息(反之亦然)。但對于不檢查參數(shù)類型的編譯器卻除外。因此,用 printf('/n');來代替 printf(“/n”);通常會在運行時得到奇怪的結(jié)果。([譯注]提示:正如上面所說,'/n'表示一個整數(shù),它被轉(zhuǎn)換為了一個指針,這個指針?biāo)赶虻膬?nèi)容是沒有意義的。)

由于一個整數(shù)通常足夠大,以至于能夠放下多個字符,一些C編譯器允許在一個字符常量中存放多個字符。這意味著用'yes'代替“yes”將不會被發(fā)現(xiàn)。后者意味著“分別包含y、e、s和一個空字符的四個連續(xù)存儲器區(qū)域中的第一個的地址”,而前者意味著“在一些實現(xiàn)定義的樣式中表示由字符y、e、s聯(lián)合構(gòu)成的一個整數(shù)”。這兩者之間的任何一致性都純屬巧合。句法缺陷

要理解C語言程序,僅了解構(gòu)成它的記號是不夠的。還要理解這些記號是如何構(gòu)成聲明、表達式、語句和程序的。盡管這些構(gòu)成通常都是定義良好的,但這些定義有時候是有悖于直覺的或混亂的。

在這一節(jié)中,我們將著眼于一些不明顯句法構(gòu)造。

2.1 理解聲明

我曾經(jīng)和一些人聊過天,他們那時正在在編寫在一個小型的微處理器上單機運行的C程序。當(dāng)這臺機器的開關(guān)打開的時候,硬件會調(diào)用地址為0處的子程序。

為了模仿電源打開的情形,我們要設(shè)計一條C語句來顯式地調(diào)用這個子程序。經(jīng)過一些思考,我們寫出了下面的語句:

(*(void(*)())0)();

這樣的表達式會令C程序員心驚膽戰(zhàn)。但是,并不需要這樣,因為他們可以在一個簡單的規(guī)則的幫助下很容易地構(gòu)造它:以你使用的方式聲明它。

每個C變量聲明都具有兩個部分:一個類型和一組具有特定格式的、期望用來對該類型求值的表達式。最簡單的表達式就是一個變量:

float f, g;

說明表達式f和g——在求值的時候——具有類型float。由于待求值的是表達式,因此可以自由地使用圓括號:

float((f));

這表示((f))求值為float并且因此,通過推斷,f也是一個float。

同樣的邏輯用在函數(shù)和指針類型。例如:

float ff();

表示表達式ff()是一個float,因此ff是一個返回一個float的函數(shù)。類似地,float *pf;

表示*pf是一個float并且因此pf是一個指向一個float的指針。

這些形式的組合聲明對表達式是一樣的。因此,float *g(),(*h)();

表示*g()和(*h)()都是float表達式。由于()比*綁定得更緊密,*g()和*(g())表示同樣的東西:g是一個返回指float指針的函數(shù),而h是一個指向返回float的函數(shù)的指針。

當(dāng)我們知道如何聲明一個給定類型的變量以后,就能夠很容易地寫出一個類型的模型(cast):只要刪除變量名和分號并將所有的東西包圍在一對圓括號中即可。因此,由于 float *g();

聲明g是一個返回float指針的函數(shù),所以(float *())就是它的模型。

有了這些知識的武裝,我們現(xiàn)在可以準(zhǔn)備解決(*(void(*)())0)()了。我們可以將它分為兩個部分進行分析。首先,假設(shè)我們有一個變量fp,它包含了一個函數(shù)指針,并且我們希望調(diào)用fp所指向的函數(shù)。可以這樣寫:

(*fp)();

如果fp是一個指向函數(shù)的指針,則*fp就是函數(shù)本身,因此(*fp)()是調(diào)用它的一種方法。(*fp)中的括號是必須的,否則這個表達式將會被分析為*(fp())。我們現(xiàn)在要找一個適當(dāng)?shù)谋磉_式來替換fp。

這個問題就是我們的第二步分析。如果C可以讀入并理解類型,我們可以寫:(*0)();

但這樣并不行,因為*運算符要求必須有一個指針作為它的操作數(shù)。另外,這個操作數(shù)必須是一個指向函數(shù)的指針,以保證*的結(jié)果可以被調(diào)用。因此,我們需要將0轉(zhuǎn)換為一個可以描述“指向一個返回void的函數(shù)的指針”的類型。

如果fp是一個指向返回void的函數(shù)的指針,則(*fp)()是一個void值,并且它的聲明將會是這樣的: void(*fp)();

因此,我們需要寫:

void(*fp)();(*fp)();

來聲明一個啞變量。一旦我們知道了如何聲明該變量,我們也就知道了如何將一個常數(shù)轉(zhuǎn)換為該類型:只要從變量的聲明中去掉名字即可。因此,我們像下面這樣將0轉(zhuǎn)換為一個“指向返回void的函數(shù)的指針”:

(void(*)())0

接下來,我們用(void(*)())0來替換fp:

(*(void(*)())0)();

結(jié)尾處的分號用于將這個表達式轉(zhuǎn)換為一個語句。

在這里,我們解決這個問題時沒有使用typedef聲明。通過使用它,我們可以更清晰地解決這個問題:

typedef void(*funcptr)();(*(funcptr)0)();

2.2 運算符并不總是具有你所想象的優(yōu)先級

假設(shè)有一個聲明了的常量FLAG,它是一個整數(shù),其二進制表示中的某一位被置位(換句話說,它是2的某次冪),并且你希望測試一個整型變量flags該位是否被置位。通常的寫法是:

if(flags & FLAG)...其意義對于很多C程序員都是很明確的:if語句測試括號中的表達式求值的結(jié)果是否為0。出于清晰的目的我們可以將它寫得更明確:

if(flags & FLAG!= 0)...這個語句現(xiàn)在更容易理解了。但它仍然是錯的,因為!=比&綁定得更緊密,因此它被分析為: if(flags &(FLAG!= 0))...這(偶爾)是可以的,如FLAG是1或0(?。┑臅r候,但對于其他2的冪是不行的[2]。

假設(shè)你有兩個整型變量,h和l,它們的值在0和15(含0和15)之間,并且你希望將r設(shè)置為8位值,其低位為l,高位為h。一種自然的寫法是: r = h << 4 + 1;不幸的是,這是錯誤的。加法比移位綁定得更緊密,因此這個例子等價于: r = h <<(4 + l);正確的方法有兩種:

r =(h << 4)+ l;r = h << 4 | l;

避免這種問題的一個方法是將所有的東西都用括號括起來,但表達式中的括號過度就會難以理解,因此最好還是是記住C中的優(yōu)先級。

不幸的是,這有15個,太困難了。然而,通過將它們分組可以變得容易。

綁定得最緊密的運算符并不是真正的運算符:下標(biāo)、函數(shù)調(diào)用和結(jié)構(gòu)選擇。這些都與左邊相關(guān)聯(lián)。

接下來是一元運算符。它們具有真正的運算符中的最高優(yōu)先級。由于函數(shù)調(diào)用比一元運算符綁定得更緊密,你必須寫(*p)()來調(diào)用p指向的函數(shù);*p()表示p是一個返回一個指針的函數(shù)。轉(zhuǎn)換是一元運算符,并且和其他一元運算符具有相同的優(yōu)先級。一元運算符是右結(jié)合的,因此*p++表示*(p++),而不是(*p)++。

在接下來是真正的二元運算符。其中數(shù)學(xué)運算符具有最高的優(yōu)先級,然后是移位運算符、關(guān)系運算符、邏輯運算符、賦值運算符,最后是條件運算符。需要記住的兩個重要的東西是:

? 所有的邏輯運算符具有比所有關(guān)系運算符都低的優(yōu)先級。

? 移位運算符比關(guān)系運算符綁定得更緊密,但又不如數(shù)學(xué)運算符。

在這些運算符類別中,有一些奇怪的地方。乘法、除法和求余具有相同的優(yōu)先級,加法和減法具有相同的優(yōu)先級,以及移位運算符具有相同的優(yōu)先級。

還有就是六個關(guān)系運算符并不具有相同的優(yōu)先級:==和!=的優(yōu)先級比其他關(guān)系運算符要低。這就允許我們判斷a和b是否具有與c和d相同的順序,例如:

a < b == c < d

在邏輯運算符中,沒有任何兩個具有相同的優(yōu)先級。按位運算符比所有順序運算符綁定得都緊密,每種與運算符都比相應(yīng)的或運算符綁定得更緊密,并且按位異或(^)運算符介于按位與和按位或之間。

三元運算符的優(yōu)先級比我們提到過的所有運算符的優(yōu)先級都低。這可以保證選擇表達式中包含的關(guān)系運算符的邏輯組合特性,如:

z = a < b && b < c ? d : e

這個例子還說明了賦值運算符具有比條件運算符更低的優(yōu)先級是有意義的。另外,所有的復(fù)合賦值運算符具有相同的優(yōu)先級并且是自右至左結(jié)合的,因此 a = b = c 和

b = c;a = b;是等價的。

具有最低優(yōu)先級的是逗號運算符。這很容易理解,因為逗號通常在需要表達式而不是語句的時候用來替代分號。

賦值是另一種運算符,通常具有混合的優(yōu)先級。例如,考慮下面這個用于復(fù)制文件的循環(huán):

while(c = getc(in)!= EOF)

putc(c, out);

這個while循環(huán)中的表達式看起來像是c被賦以getc(in)的值,接下來判斷是否等于EOF以結(jié)束循環(huán)。不幸的是,賦值的優(yōu)先級比任何比較操作都低,因此c的值將會是getc(in)和EOF比較的結(jié)果,并且會被拋棄。因此,“復(fù)制”得到的文件將是一個由值為1的字節(jié)流組成的文件。

上面這個例子正確的寫法并不難:

while((c = getc(in))!= EOF)

putc(c, out);

然而,這種錯誤在很多復(fù)雜的表達式中卻很難被發(fā)現(xiàn)。例如,隨UNIX系統(tǒng)一同發(fā)布的lint程序通常帶有下面的錯誤行:

if(((t = BTYPE(pt1->aty)== STRTY)|| t == UNIONTY){

這條語句希望給t賦一個值,然后看t是否與STRTY或UNIONTY相等。而實際的效果卻大不相同[3]。

C中的邏輯運算符的優(yōu)先級具有歷史原因。B語言——C的前輩——具有和C中的&和|運算符對應(yīng)的邏輯運算符。盡管它們的定義是按位的,但編譯器在條件判斷上下文中將它們視為和&&和||一樣。當(dāng)在C中將它們分開后,優(yōu)先級的改變是很危險的[4]。

2.3 看看這些分號!

C中的一個多余的分號通常會帶來一點點不同:或者是一個空語句,無任何效果;或者編譯器可能提出一個診斷消息,可以方便除去掉它。一個重要的區(qū)別是在必須跟有一個語句的if和while語句中??紤]下面的例子: if(x[i] > big);

big = x[i];

這不會發(fā)生編譯錯誤,但這段程序的意義與: if(x[i] > big)

big = x[i];

就大不相同了。第一個程序段等價于: if(x[i] > big){ } big = x[i];也就是等價于:

big = x[i];

(除非x、i或big是帶有副作用的宏)。

另一個因分號引起巨大不同的地方是函數(shù)定義前面的結(jié)構(gòu)聲明的末尾([譯注]這句話不太好聽,看例子就明白了)??紤]下面的程序片段: struct foo {

int x;}

f(){

...}

在緊挨著f的第一個}后面丟失了一個分號。它的效果是聲明了一個函數(shù)f,返回值類型是struct foo,這個結(jié)構(gòu)成了函數(shù)聲明的一部分。如果這里出現(xiàn)了分號,則f將被定義為具有默認的整型返回值[5]。

2.4 switch語句

通常C中的switch語句中的case段可以進入下一個。例如,考慮下面的C和Pascal程序片斷:

switch(color){ case 1: printf(“red”);

break;case 2: printf(“yellow”);

break;case 3: printf(“blue”);

break;}

case color of 1: write('red');2: write('yellow');3: write('blue');end

這兩個程序片段都作相同的事情:根據(jù)變量color的值是1、2還是3打印red、yellow或blue(沒有新行符)。這兩個程序片段非常相似,只有一點不同:Pascal程序中沒有C中相應(yīng)的break語句。C中的case標(biāo)簽是真正的標(biāo)簽:控制流程可以無限制地進入到一個case標(biāo)簽中。

看看另一種形式,假設(shè)C程序段看起來更像Pascal:

switch(color){ case 1: printf(“red”);case 2: printf(“yellow”);case 3: printf(“blue”);}

并且假設(shè)color的值是2。則該程序?qū)⒋蛴ellowblue,因為控制自然地轉(zhuǎn)入到下一個printf()的調(diào)用。

這既是C語言switch語句的優(yōu)點又是它的弱點。說它是弱點,是因為很容易忘記一個break語句,從而導(dǎo)致程序出現(xiàn)隱晦的異常行為。說它是優(yōu)點,是因為通過故意去掉break語句,可以很容易實現(xiàn)其他方法難以實現(xiàn)的控制結(jié)構(gòu)。尤其是在一個大型的switch語句中,我們經(jīng)常發(fā)現(xiàn)對一個case的處理可以簡化其他一些特殊的處理。

例如,設(shè)想有一個程序是一臺假想的機器的翻譯器。這樣的一個程序可能包含一個switch語句來處理各種操作碼。在這樣一臺機器上,通常減法在對其第二個運算數(shù)進行變號后就變成和加法一樣了。因此,最好可以寫出這樣的語句:

case SUBTRACT:

opnd2 =-opnd2;

/* no break;*/ case ADD:

...另外一個例子,考慮編譯器通過跳過空白字符來查找一個記號。這里,我們將空格、制表符和新行符視為是相同的,除了新行符還要引起行計數(shù)器的增長外: case '/n':

linecount++;

/* no break */ case '/t': case ' ':

...2.5 函數(shù)調(diào)用

和其他程序設(shè)計語言不同,C要求一個函數(shù)調(diào)用必須有一個參數(shù)列表,但可以沒有參數(shù)。因此,如果f是一個函數(shù),f();

就是對該函數(shù)進行調(diào)用的語句,而

f;

什么也不做。它會作為函數(shù)地址被求值,但不會調(diào)用它[6]。

2.6 懸掛else問題

在討論任何語法缺陷時我們都不會忘記提到這個問題。盡管這一問題不是C語言所獨有的,但它仍然傷害著那些有著多年經(jīng)驗的C程序員。

考慮下面的程序片斷:

if(x == 0)

if(y == 0)error();else {

z = x + y;

f(&z);}

寫這段程序的程序員的目的明顯是將情況分為兩種:x = 0和x!= 0。在第一種情況中,程序段什么都不做,除非y = 0時調(diào)用error()。第二種情況中,程序設(shè)置z = x + y并以z的地址作為參數(shù)調(diào)用f()。

然而,這段程序的實際效果卻大為不同。其原因是一個else總是與其最近的if相關(guān)聯(lián)。如果我們希望這段程序能夠按照實際的情況運行,應(yīng)該這樣寫:

if(x == 0){

if(y == 0)

error();

else {

z = x + y;

f(&z);

} }

換句話說,當(dāng)x!= 0發(fā)生時什么也不做。如果要達到第一個例子的效果,應(yīng)該寫: if(x == 0){

if(y ==0)

error();} else {

z = z + y;

f(&z);} 3 連接

一個C程序可能有很多部分組成,它們被分別編譯,并由一個通常稱為連接器、連接編輯器或加載器的程序綁定到一起。由于編譯器一次通常只能看到一個文件,因此它無法檢測到需要程序的多個源文件的內(nèi)容才能發(fā)現(xiàn)的錯誤。

在這一節(jié)中,我們將看到一些這種類型的錯誤。有一些C實現(xiàn),但不是所有的,帶有一個稱為lint的程序來捕獲這些錯誤。如果具有一個這樣的程序,那么無論怎樣地強調(diào)它的重要性都不過分。

3.1 你必須自己檢查外部類型

假設(shè)你有一個C程序,被劃分為兩個文件。其中一個包含如下聲明: int n;

而令一個包含如下聲明:

long n;

這不是一個有效的C程序,因為一些外部名稱在兩個文件中被聲明為不同的類型。然而,很多實現(xiàn)檢測不到這個錯誤,因為編譯器在編譯其中一個文件時并不知道另一個文件的內(nèi)容。因此,檢查類型的工作只能由連接器(或一些工具程序如lint)來完成;如果操作系統(tǒng)的連接器不能識別數(shù)據(jù)類型,C編譯器也沒法過多地強制它。

那么,這個程序運行時實際會發(fā)生什么?這有很多可能性:

? 實現(xiàn)足夠聰明,能夠檢測到類型沖突。則我們會得到一個診斷消息,說明n在兩個文件中具有不同的類型。

? 你所使用的實現(xiàn)將int和long視為相同的類型。典型的情況是機器可以自然地進行32位運算。在這種情況下你的程序或許能夠工作,好象你兩次都將變量聲明為long(或int)。但這種程序的工作純屬偶然。

? n的兩個實例需要不同的存儲,它們以某種方式共享存儲區(qū),即對其中一個的賦值對另一個也有效。這可能發(fā)生,例如,編譯器可以將int安排在long的低位。不論這是基于系統(tǒng)的還是基于機器的,這種程序的運行同樣是偶然。

? n的兩個實例以另一種方式共享存儲區(qū),即對其中一個賦值的效果是對另一個賦以不同的值。在這種情況下,程序可能失敗。

這種情況發(fā)生的里一個例子出奇地頻繁。程序的某一個文件包含下面的聲明: char filename[] = “etc/passwd”;而另一個文件包含這樣的聲明:

char *filename;

盡管在某些環(huán)境中數(shù)組和指針的行為非常相似,但它們是不同的。在第一個聲明中,filename是一個字符數(shù)組的名字。盡管使用數(shù)組的名字可以產(chǎn)生數(shù)組第一個元素的指針,但這個指針只有在需要的時候才產(chǎn)生并且不會持續(xù)。在第二個聲明中,filename是一個指針的名字。這個指針可以指向程序員讓它指向的任何地方。如果程序員沒有給它賦一個值,它將具有一個默認的0值(NULL)([譯注]實際上,在C中一個為初始化的指針通常具有一個隨機的值,這是很危險的?。?。

這兩個聲明以不同的方式使用存儲區(qū),它們不可能共存。

避免這種類型沖突的一個方法是使用像lint這樣的工具(如果可以的話)。為了在一個程序的不同編譯單元之間檢查類型沖突,一些程序需要一次看到其所有部分。典型的編譯器無法完成,但lint可以。

避免該問題的另一種方法是將外部聲明放到包含文件中。這時,一個外部對象的類型僅出現(xiàn)一次[7]。語義缺陷

一個句子可以是精確拼寫的并且沒有語法錯誤,但仍然沒有意義。在這一節(jié)中,我們將會看到一些程序的寫法會使得它們看起來是一個意思,但實際上是另一種完全不同的意思。

我們還要討論一些表面上看起來合理但實際上會產(chǎn)生未定義結(jié)果的環(huán)境。我們這里討論的東西并不保證能夠在所有的C實現(xiàn)中工作。我們暫且忘記這些能夠在一些實現(xiàn)中工作但可能不能在另一些實現(xiàn)中工作的東西,直到第7節(jié)討論可以執(zhí)行問題為止。

4.1 表達式求值順序

一些C運算符以一種已知的、特定的順序?qū)ζ洳僮鲾?shù)進行求值。但另一些不能。例如,考慮下面的表達式:

a < b && c < d

C語言定義規(guī)定a < b首先被求值。如果a確實小于b,c < d必須緊接著被求值以計算整個表達式的值。但如果a大于或等于b,則c < d根本不會被求值。

要對a < b求值,編譯器對a和b的求值就會有一個先后。但在一些機器上,它們也許是并行進行的。

C中只有四個運算符&&、||、?:和,指定了求值順序。&&和||最先對左邊的操作數(shù)進行求值,而右邊的操作數(shù)只有在需要的時候才進行求值。而?:運算符中的三個操作數(shù):a、b和c,最先對a進行求值,之后僅對b或c中的一個進行求值,這取決于a的值。,運算符首先對左邊的操作數(shù)進行求值,然后拋棄它的值,對右邊的操作數(shù)進行求值[8]。

C中所有其它的運算符對操作數(shù)的求值順序都是未定義的。事實上,賦值運算符不對求值順序做出任何保證。

出于這個原因,下面這種將數(shù)組x中的前n個元素復(fù)制到數(shù)組y中的方法是不可行的: i = 0;while(i < n)

y[i] = x[i++];

其中的問題是y[i]的地址并不保證在i增長之前被求值。在某些實現(xiàn)中,這是可能的;但在另一些實現(xiàn)中卻不可能。另一種情況出于同樣的原因會失?。?i = 0;while(i < n)

y[i++] = x[i];

而下面的代碼是可以工作的: i = 0;while(i < n){

y[i] = x[i];

i++;}

當(dāng)然,這可以簡寫為: for(i = 0;i < n;i++)

y[i] = x[i];4.2 &&、||和!運算符

C中有兩種邏輯運算符,在某些情況下是可以交換的:按位運算符&、|和~,以及邏輯運算符&&、||和!。一個程序員如果用某一類運算符替換相應(yīng)的另一類運算符會得到某些奇怪的效果:程序可能會正確地工作,但這純屬偶然。

&&、||和!運算符將它們的參數(shù)視為僅有“真”或“假”,通常約定0代表“假”而其它的任意值都代表“真”。這些運算符返回1表示“真”而返回0表示“假”,而且&&和||運算符當(dāng)可以通過左邊的操作數(shù)確定其返回值時,就不會對右邊的操作數(shù)進行求值。

因此!10是零,因為10非零;10 && 12是1,因為10和12都非零;10 || 12也是1,因為10非零。另外,最后一個表達式中的12不會被求值,10 || f()中的f()也不會被求值。

考慮下面這段用于在一個表中查找一個特定元素的程序:

i = 0;while(i < tabsize && tab[i]!= x)

i++;

這段循環(huán)背后的意思是如果i等于tabsize時循環(huán)結(jié)束,元素未被找到。否則,i包含了元素的索引。

假設(shè)這個例子中的&&不小心被替換為了&,這個循環(huán)可能仍然能夠工作,但只有兩種幸運的情況可以使它停下來。

首先,這兩個操作都是當(dāng)條件為假時返回0,當(dāng)條件為真時返回1。只要x和y都是1或0,x & y和x && y都具有相同的值。然而,如果當(dāng)使用了除1之外的非零值表示“真”時互換了這兩個運算符,這個循環(huán)將不會工作。

其次,由于數(shù)組元素不會改變,因此越過數(shù)組最后一個元素前進一個位置時是無害的,循環(huán)會幸運地停下來。失誤的程序會越過數(shù)組的結(jié)尾,因為&不像&&,總是會對所有的操作數(shù)進行求值。因此循環(huán)的最后一次獲取tab[i]時i的值已經(jīng)等于tabsize了。如果tabsize是tab中元素的數(shù)量,則會取到tab中不存在的一個值。

4.3 下標(biāo)從零開始

在很多語言中,具有n個元素的數(shù)組其元素的號碼和它的下標(biāo)是從1到n嚴格對應(yīng)的。但在C中不是這樣。

一個具有n個元素的C數(shù)組中沒有下標(biāo)為n的元素,其中的元素的下標(biāo)是從0到n'a';

return c;}

在很多C實現(xiàn)中,為了減少比實際計算還要多的調(diào)用開銷,通常將其實現(xiàn)為宏:

#define toupper(c)((c)>= 'a' &&(c)<= 'z' ?(c)+('A''a')#define tolower(c)((c)+ 'A''a' :(c))#define tolower(c)((c)>= 'A' &&(c)<= 'Z' ?(c)+ 'a''a';

return c;}

tolower()類似。

這個改變帶來更多的問題,每次使用這些函數(shù)的時候都會引入函數(shù)調(diào)用開銷。我們的英雄認為一些人可能不愿意支付這些開銷,因此他們將這個宏重命名為:

#define _toupper(c)((c)+ 'A''A')這就允許用戶選擇方便或速度。

這里面其實只有一個問題:伯克利的人們和其他的C實現(xiàn)者并沒有跟著這么做。這意味著一個在AT&T系統(tǒng)上編寫的使用了toupper()或tolower()的程序,如果沒有為其傳遞正確大小寫字母參數(shù),在其他C實現(xiàn)中可能不會正常工作。

如果不知道這些歷史,可能很難對這類錯誤進行跟蹤。

7.8 先釋放,再重新分配

很多C實現(xiàn)為用戶提供了三個內(nèi)存分配函數(shù):malloc()、realloc()和free()。調(diào)用malloc(n)返回一個指向有n個字符的新分配的內(nèi)存的指針,這個指針可以由程序員使用。給free()傳遞一個指向由malloc()分配的內(nèi)存的指針可以使這塊內(nèi)存得以再次使用。通過一個指向已分配區(qū)域的指針和一個新的大小調(diào)用realloc()可以將這塊內(nèi)存擴大或縮小到新尺寸,這個過程中可能要復(fù)制內(nèi)存。

也許有人會想,真相真是有點微妙啊。下面是System V接口定義中出現(xiàn)的對realloc()的描述:

realloc改變一個由ptr指向的size個字節(jié)的塊,并返回該塊(可能被移動)的指針。在新舊尺寸中比較小的一個尺寸之下的內(nèi)容不會被改變。

而UNIX系統(tǒng)第七版的參考手冊中包含了這一段的副本。此外,還包含了描述realloc()的另外一段:

如果在最后一次調(diào)用malloc、realloc或calloc后釋放了ptr所指向的塊,realloc依舊可以工作;因此,free、malloc和realloc的順序可以利用malloc壓縮存貯的查找策略。

因此,下面的代碼片段在UNIX第七版中是合法的:

free(p);p = realloc(p, newsize);

這一特性保留在從UNIX第七版衍生出來的系統(tǒng)中:可以先釋放一塊存儲區(qū)域,然后再重新分配它。這意味著,在這些系統(tǒng)中釋放的內(nèi)存中的內(nèi)容在下一次內(nèi)存分配之前可以保證不變。因此,在這些系統(tǒng)中,我們可以用下面這種奇特的思想來釋放一個鏈表中的所有元素: for(p = head;p!= NULL;p = p->next)

free((char *)p);

而不用擔(dān)心調(diào)用free()會導(dǎo)致p->next不可用。

不用說,這種技術(shù)是不推薦的,因為不是所有C實現(xiàn)都能在內(nèi)存被釋放后將它的內(nèi)容保留足夠長的時間。然而,第七版的手冊遺留了一個未聲明的問題:realloc()的原始實現(xiàn)實際上是必須要先釋放再重新分配的。出于這個原因,一些C程序都是先釋放內(nèi)存再重新分配的,而當(dāng)這些程序移植到其他實現(xiàn)中時就會出現(xiàn)問題。

7.9 可移植性問題的一個實例

讓我們來看一個已經(jīng)被很多人在很多時候解決了的問題。下面的程序帶有兩個參數(shù):一個長整數(shù)和一個函數(shù)(的指針)。它將整數(shù)轉(zhuǎn)換位十進制數(shù),并用代表其中每一個數(shù)字的字符來調(diào)用給定的函數(shù)。

void printnum(long n, void(*p)()){

if(n < 0){

(*p)('-');

n =-n;

}

if(n >= 10)

printnum(n / 10, p);

(*p)(n % 10 + '0');}

這個程序非常簡單。首先檢查n是否為負數(shù);如果是,則打印一個符號并將n變?yōu)檎龜?shù)。接下來,測試是否n >= 10。如果是,則它的十進制表示中包含兩個或更多個數(shù)字,因此我們遞歸地調(diào)用printnum()來打印除最后一個數(shù)字外的所有數(shù)字。最后,我們打印最后一個數(shù)字。

這個程序——由于它的簡單——具有很多可移植性問題。首先是將n的低位數(shù)字轉(zhuǎn)換成字符形式的方法。用n % 10來獲取低位數(shù)字的值是好的,但為它加上'0'來獲得相應(yīng)的字符表示就不好了。這個加法假設(shè)機器中順序的數(shù)字所對應(yīng)的字符數(shù)順序的,沒有間隔,因此'0' + 5和'5'的值是相同的,等等。盡管這個假設(shè)對于ASCII和EBCDIC字符集是成立的,但對于其他一些機器可能不成立。避免這個問題的方法是使用一個表:

void printnum(long n, void(*p)()){

if(n < 0){

(*p)('-');

n =-n;

}

if(n >= 10)

printnum(n / 10, p);

(*p)(“0123456789”[n % 10]);}

另一個問題發(fā)生在當(dāng)n < 0時。這時程序會打印一個負號并將n設(shè)置為-n。這個賦值會發(fā)生溢出,因為在使用2的補碼的機器上通常能夠表示的負數(shù)比正數(shù)要多。例如,一個(長)整數(shù)有k位和一個附加位表示符號,則-2k可以表示而2k卻不能。

解決這一問題有很多方法。最直觀的一種是將n賦給一個unsigned long值。然而,一些C便一起可能沒有實現(xiàn)unsigned long,因此我們來看看沒有它怎么辦。

在第一個實現(xiàn)和第二個實現(xiàn)的機器上,改變一個正整數(shù)的符號保證不會發(fā)生溢出。問題僅出在改變一個負數(shù)的符號時。因此,我們可以通過避免將n變?yōu)檎龜?shù)來避免這個問題。

當(dāng)然,一旦我們打印了負數(shù)的符號,我們就能夠?qū)⒇摂?shù)和正數(shù)視為是一樣的。下面的方法就強制在打印符號之后n為負數(shù),并且用負數(shù)值完成我們所有的算法。如果我們這么做,我們就必須保證程序中打印符號的部分只執(zhí)行一次;一個簡單的方法是將這個程序劃分為兩個函數(shù): void printnum(long n, void(*p)()){

if(n < 0){

(*p)('-');

printneg(n, p);

}

else

printneg(-n, p);}

void printneg(long n, void(*p)()){

if(n <=-10)

printneg(n / 10, p);

(*p)(“0123456789”[-(n % 10)]);}

printnum()現(xiàn)在只檢查要打印的數(shù)是否為負數(shù);如果是的話則打印一個符號。否則,它以n的負絕對值來調(diào)用printneg()。我們同時改變了printneg()的函數(shù)體來適應(yīng)n永遠是負數(shù)或零這一事實。

我們得到什么?我們使用n / 10和n % 10來獲取n的前導(dǎo)數(shù)字和結(jié)尾數(shù)字(經(jīng)過適當(dāng)?shù)姆栕儞Q)。調(diào)用整數(shù)除法的行為在其中一個操作數(shù)為負的時候是實現(xiàn)相關(guān)的。因此,n % 10有可能是正的!這時,-(n % 10)是負數(shù),將會超出我們的數(shù)字字符數(shù)組的末尾。

為了解決這一問題,我們建立兩個臨時變量來存放商和余數(shù)。作完除法后,我們檢查余數(shù)是否在正確的范圍內(nèi),如果不是的話則調(diào)整這兩個變量。printnum()沒有改變,因此我們只列出printneg():

void printneg(long n, void(*p)()){

long q;

int r;

if(r > 0){

r-= 10;

q++;

}

if(n <=-10){

printneg(q, p);

}

(*p)(“0123456789”[-r]);} 這里是空閑空間

還有很多可能讓C程序員誤入迷途的地方本文沒有提到。如果你發(fā)現(xiàn)了,請聯(lián)系作者。在以后的版本中它會被包含進來,并添加一個表示感謝的腳注。

參考

《The C Programming Language》(Kernighan and Ritchie, Prentice-Hall 1978)是最具權(quán)威的C著作。它包含了一個優(yōu)秀的教程,面向那些熟悉其他高級語言程序設(shè)計的人,和一個參考手冊,簡潔地描述了整個語言。盡管自1978年以來這門語言發(fā)生了不少變化,這本書對于很多主題來說仍然是個定論。這本書同時還包含了本文中多次提到的“C語言參考手冊”。

《The C Puzzle Book》(Feuer, Prentice-Hall, 1982)是一本少見的磨煉人們文法能力的書。這本書收集了很多謎題(和答案),它們的解決方法能夠測試讀者對于C語言精妙之處的知識。

《C: A Referenct Manual》(Harbison and Steele, Prentice Hall 1984)是特意為實現(xiàn)者編寫的一本參考資料。其他人也會發(fā)現(xiàn)它是特別有用的——因為他能從中參考細節(jié)。

腳注

1.這本書是基于圖書《C Traps and Pitfalls》(Addison-Wesley, 1989, ISBN 0-201-17928-8)的一個擴充,有興趣的讀者可以讀一讀它。

2.因為!=的結(jié)果不是1就是0。

3.感謝Guy Harris為我指出這個問題。

4.Dennis Ritchie和Steve Johnson同時向我指出了這個問題。

5.感謝一位不知名的志愿者提出這個問題。

6.感謝Richard Stevens指出了這個問題。

7.一些C編譯器要求每個外部對象僅有一個定義,但可以有多個聲明。使用這樣的編譯器時,我們何以很容易地將一個聲明放到一個包含文件中,并將其定義放到其它地方。這意味著每個外部對象的類型將出現(xiàn)兩次,但這比出現(xiàn)多于兩次要好。

8.分離函數(shù)參數(shù)用的逗號不是逗號運算符。例如在f(x, y)中,x和y的獲取順序是未定義的,但在g((x, y))中不是這樣的。其中g(shù)只有一個參數(shù)。它的值是通過對x進行求值、拋棄這個值、再對y進行求值來確定的。

9.預(yù)處理器還可以很容易地組織這樣的顯式常量以能夠方便地找到它們。

10.PDP-11和VAX-11是數(shù)組設(shè)備集團(DEC)的商標(biāo)。

本文來自CSDN

客,轉(zhuǎn)

標(biāo)

:http://blog.csdn.net/milan25429688/archive/2005/03/24/328944.aspx#contents

第二篇:C語言缺陷與陷阱

C語言陷阱和缺陷

[譯序]

那些自認為已經(jīng)“學(xué)完”C語言的人,請你們仔細讀閱讀這篇文章吧。路還長,很多東西要學(xué)。我也是??

[概述]

C語言像一把雕刻刀,鋒利,并且在技師手中非常有用。和任何鋒利的工具一樣,C會傷到那些不能掌握它的人。本文介紹C語言傷害粗心的人的方法,以及如何避免傷害。

[內(nèi)容]

0 簡介 1 詞法缺陷 1.1 = 不是 == 1.2 & 和 | 不是 && 和 || 1.3 多字符記號 1.4 例外

1.5 字符串和字符 2 句法缺陷 2.1 理解聲明

2.2 運算符并不總是具有你所想象的優(yōu)先級 2.3 看看這些分號!2.4 switch語句 2.5 函數(shù)調(diào)用

2.6 懸掛else問題 3 鏈接

3.1 你必須自己檢查外部類型 4 語義缺陷

4.1 表達式求值順序 4.2 &&、||和!運算符 4.3 下標(biāo)從零開始

4.4 C并不總是轉(zhuǎn)換實參 4.5 指針不是數(shù)組 4.6 避免提喻法

4.7 空指針不是空字符串 4.8 整數(shù)溢出 4.9 移位運算符 5 庫函數(shù)

5.1 getc()返回整數(shù) 5.2 緩沖輸出和內(nèi)存分配 6 預(yù)處理器 6.1 宏不是函數(shù) 6.2 宏不是類型定義 7 可移植性缺陷

7.1 一個名字中都有什么? 7.2 一個整數(shù)有多大?

7.3 字符是帶符號的還是無符號的? 7.4 右移位是帶符號的還是無符號的? 7.5 除法如何舍入? 7.6 一個隨機數(shù)有多大? 7.7 大小寫轉(zhuǎn)換

7.8 先釋放,再重新分配 7.9 可移植性問題的一個實例 8 這里是空閑空間 參考 腳注

0 簡介

C語言及其典型實現(xiàn)被設(shè)計為能被專家們?nèi)菀椎厥褂?。這門語言簡潔并附有表達力。但有一些限制可以保護那些浮躁的人。一個浮躁的人可以從這些條款中獲得一些幫助。

在本文中,我們將會看一看這些未可知的益處。這是由于它的未可知,我們無法為其進行完全的分類。不過,我們?nèi)匀煌ㄟ^研究為了一個C程序的運行所需要做的事來做到這些。我們假設(shè)讀者對C語言至少有個粗淺的了解。

第一部分研究了當(dāng)程序被劃分為記號時會發(fā)生的問題。第二部分繼續(xù)研究了當(dāng)程序的記號被編譯器組合為聲明、表達式和語句時會出現(xiàn)的問題。第三部分研究了由多個部分組成、分別編譯并綁定到一起的C程序。第四部分處理了概念上的誤解:當(dāng)一個程序具體執(zhí)行時會發(fā)生的事情。第五部分研究了我們的程序和它們所使用的常用庫之間的關(guān)系。在第六部分中,我們注意到了我們所寫的程序也不并不是我們所運行的程序;預(yù)處理器將首先運行。最后,第七部分討論了可移植性問題:一個能在一個實現(xiàn)中運行的程序無法在另一個實現(xiàn)中運行的原因。詞法缺陷

編譯器的第一個部分常被稱為詞法分析器(lexical analyzer)。詞法分析器檢查組成程序的字符序列,并將它們劃分為記號(token)一個記號是一個有一個或多個字符的序列,它在語言被編譯時具有一個(相關(guān)地)統(tǒng)一的意義。在C中,例如,記號->的意義和組成它的每個獨立的字符具有明顯的區(qū)別,而且其意義獨立于->出現(xiàn)的上下文環(huán)境。

另外一個例子,考慮下面的語句:

if(x > big)big = x;

該語句中的每一個分離的字符都被劃分為一個記號,除了關(guān)鍵字if和標(biāo)識符big的兩個實例。

事實上,C程序被兩次劃分為記號。首先是預(yù)處理器讀取程序。它必須對程序進行記號劃分以發(fā)現(xiàn)標(biāo)識宏的標(biāo)識符。它必須通過對每個宏進行求值來替換宏調(diào)用。最后,經(jīng)過宏替換的程序又被匯集成字符流送給編譯器。編譯器再第二次將這個流劃分為記號。

在這一節(jié)中,我們將探索對記號的意義的普遍的誤解以及記號和組成它們的字符之間的關(guān)系。稍后我們將談到預(yù)處理器。

1.1 = 不是 == 從Algol派生出來的語言,如Pascal和Ada,用:=表示賦值而用=表示比較。而C語言則是用=表示賦值而用==表示比較。這是因為賦值的頻率要高于比較,因此為其分配更短的符號。

此外,C還將賦值視為一個運算符,因此可以很容易地寫出多重賦值(如a = b = c),并且可以將賦值嵌入到一個大的表達式中。

這種便捷導(dǎo)致了一個潛在的問題:可能將需要比較的地方寫成賦值。因此,下面的語句好像看起來是要檢查x是否等于y:

if(x = y)foo();

而實際上是將x設(shè)置為y的值并檢查結(jié)果是否非零。在考慮下面的一個希望跳過空格、制表符和換行符的循環(huán):

while(c == ' ' || c = 't' || c == 'n')c = getc(f);

在與't'進行比較的地方程序員錯誤地使用=代替了==。這個“比較”實際上是將't'賦給c,然后判斷c的(新的)值是否為零。因為't'不為零,這個“比較”將一直為真,因此這個循環(huán)會吃盡整個文件。這之后會發(fā)生什么取決于特定的實現(xiàn)是否允許一個程序讀取超過文件尾部的部分。如果允許,這個循環(huán)會一直運行。

一些C編譯器會對形如e1 = e2的條件給出一個警告以提醒用戶。當(dāng)你趨勢需要先對一個變量進行賦值之后再檢查變量是否非零時,為了在這種編譯器中避免警告信息,應(yīng)考慮顯式給出比較符。換句話說,將:

if(x = y)foo();

改寫為:

if((x = y)!= 0)foo();

這樣可以清晰地表示你的意圖。

1.2 & 和 | 不是 && 和 || 容易將==錯寫為=是因為很多其他語言使用=表示比較運算。其他容易寫錯的運算符還有&和&&,或|和||,這主要是因為C語言中的&和|運算符于其他語言中具有類似功能的運算符大為不同。我們將在第4節(jié)中貼近地觀察這些運算符。

1.3 多字符記號

一些C記號,如/、*和=只有一個字符。而其他一些C記號,如/*和==,以及標(biāo)識符,具有多個字符。當(dāng)C編譯器遇到緊連在一起的/和*時,它必須能夠決定是將這兩個字符識別為兩個分離的記號還是一個單獨的記號。C語言參考手冊說明了如何決定:“如果輸入流到一個給定的字符串為止已經(jīng)被識別為記號,則應(yīng)該包含下一個字符以組成能夠構(gòu)成記號的最長的字符串”。因此,如果/是一個記號的第一個字符,并且/后面緊隨了一個*,則這兩個字符構(gòu)成了注釋的開始,不管其他上下文環(huán)境。

下面的語句看起來像是將y的值設(shè)置為x的值除以p所指向的值:

y = x/*p /* p 指向除數(shù) */;

實際上,/*開始了一個注釋,因此編譯器簡單地吞噬程序文本,直到*/的出現(xiàn)。換句話說,這條語句僅僅把y的值設(shè)置為x的值,而根本沒有看到p。將這條語句重寫為:

y = x / *p /* p 指向除數(shù) */;

或者干脆是

y = x /(*p)/* p指向除數(shù) */;

它就可以做注釋所暗示的除法了。

這種模棱兩可的寫法在其他環(huán)境中就會引起麻煩。例如,老版本的C使用=+表示現(xiàn)在版本中的+=。這樣的編譯器會將

a=-1;

視為

a =-1;或

a = a> a

是不合法的。它和

p-> a

不是同義詞。

另一方面,有些老式編譯器還是將=+視為一個單獨的記號并且和+=是同義詞。

1.5 字符串和字符

單引號和雙引號在C中的意義完全不同,在一些混亂的上下文中它們會導(dǎo)致奇怪的結(jié)果而不是錯誤消息。

包圍在單引號中的一個字符只是書寫整數(shù)的另一種方法。這個整數(shù)是給定的字符在實現(xiàn)的對照序列中的一個對應(yīng)的值。因此,在一個ASCII實現(xiàn)中,'a'和0141或97表示完全相同的東西。而一個包圍在雙引號中的字符串,只是書寫一個有雙引號之間的字符和一個附加的二進制值為零的字符所初始化的一個無名數(shù)組的指針的一種簡短方法。

線面的兩個程序片斷是等價的:

printf(“Hello worldn”);

char hello[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'n', 0 };printf(hello);

使用一個指針來代替一個整數(shù)通常會得到一個警告消息(反之亦然),使用雙引號來代替單引號也會得到一個警告消息(反之亦然)。但對于不檢查參數(shù)類型的編譯器卻除外。因此,用

printf('n');

來代替

printf(“n”);

通常會在運行時得到奇怪的結(jié)果。

由于一個整數(shù)通常足夠大,以至于能夠放下多個字符,一些C編譯器允許在一個字符常量中存放多個字符。這意味著用'yes'代替“yes”將不會被發(fā)現(xiàn)。后者意味著“分別包含y、e、s和一個空字符的四個連續(xù)存貯器區(qū)域中的第一個的地址”,而前者意味著“在一些實現(xiàn)定義的樣式中表示由字符y、e、s聯(lián)合構(gòu)成的一個整數(shù)”。這兩者之間的任何一致性都純屬巧合。句法缺陷

要理解C語言程序,僅了解構(gòu)成它的記號是不夠的。還要理解這些記號是如何構(gòu)成聲明、表達式、語句和程序的。盡管這些構(gòu)成通常都是定義良好的,但這些定義有時候是有悖于直覺的或混亂的。

在這一節(jié)中,我們將著眼于一些不明顯句法構(gòu)造。

2.1 理解聲明

我曾經(jīng)和一些人聊過天,他們那時在書寫在一個小型的微處理器上單機運行的C程序。當(dāng)這臺機器的開關(guān)打開的時候,硬件會調(diào)用地址為0處的子程序。

為了模仿電源打開的情形,我們要設(shè)計一條C語句來顯式地調(diào)用這個子程序。經(jīng)過一些思考,我們寫出了下面的語句:

(*(void(*)())0)();

這樣的表達式會令C程序員心驚膽戰(zhàn)。但是,并不需要這樣,因為他們可以在一個簡單的規(guī)則的幫助下很容易地構(gòu)造它:以你使用的方式聲明它。

每個C變量聲明都具有兩個部分:一個類型和一組具有特定格式的期望用來對該類型求值的表達式。最簡單的表達式就是一個變量:

float f, g;

說明表達式f和g——在求值的時候——具有類型float。由于待求值的時表達式,因此可以自由地使用圓括號: float((f));

者表示((f))求值為float并且因此,通過推斷,f也是一個float。

同樣的邏輯用在函數(shù)和指針類型。例如:

float ff();

表示表達式ff()是一個float,因此ff是一個返回一個float的函數(shù)。類似地,float *pf;

表示*pf是一個float并且因此pf是一個指向一個float的指針。

這些形式的組合聲明對表達式是一樣的。因此,float *g(),(*h)();

表示*g()和(*h)()都是float表達式。由于()比*綁定得更緊密,*g()和*(g())表示同樣的東西:g是一個返回指float指針的函數(shù),而h是一個指向返回float的函數(shù)的指針。

當(dāng)我們知道如何聲明一個給定類型的變量以后,就能夠很容易地寫出一個類型的模型(cast):只要刪除變量名和分號并將所有的東西包圍在一對圓括號中即可。因此,由于

float *g();

聲明g是一個返回float指針的函數(shù),所以(float *())就是它的模型。

有了這些知識的武裝,我們現(xiàn)在可以準(zhǔn)備解決(*(void(*)())0)()了。我們可以將它分為兩個部分進行分析。首先,假設(shè)我們有一個變量fp,它包含了一個函數(shù)指針,并且我們希望調(diào)用fp所指向的函數(shù)。可以這樣寫:

(*fp)();

如果fp是一個指向函數(shù)的指針,則*fp就是函數(shù)本身,因此(*fp)()是調(diào)用它的一種方法。(*fp)中的括號是必須的,否則這個表達式將會被分析為*(fp())。我們現(xiàn)在要找一個適當(dāng)?shù)谋磉_式來替換fp。

這個問題就是我們的第二步分析。如果C可以讀入并理解類型,我們可以寫:

(*0)();

但這樣并不行,因為*運算符要求必須有一個指針作為他的操作數(shù)。另外,這個操作數(shù)必須是一個指向函數(shù)的指針,以保證*的結(jié)果可以被調(diào)用。因此,我們需要將0轉(zhuǎn)換為一個可以描述“指向一個返回void的函數(shù)的指針”的類型。如果fp是一個指向返回void的函數(shù)的指針,則(*fp)()是一個void值,并且它的聲明將會是這樣的:

void(*fp)();

因此,我們需要寫:

void(*fp)();(*fp)();

來聲明一個啞變量。一旦我們知道了如何聲明該變量,我們也就知道了如何將一個常數(shù)轉(zhuǎn)換為該類型:只要從變量的聲明中去掉名字即可。因此,我們像下面這樣將0轉(zhuǎn)換為一個“指向返回void的函數(shù)的指針”:

(void(*)())0

接下來,我們用(void(*)())0來替換fp:

(*(void(*)())0)();

結(jié)尾處的分號用于將這個表達式轉(zhuǎn)換為一個語句。

在這里,我們就解決了這個問題時沒有使用typedef聲明。通過使用它,我們可以更清晰地解決這個問題:

typedef void(*funcptr)();(*(funcptr)0)();

2.2 運算符并不總是具有你所想象的優(yōu)先級

假設(shè)有一個聲明了的常量FLAG是一個整數(shù),其二進制表示中的某一位被置位(換句話說,它是2的某次冪),并且你希望測試一個整型變量flags該位是否被置位。通常的寫法是:

if(flags & FLAG)...其意義對于很多C程序員都是很明確的:if語句測試括號中的表達式求值的結(jié)果是否為0。出于清晰的目的我們可以將它寫得更明確:

if(flags & FLAG!= 0)...這個語句現(xiàn)在更容易理解了。但它仍然是錯的,因為!=比&綁定得更緊密,因此它被分析為:

if(flags &(FLAG!= 0))...這(偶爾)是可以的,如FLAG是1或0(!)的時候,但對于其他2的冪是不行的[2]。

假設(shè)你有兩個整型變量,h和l,它們的值在0和15(含0和15)之間,并且你希望將r設(shè)置為8位值,其低位為l,高位為h。一種自然的寫法是:

r = h << 4 + 1;

不幸的是,這是錯誤的。加法比移位綁定得更緊密,因此這個例子等價于:

r = h <<(4 + l);

正確的方法有兩種:

r =(h << 4)+ l;

r = h << 4 | l;

避免這種問題的一個方法是將所有的東西都用括號括起來,但表達式中的括號過度就會難以理解,因此最好還是是記住C中的優(yōu)先級。

不幸的是,這有15個,太困難了。然而,通過將它們分組可以變得容易。

綁定得最緊密的運算符并不是真正的運算符:下標(biāo)、函數(shù)調(diào)用和結(jié)構(gòu)選擇。這些都與左邊相關(guān)聯(lián)。

接下來是一元運算符。它們具有真正的運算符中的最高優(yōu)先級。由于函數(shù)調(diào)用比一元運算符綁定得更緊密,你必須寫(*p)()來調(diào)用p指向的函數(shù);*p()表示p是一個返回一個指針的函數(shù)。轉(zhuǎn)換是一元運算符,并且和其他一元運算符具有相同的優(yōu)先級。一元運算符是右結(jié)合的,因此*p++表示*(p++),而不是(*p)++。

在接下來是真正的二元運算符。其中數(shù)學(xué)運算符具有最高的優(yōu)先級,然后是移位運算符、關(guān)系運算符、邏輯運算符、賦值運算符,最后是條件運算符。需要記住的兩個重要的東西是:

所有的邏輯運算符具有比所有關(guān)系運算符都低的優(yōu)先級。

一位運算符比關(guān)系運算符綁定得更緊密,但又不如數(shù)學(xué)運算符。

在這些運算符類別中,有一些奇怪的地方。乘法、除法和求余具有相同的優(yōu)先級,加法和減法具有相同的優(yōu)先級,以及移位運算符具有相同的優(yōu)先級。

還有就是六個關(guān)系運算符并不具有相同的優(yōu)先級:==和!=的優(yōu)先級比其他關(guān)系運算符要低。這就允許我們判斷a和b是否具有與c和d相同的順序,例如:

a < b == c < d

在邏輯運算符中,沒有任何兩個具有相同的優(yōu)先級。按位運算符比所有順序運算符綁定得都緊密,每種與運算符都比相應(yīng)的或運算符綁定得更緊密,并且按位異或(^)運算符介于按位與和按位或之間。

三元運算符的優(yōu)先級比我們提到過的所有運算符的優(yōu)先級都低。這可以保證選擇表達式中包含的關(guān)系運算符的邏輯組合特性,如:

z = a < b && b < c ? d : e

這個例子還說明了賦值運算符具有比條件運算符更低的優(yōu)先級是有意義的。另外,所有的復(fù)合賦值運算符具有相同的優(yōu)先級并且是自右至左結(jié)合的,因此

a = b = c 和

b = c;a = b;

是等價的。

具有最低優(yōu)先級的是逗號運算符。這很容易理解,因為逗號通常在需要表達式而不是語句的時候用來替代分號。

賦值是另一種運算符,通常具有混合的優(yōu)先級。例如,考慮下面這個用于復(fù)制文件的循環(huán):

while(c = getc(in)!= EOF)putc(c, out);

這個while循環(huán)中的表達式看起來像是c被賦以getc(in)的值,接下來判斷是否等于EOF以結(jié)束循環(huán)。不幸的是,賦值的優(yōu)先級比任何比較操作都低,因此c的值將會是getc(in)和EOF比較的結(jié)果,并且會被拋棄。因此,“復(fù)制”得到的文件將是一個由值為1的字節(jié)流組成的文件。

上面這個例子正確的寫法并不難:

while((c = getc(in))!= EOF)putc(c, out);

然而,這種錯誤在很多復(fù)雜的表達式中卻很難被發(fā)現(xiàn)。例如,隨UNIX系統(tǒng)一同發(fā)布的lint程序通常帶有下面的錯誤行:

if(((t = BTYPE(pt1->aty)== STRTY)|| t == UNIONTY){

這條語句希望給t賦一個值,然后看t是否與STRTY或UNIONTY相等。而實際的效果卻大不相同[3]。

C中的邏輯運算符的優(yōu)先級具有歷史原因。B——C的前輩——具有和C中的&和|運算符對應(yīng)的邏輯運算符。盡管它們的定義是按位的,但編譯器在條件判斷上下文中將它們視為和&&和||一樣。當(dāng)在C中將它們分開后,優(yōu)先級的改變是很危險的[4]。

2.3 看看這些分號!

C中的一個多余的分號通常會帶來一點點不同:或者是一個空語句,無任何效果;或者編譯器可能提出一個診斷消息,可以方便除去掉它。一個重要的區(qū)別是在必須跟有一個語句的if和while語句中??紤]下面的例子:

if(x > big);big = x;這不會發(fā)生編譯錯誤,但這段程序的意義與: if(x > big)big = x;就大不相同了。第一個程序段等價于: if(x > big){ } big = x;也就是等價于: big = x;(除非x、i或big是帶有副作用的宏)。另一個因分號引起巨大不同的地方是函數(shù)定義前面的結(jié)構(gòu)聲明的末尾[譯注:這句話不太好聽,看例子就明白了]??紤]下面的程序片段: struct foo { int x;} f(){...} 在緊挨著f的第一個}后面丟失了一個分號。它的效果是聲明了一個函數(shù)f,返回值類型是struct foo,這個結(jié)構(gòu)成了函數(shù)聲明的一部分。如果這里出現(xiàn)了分號,則f將被定義為具有默認的整型返回值[5]。2.4 switch語句 通常C中的switch語句中的case段可以進入下一個。例如,考慮下面的C和Pascal程序片斷: switch(color){ case 1: printf(“red”);break;case 2: printf(“yellow”);break;case 3: printf(“blue”);break;} case color of 1: write('red');2: write('yellow');3: write('blue');end 這兩個程序片斷都作相同的事情:根據(jù)變量color的值是1、2還是3打印red、yellow或blue(沒有新行符)。這兩個程序片斷非常相似,只有一點不同:Pascal程序中沒有C中相應(yīng)的break語句。C中的case標(biāo)簽是真正的標(biāo)簽:控制流程可以無限制地進入到一個case標(biāo)簽中??纯戳硪环N形式,假設(shè)C程序段看起來更像Pascal: switch(color){ case 1: printf(“red”);case 2: printf(“yellow”);case 3: printf(“blue”);} 并且假設(shè)color的值是2。則該程序?qū)⒋蛴ellowblue,因為控制自然地轉(zhuǎn)入到下一個printf()的調(diào)用。這既是C語言switch語句的優(yōu)點又是它的弱點。說它是弱點,是因為很容易忘記一個break語句,從而導(dǎo)致程序出現(xiàn)隱晦的異常行為。說它是優(yōu)點,是因為通過故意去掉break語句,可以很容易實現(xiàn)其他方法難以實現(xiàn)的控制結(jié)構(gòu)。尤其是在一個大型的switch語句中,我們經(jīng)常發(fā)現(xiàn)對一個case的處理可以簡化其他一些特殊的處理。例如,設(shè)想有一個程序是一臺假想的機器的翻譯器。這樣的一個程序可能包含一個switch語句來處理各種操作碼。在這樣一臺機器上,通常減法在對其第二個運算數(shù)進行變號后就變成和加法一樣了。因此,最好可以寫出這樣的語句: case SUBTRACT: opnd2 =-opnd2;/* no break;*/ case ADD:...另外一個例子,考慮編譯器通過跳過空白字符來查找一個記號。這里,我們將空格、制表符和新行符視為是相同的,除了新行符還要引起行計數(shù)器的增長外: case 'n': linecount++;/* no break */ case 't': case ' ':...2.5 函數(shù)調(diào)用 和其他程序設(shè)計語言不同,C要求一個函數(shù)調(diào)用必須有一個參數(shù)列表,但可以沒有參數(shù)。因此,如果f是一個函數(shù),f();就是對該函數(shù)進行調(diào)用的語句,而 f;什么也不做。它會作為函數(shù)地址被求值,但不會調(diào)用它[6]。2.6 懸掛else問題 在討論任何語法缺陷時我們都不會忘記提到這個問題。盡管這一問題不是C語言所獨有的,但它仍然傷害著那些有著多年經(jīng)驗的C程序員??紤]下面的程序片斷: if(x == 0)if(y == 0)error();else { z = x + y;f(&z);} 寫這段程序的程序員的目的明顯是將情況分為兩種:x = 0和x!= 0。在第一種情況中,程序段什么都不做,除非y = 0時調(diào)用error()。第二種情況中,程序設(shè)置z = x + y并以z的地址作為參數(shù)調(diào)用f()。然而,這段程序的實際效果卻大為不同。其原因是一個else總是與其最近的if相關(guān)聯(lián)。如果我們希望這段程序能夠按照實際的情況運行,應(yīng)該這樣寫: if(x == 0){ if(y == 0)error();else { z = x + y;f(&z);} } 換句話說,當(dāng)x!= 0發(fā)生時什么也不做。如果要達到第一個例子的效果,應(yīng)該寫: if(x == 0){ if(y ==0)error();} else { z = z + y;f(&z);} 3 鏈接 一個C程序可能有很多部分組成,它們被分別編譯,并由一個通常稱為鏈接器、鏈接編輯器或加載器的程序綁定到一起。由于編譯器一次通常只能看到一個文件,因此它無法檢測到需要程序的多個源文件的內(nèi)容才能發(fā)現(xiàn)的錯誤。在這一節(jié)中,我們將看到一些這種類型的錯誤。有一些C實現(xiàn),但不是所有的,帶有一個稱為lint的程序來捕獲這些錯誤。如果具有一個這樣的程序,那么無論怎樣地強調(diào)它的重要性都不過分。3.1 你必須自己檢查外部類型 假設(shè)你有一個C程序,被劃分為兩個文件。其中一個包含如下聲明: int n;而令一個包含如下聲明: long n;這不是一個有效的C程序,因為一些外部名稱在兩個文件中被聲明為不同的類型。然而,很多實現(xiàn)檢測不到這個錯誤,因為編譯器在編譯其中一個文件時并不知道另一個文件的內(nèi)容。因此,檢查類型的工作只能由鏈接器(或一些工具程序如lint)來完成;如果操作系統(tǒng)的鏈接器不能識別數(shù)據(jù)類型,C編譯器也沒法過多地強制它。那么,這個程序運行時實際會發(fā)生什么?這有很多可能性: 實現(xiàn)足夠聰明,能夠檢測到類型沖突。則我們會得到一個診斷消息,說明n在兩個文件中具有不同的類型。你所使用的實現(xiàn)將int和long視為相同的類型。典型的情況是機器可以自然地進行32位運算。在這種情況下你的程序或許能夠工作,好象你兩次都將變量聲明為long(或int)。但這種程序的工作純屬偶然。n的兩個實例需要不同的存儲,它們以某種方式共享存儲區(qū),即對其中一個的賦值對另一個也有效。這可能發(fā)生,例如,編譯器可以將int安排在long的低位。不論這是基于系統(tǒng)的還是基于機器的,這種程序的運行同樣是偶然。n的兩個實例以另一種方式共享存儲區(qū),即對其中一個賦值的效果是對另一個賦以不同的值。在這種情況下,程序可能失敗。這種情況發(fā)生的里一個例子出奇地頻繁。程序的某一個文件包含下面的聲明: char filename[] = “etc/passwd”;而另一個文件包含這樣的聲明: char *filename;盡管在某些環(huán)境中數(shù)組和指針的行為非常相似,但它們是不同的。在第一個聲明中,filename是一個字符數(shù)組的名字。盡管使用數(shù)組的名字可以產(chǎn)生數(shù)組第一個元素的指針,但這個指針只有在需要的時候才產(chǎn)生并且不會持續(xù)。在第二個聲明中,filename是一個指針的名字。這個指針可以指向程序員讓它指向的任何地方。如果程序員沒有給它賦一個值,它將具有一個默認的0值(null)[譯注:實際上,在C中一個為初始化的指針通常具有一個隨機的值,這是很危險的!]。這兩個聲明以不同的方式使用存儲區(qū),他們不可能共存。避免這種類型沖突的一個方法是使用像lint這樣的工具(如果可以的話)。為了在一個程序的不同編譯單元之間檢查類型沖突,一些程序需要一次看到其所有部分。典型的編譯器無法完成,但lint可以。避免該問題的另一種方法是將外部聲明放到包含文件中。這時,一個外部對象的類型僅出現(xiàn)一次[7]。4 語義缺陷 一個句子可以是精確拼寫的并且沒有語法錯誤,但仍然沒有意義。在這一節(jié)中,我們將會看到一些程序的寫法會使得它們看起來是一個意思,但實際上是另一種完全不同的意思。我們還要討論一些表面上看起來合理但實際上會產(chǎn)生未定義結(jié)果的環(huán)境。我們這里討論的東西并不保證能夠在所有的C實現(xiàn)中工作。我們暫且忘記這些能夠在一些實現(xiàn)中工作但可能不能在另一些實現(xiàn)中工作的東西,直到第7節(jié)討論可以執(zhí)行問題為止。4.1 表達式求值順序 一些C運算符以一種已知的、特定的順序?qū)ζ洳僮鲾?shù)進行求值。但另一些不能。例如,考慮下面的表達式: a < b && c < d C語言定義規(guī)定a < b首先被求值。如果a確實小于b,c < d必須緊接著被求值以計算整個表達式的值。但如果a大于或等于b,則c < d根本不會被求值。要對a < b求值,編譯器對a和b的求值就會有一個先后。但在一些機器上,它們也許是并行進行的。C中只有四個運算符&&、||、?:和,指定了求值順序。&&和||最先對左邊的操作數(shù)進行求值,而右邊的操作數(shù)只有在需要的時候才進行求值。而?:運算符中的三個操作數(shù):a、b和c,最先對a進行求值,之后僅對b或c中的一個進行求值,這取決于a的值。,運算符首先對左邊的操作數(shù)進行求值,然后拋棄它的值,對右邊的操作數(shù)進行求值[8]。C中所有其它的運算符對操作數(shù)的求值順序都是未定義的。事實上,賦值運算符不對求值順序做出任何保證。出于這個原因,下面這種將數(shù)組x中的前n個元素復(fù)制到數(shù)組y中的方法是不可行的: i = 0;while(i < n)y = x[i++];其中的問題是y的地址并不保證在i增長之前被求值。在某些實現(xiàn)中,這是可能的;但在另一些實現(xiàn)中卻不可能。另一種情況出于同樣的原因會失?。? i = 0;while(i < n)y[i++] = x;而下面的代碼是可以工作的: i = 0;while(i < n){ y = x;i++;} 當(dāng)然,這可以簡寫為: for(i = 0;i < n;i++)y = x;4.2 &&、||和!運算符 C中有兩種邏輯運算符,在某些情況下是可以交換的:按位運算符&、|和~,以及邏輯運算符&&、||和!。一個程序員如果用某一類運算符替換相應(yīng)的另一類運算符會得到某些奇怪的效果:程序可能會正確地工作,但這純屬偶然。&&、||和!運算符將它們的參數(shù)視為僅有“真”或“假”,通常約定0代表“假”而其它的任意值都代表“真”。這些運算符返回1表示“真”而返回0表示“假”,而且&&和||運算符當(dāng)可以通過左邊的操作數(shù)確定其返回值時,就不會對右邊的操作數(shù)進行求值。因此!10是零,因為10非零;10 && 12是1,因為10和12都非零;10 || 12也是1,因為10非零。另外,最后一個表達式中的12不會被求值,10 || f()中的f()也不會被求值??紤]下面這段用于在一個表中查找一個特定元素的程序: i = 0;while(i < tabsize && tab!= x)i++;這段循環(huán)背后的意思是如果i等于tabsize時循環(huán)結(jié)束,元素未被找到。否則,i包含了元素的索引。假設(shè)這個例子中的&&不小心被替換為了&,這個循環(huán)可能仍然能夠工作,但只有兩種幸運的情況可以使它停下來。首先,這兩個操作都是當(dāng)條件為假時返回0,當(dāng)條件為真時返回1。只要x和y都是1或0,x & y和x && y都具有相同的值。然而,如果當(dāng)使用了出了1之外的非零值表示“真”時互換了這兩個運算符,這個循環(huán)將不會工作。其次,由于數(shù)組元素不會改變,因此越過數(shù)組最后一個元素進一個位置時是無害的,循環(huán)會幸運地停下來。失誤的程序會越過數(shù)組的結(jié)尾,因為&不像&&,總是會對所有的操作數(shù)進行求值。因此循環(huán)的最后一次獲取tab時i的值已經(jīng)等于tabsize了。如果tabsize是tab中元素的數(shù)量,則會取到tab中不存在的一個值。4.3 下標(biāo)從零開始 在很多語言中,具有n個元素的數(shù)組其元素的號碼和它的下標(biāo)是從1到n嚴格對應(yīng)的。但在C中不是這樣。一個具有n個元素的C數(shù)組中沒有下標(biāo)為n的元素,其中的元素的下標(biāo)是從0到n'a';return c;} 在很多C實現(xiàn)中,為了減少比實際計算還要多的調(diào)用開銷,通常將其實現(xiàn)為宏: #define toupper(c)((c)>= 'a' &&(c)<= 'z' ?(c)+('A''a')#define tolower(c)((c)+ 'A''a' :(c))#define tolower(c)((c)>= 'A' &&(c)<= 'Z' ?(c)+ 'a''a';return c;} tolower()類似。這個改變帶來更多的問題,每次使用這些函數(shù)的時候都會引入函數(shù)調(diào)用開銷。我們的英雄認為一些人可能不愿意支付這些開銷,因此他們將這個宏重命名為: #define _toupper(c)((c)+ 'A''A')這就允許用戶選擇方便或速度。這里面其實只有一個問題:伯克利的人們和其他的C實現(xiàn)者并沒有跟著這么做。這意味著一個在AT&T系統(tǒng)上編寫的使用了toupper()或tolower()的程序,如果沒有為其傳遞正確大小寫字母參數(shù),在其他C實現(xiàn)中可能不會正常工作。如果不知道這些歷史,可能很難對這類錯誤進行跟蹤。7.8 先釋放,再重新分配 很多C實現(xiàn)為用戶提供了三個內(nèi)存分配函數(shù):malloc()、realloc()和free()。調(diào)用malloc(n)返回一個指向有n個字符的新分配的內(nèi)存的指針,這個指針可以由程序員使用。給free()傳遞一個指向由malloc()分配的內(nèi)存的指針可以使這塊內(nèi)存得以重用。通過一個指向已分配區(qū)域的指針和一個新的大小調(diào)用realloc()可以將這塊內(nèi)存擴大或縮小到新尺寸,這個過程中可能要復(fù)制內(nèi)存。也許有人會想,真相真是有點微妙啊。下面是System V接口定義中出現(xiàn)的對realloc()的描述: realloc改變一個由ptr指向的size個字節(jié)的塊,并返回該塊(可能被移動)的指針。在新舊尺寸中比較小的一個尺寸之下的內(nèi)容不會被改變。而UNIX系統(tǒng)第七版的參考手冊中包含了這一段的副本。此外,還包含了描述realloc()的另外一段: 如果在最后一次調(diào)用malloc、realloc或calloc后釋放了ptr所指向的塊,realloc依舊可以工作;因此,free、malloc和realloc的順序可以利用malloc壓縮存貯的查找策略。因此,下面的代碼片段在UNIX第七版中是合法的: free(p);p = realloc(p, newsize);這一特性保留在從UNIX第七版衍生出來的系統(tǒng)中:可以先釋放一塊存儲區(qū)域,然后再重新分配它。這意味著,在這些系統(tǒng)中釋放的內(nèi)存中的內(nèi)容在下一次內(nèi)存分配之前可以保證不變。因此,在這些系統(tǒng)中,我們可以用下面這種奇特的思想來釋放一個鏈表中的所有元素: for(p = head;p!= NULL;p = p->next)free((char *)p);而不用擔(dān)心調(diào)用free()會導(dǎo)致p->next不可用。不用說,這種技術(shù)是不推薦的,因為不是所有C實現(xiàn)都能在內(nèi)存被釋放后將它的內(nèi)容保留足夠長的時間。然而,第七版的手冊遺留了一個未聲明的問題:realloc()的原始實現(xiàn)實際上是必須要先釋放再重新分配的。出于這個原因,一些C程序都是先釋放內(nèi)存再重新分配的,而當(dāng)這些程序移植到其他實現(xiàn)中時就會出現(xiàn)問題。7.9 可移植性問題的一個實例 讓我們來看一個已經(jīng)被很多人在很多時候解決了的問題。下面的程序帶有兩個參數(shù):一個長整數(shù)和一個函數(shù)(的指針)。它將整數(shù)轉(zhuǎn)換位十進制數(shù),并用代表其中每一個數(shù)字的字符來調(diào)用給定的函數(shù)。void printnum(long n, void(*p)()){ if(n < 0){(*p)('-');n =-n;} if(n >= 10)printnum(n / 10, p);(*p)(n % 10 + '0');} 這個程序非常簡單。首先檢查n是否為負數(shù);如果是,則打印一個符號并將n變?yōu)檎龜?shù)。接下來,測試是否n >= 10。如果是,則它的十進制表示中包含兩個或更多個數(shù)字,因此我們遞歸地調(diào)用printnum()來打印除最后一個數(shù)字外的所有數(shù)字。最后,我們打印最后一個數(shù)字。這個程序——由于它的簡單——具有很多可移植性問題。首先是將n的低位數(shù)字轉(zhuǎn)換成字符形式的方法。用n % 10來獲取低位數(shù)字的值是好的,但為它加上'0'來獲得相應(yīng)的字符表示就不好了。這個加法假設(shè)機器中順序的數(shù)字所對應(yīng)的字符數(shù)順序的,沒有間隔,因此'0' + 5和'5'的值是相同的,等等。盡管這個假設(shè)對于ASCII和EBCDIC字符集是成立的,但對于其他一些機器可能不成立。避免這個問題的方法是使用一個表: void printnum(long n, void(*p)()){ if(n < 0){(*p)('-');n =-n;} if(n >= 10)printnum(n / 10, p);(*p)(“0123456789”[n % 10]);} 另一個問題發(fā)生在當(dāng)n < 0時。這時程序會打印一個負號并將n設(shè)置為-n。這個賦值會發(fā)生溢出,因為在使用2的補碼的機器上通常能夠表示的負數(shù)比正數(shù)要多。例如,一個(長)整數(shù)有k位和一個附加位表示符號,則-2k可以表示而2k卻不能。解決這一問題有很多方法。最直觀的一種是將n賦給一個unsigned long值。然而,一些C便一起可能沒有實現(xiàn)unsigned long,因此我們來看看沒有它怎么辦。在第一個實現(xiàn)和第二個實現(xiàn)的機器上,改變一個正整數(shù)的符號保證不會發(fā)生溢出。問題僅出在改變一個負數(shù)的符號時。因此,我們可以通過避免將n變?yōu)檎龜?shù)來避免這個問題。當(dāng)然,一旦我們打印了負數(shù)的符號,我們就能夠?qū)⒇摂?shù)和正數(shù)視為是一樣的。下面的方法就強制在打印符號之后n為負數(shù),并且用負數(shù)值完成我們所有的算法。如果我們這么做,我們就必須保證程序中打印符號的部分只執(zhí)行一次;一個簡單的方法是將這個程序劃分為兩個函數(shù): void printnum(long n, void(*p)()){ if(n < 0){(*p)('-');printneg(n, p);} else printneg(-n, p);} void printneg(long n, void(*p)()){ if(n <=-10)printneg(n / 10, p);(*p)(“0123456789”[-(n % 10)]);} printnum()現(xiàn)在只檢查要打印的數(shù)是否為負數(shù);如果是的話則打印一個符號。否則,它以n的負絕對值來調(diào)用printneg()。我們同時改變了printneg()的函數(shù)體來適應(yīng)n永遠是負數(shù)或零這一事實。我們得到什么?我們使用n / 10和n % 10來獲取n的前導(dǎo)數(shù)字和結(jié)尾數(shù)字(經(jīng)過適當(dāng)?shù)姆栕儞Q)。調(diào)用整數(shù)除法的行為在其中一個操作數(shù)為負的時候是實現(xiàn)相關(guān)的。因此,n % 10有可能是正的!這時,-(n % 10)是正數(shù),將會超出我們的數(shù)字字符數(shù)組的末尾。為了解決這一問題,我們建立兩個臨時變量來存放商和余數(shù)。作完除法后,我們檢查余數(shù)是否在正確的范圍內(nèi),如果不是的話則調(diào)整這兩個變量。printnum()沒有改變,因此我們只列出printneg(): void printneg(long n, void(*p)()){ long q;int r;if(r > 0){ r-= 10;q++;} if(n <=-10){ printneg(q, p);}(*p)(“0123456789”[-r]);} 8 這里是空閑空間 還有很多可能讓C程序員誤入迷途的地方本文沒有提到。如果你發(fā)現(xiàn)了,請聯(lián)系作者。在以后的版本中它會被包含進來,并添加一個表示感謝的腳注。參考 《The C Programming Language》(Kernighan and Ritchie, Prentice-Hall 1978)是最具權(quán)威的C著作。它包含了一個優(yōu)秀的教程,面向那些熟悉其他高級語言程序設(shè)計的人,和一個參考手冊,簡潔地描述了整個語言。盡管自1978年以來這門語言發(fā)生了不少變化,這本書對于很多主題來說仍然是個定論。這本書同時還包含了本文中多次提到的“C語言參考手冊”?!禩he C Puzzle Book》(Feuer, Prentice-Hall, 1982)是一本少見的磨煉人們文法能力的書。這本書收集了很多謎題(和答案),它們的解決方法能夠測試讀者對于C語言精妙之處的知識。《C: A Referenct Manual》(Harbison and Steele, Prentice Hall 1984)是特意為實現(xiàn)者編寫的一本參考資料。其他人也會發(fā)現(xiàn)它是特別有用的——因為他能從中參考細節(jié)。------------------腳注 1.這本書是基于圖書《C Traps and Pitfalls》(Addison-Wesley, 1989, ISBN 0-201-17928-8)的一個擴充,有興趣的讀者可以讀一讀它。2.因為!=的結(jié)果不是1就是0。3.感謝Guy Harris為我指出這個問題。4.Dennis Ritchie和Steve Johnson同時向我指出了這個問題。5.感謝一位不知名的志愿者提出這個問題。6.感謝Richard Stevens指出了這個問題。7.一些C編譯器要求每個外部對象僅有一個定義,但可以有多個聲明。使用這樣的編譯器時,我們何以很容易地將一個聲明放到一個包含文件中,并將其定義放到其它地方。這意味著每個外部對象的類型將出現(xiàn)兩次,但這比出現(xiàn)多于兩次要好。8.分離函數(shù)參數(shù)用的逗號不是逗號運算符。例如在f(x, y)中,x和y的獲取順序是未定義的,但在g((x, y))中不是這樣的。其中g(shù)只有一個參數(shù)。它的值是通過對x進行求值、拋棄這個值、再對y進行求值來確定的。9.預(yù)處理器還可以很容易地組織這樣的顯式常量以能夠方便地找到它們。10.PDP-11和VAX-11是數(shù)組設(shè)備集團(DEC)的商標(biāo)。

第三篇:語言的功能和陷阱

n

語言的功能和陷阱

n

王蒙

n

一、焦點問題

?

思考語言的社會功能問題,以及由語言而引發(fā)的社會問題。

?

演講的基本特點和要求。

n

二、王蒙其人

王蒙,1934年生于北京

n

5歲上小學(xué)。

n

10歲時跳級考入中學(xué)。

n

1948年,14歲,王蒙參加地下黨。

n

1949年,15歲調(diào)入新民主主義青年團(后改名為共產(chǎn)主義青年團)北京市委工作。

n

1953年,19歲,長篇小說《青春萬歲》獲得成功。

n

1956年,參加全國第一屆青年作者會議。

n

1956年秋,發(fā)表《組織部來了個年輕人》,引起極大反響。

n

1958年,24歲,被錯劃為右派。

n

1958年,赴北京郊區(qū)勞動。

n

1962年,赴新疆勞動。

n

1963年起在伊犁地區(qū)農(nóng)村勞動多年。

n

n

三、王蒙成就

n

其間曾任自治區(qū)文聯(lián)編輯、維吾爾語翻譯。1979年調(diào)回北京,任北京市文聯(lián)專業(yè)作家。中國作家協(xié)會副主席。

n

自20世紀(jì)50年代以來,發(fā)表作品共一千余萬字。

n

被翻譯成英、法、德、俄、日、韓、意、西班牙、等二十余種語言文字。

n

曾獲意大利蒙德羅文學(xué)獎、日本創(chuàng)作學(xué)會和平與文化獎。

n

學(xué)術(shù)著作《〈紅樓夢〉啟示錄》。

n

擔(dān)任十余所大學(xué)教授、名譽教授、顧問。

n

曾應(yīng)邀訪問世界各大洲四十多個國家。曾任哈佛大學(xué)燕京學(xué)院特邀訪問學(xué)者、美國三一學(xué)院校長級學(xué)者

n

三、文本分析

n

為什么關(guān)注語言?

“語言是存在的揭示、澄明、到達”

“語言是存在的家,人就居住在這家中”。

——海德格爾

王蒙這篇演講的前一部分主要講述語言、尤其是文學(xué)語言的基本功能。

?

(一)王蒙提出語言的三種功能:

?

現(xiàn)實有用的功能;

?

生發(fā)和促進的功能(推進思想、推進感情、推進文化、創(chuàng)造文化);

?

浪漫的功能(語言和文字離開了現(xiàn)實或者超出了現(xiàn)實的功能)。

?

(二)如何理解王蒙的觀點:

?

語言創(chuàng)造了人:(反對語言工具論)提倡語言本體論,如“皎潔”。

?

沒有語言就沒有記憶:(反對語言交際論)提倡語言文化論,如“我們倆困覺”。

?

語言的審美化:(反對語言反映論)提倡語言形象論,如“吃葡萄”。

?

(三)王蒙這篇演講的后一部分主要講述語言的陷阱。

語言的陷阱:

?

語言和現(xiàn)實和你的思想感情脫節(jié);

?

脫離生活,變成反面的東西;

?

異化、狗屎化效應(yīng)、被語言文字主宰,扼殺創(chuàng)造性,扼殺活潑的生機。

n

四、怎樣認識語言的功能和陷阱

?

(1)語言決定、生產(chǎn)意義。

?

(2)語言是思想的物質(zhì)現(xiàn)實:維特根斯坦說,我的語言的局限就是我的世界的局限。例如,現(xiàn)代“時間”是一種空間化的隱喻,“自……以來”;但是,在美國印第安的霍皮族那里,沒有昨天、今天、明天的概念。同樣,不同的語言體系生產(chǎn)出不同的“宇宙”,也就有了不同的生命觀、宇宙觀和哲學(xué)。

?

(3)語言限定體驗:語言在瞬間體驗中起著決定性的作用,歷史性的養(yǎng)成人們感受的習(xí)性,如明月、流水;也橫向地限定了人們的體驗?zāi)芰头绞?,如月色、五味?/p>

?

(4)語言本身也可以是美的形象。

?

(5)語言可以“修改”現(xiàn)實:王蒙是一個經(jīng)歷了“反右”和十年“文化大革命”的作家,特殊的經(jīng)歷和遭遇使他對“語言”的負面功能有著特殊的認識。他說,“語言文字可以反過來主宰我們,扼殺我們的創(chuàng)造性,扼殺我們活潑的生機”。

n

五、“概念恐懼”與語言的權(quán)利

存在主義哲學(xué)家基爾克郭爾提出“概念恐懼”認為,“恐懼”和“畏懼”不同,前者是對沒有具體對象的恐懼。在十年“文化大革命”中,很多語言就是這樣造成一種“恐懼”,這些語言并沒有創(chuàng)造“實體”,比如“地富反壞右”、“牛鬼蛇神”等?!耙粋€青年在街上走”,這說明了語言本身隱藏著權(quán)力,影響我們的認識和思考。事實上,有“語言”的地方,就存在著權(quán)力的妥協(xié)、對立和斗爭,就存在著“扼殺”和對“扼殺”的反抗。

n

六、再談?wù)勚v演

講演稿也叫演說詞,是在較隆重的集會和會議上發(fā)表的講話文稿??梢杂脕斫涣魉枷?、感情,表達主張、見解,具有宣傳、鼓動和教育作用。

演講是一種溝通。在古代希臘,演講被稱之為“誘動術(shù)”。這包含了三個意思:

(1)廣場性:利用話語修辭,調(diào)動公眾情緒的相互感染;

(2)單向性:含有表演性質(zhì)的獨白話語行為;

(3)

共謀性:演講是一種修辭性的“共謀”策略的實施。

七、王蒙講演的風(fēng)格:

?

外松內(nèi)緊

?

亦莊亦諧

?

取譬引喻

n

八、思考與討論

l

找出表現(xiàn)演講者機智的句子。

l

怎樣區(qū)分演講中的幽默與噱頭?

l

舉例說明演講者是怎樣不斷調(diào)動、活躍場內(nèi)氣氛的。

l

你同意演講者關(guān)于“語言陷阱”的觀點嗎?為什么?

第四篇:王蒙《語言的功能和陷阱》(模版)

王蒙《語言的功能與陷阱》

語言對人來說是太重要了,可以說是人與非人之間的一個非常重大的區(qū)別,當(dāng)然我們首先倡導(dǎo)的學(xué)說是勞動創(chuàng)造了人,但是從一定意義上也可以說語言和勞動一起創(chuàng)造了人,人創(chuàng)造了語言,語言反過來又創(chuàng)造了人。人性化離不開語言。所以,我仍然假定,發(fā)達的語言,尤其是文字是人與非人,與其他動物,更不用說是植物和礦物的一個重大的區(qū)別。

語言的功能

第一,語言的人性化。之前我曾說,有三個詞我老是煩它,一個是:芝麻開花節(jié)節(jié)高,這個我覺得也太俗了。你說你情況越來越好就是好就完了,說進步就是進步,提升就是提升了,富裕了就是富裕了,你干嗎還芝麻開花節(jié)節(jié)高呀?芝麻開花節(jié)節(jié)高能高到什么程度,這個我不喜歡;另外一個詞就是我們的一個固定說法,形容房屋很多,叫鱗次櫛比。我說你形容房子多無論如何不能用魚鱗來形容,用魚鱗來形容我有一種生理上的不舒服的感覺;第三個我不喜歡的詞就是天麻麻亮,天亮了就是亮了,沒亮就是沒亮,就是用書面語來說是拂曉也行,用破曉也可以,為什么要麻,而且一連麻兩次呢?我就說老實話,說東方顯出了魚肚白,這個魚肚白我也不喜歡,因為它讓我想到的是死魚。

這些都沒有什么道理,都不是語言學(xué),我只是說語言里面包含著一些很人性的東西。我喜歡什么樣的詞語呢?我隨便給你們舉幾個例子,我特別喜歡?你好?,而且我認為這是受了蘇聯(lián)的影響,它完全是受俄語的影響。甚至于俄語里面有更好的表示,它說:?你好,爸爸?,?你好,媽媽?,?你好,列寧同志。?哎呀,我覺得這個詞好;我喜歡?再見?,我喜歡說?我想你?,我覺得說?我想你?甚至比說?我愛你?還好,我還喜歡?我們都老了?,這話特別有感情,說這話的人一定是老相識,很可能是老情人,見到一個老情人你說什么呢??啊,我們都老了!?我覺得這個特別的人性化。我還喜歡一個一個書面的說法,一個小說里的、話劇里的語言,這樣說有點酸,但是也沒有辦法,這就是語言的陷阱。一個本來很好的語言呢,場合不對它說出來就顯得有點酸。說有個人呢,他原來跟我很好,感情很好,但是后來呢他離開了我,在我最生活困難的時候離開了我。那么我會說什么呢?我會說?你曾經(jīng)轉(zhuǎn)過臉去了?,當(dāng)我說?你曾經(jīng)轉(zhuǎn)過臉去了?的時候,眼淚已經(jīng)在我的眼睛里逛蕩了,我覺得這句話太好了。

在這里我加一個文言文,我讀文言文作品的時候,我不知道為什么我那么喜歡一句話,那就是?先生別來無恙乎??就沖這句話,我就覺得中國的文言文寫的是太棒了。?別來無恙?現(xiàn)在白話文里也可以考慮,完全可以說,這個你不可以按文字來講。?先生別來無恙乎??這個英語里它沒法翻譯,英語變成什么呢??Are you Ok??它沒有那種感情。?先生別來無恙乎??,說明這兩個人已經(jīng)離別很久了,而用?無恙?,表達一種人世的滄桑的態(tài)度;還有一句就是?誰知道呢??,?誰知道呢??尤其是我讀肖洛霍夫的《被開墾的處女地》第二部,它的那個主人公拉古爾洛夫是一個貧農(nóng)團的團長,一個天天搞革命的農(nóng)民,但是他跟一個富農(nóng)出身的,一個很不檢點的女人,兩個人有點難分難解,然后這個女人是更喜歡一個富農(nóng),而那個富農(nóng)呢,實際上是已經(jīng)作了一些破壞集體農(nóng)莊的反革命事情,但是他最后決定把這個他心愛的政治上犯有錯誤的,而且牽扯著刑事案件的女人放掉。最后他說這個時刻,這個富農(nóng)女人對這個很粗魯,很野蠻的,也不會穿衣服,說話大大咧咧,也不懂女性心理的拉古爾洛夫的感覺,也許有一些不同吧?誰知道呢?我覺得這句很棒!同樣反過來呢,我對英文的這個?Who knows??也有興趣?Who knows??他回答得很瀟灑,有時候需要互相發(fā)現(xiàn),有的時候北京人回答一件事的時候說?沒戲?,這個我們聽起來很貧哪!是胡同里我們才這么說,但是一個英國人他告訴我,說這個北京人的文化積淀太深了!當(dāng)他們說一件事辦不成的時候,他們不說?impossible?,他們說?No theater?,所以他們對?沒戲?的感覺跟我對?Who knows??的感覺是相同的。

我的英語知識很少,但是我有多次和外國人一起生活的經(jīng)歷,我比較喜歡他們的?Why not ??。?Why not ??你沒法翻(譯)。你比如說他說,老王,今晚上我那有個party ,有一個雞尾酒會,甚至?xí)袀€自助餐,你晚上來嗎?我怎么回答呢?我說:?Ok, I will go!?,這太干巴巴了,所以我一定回答:?Yes, Why not ??但你不能翻,一翻就酸得你的牙都掉了,英語有些詞,在我有限的接觸當(dāng)中,也有些我非常喜愛的說法。比如說甭管它,愛怎么怎么著,?Let it be!?這怎么翻呀?愛怎么怎么著,這還湊合。女作家劉索拉把它翻譯成?隨他媽的去!?這就過啦,這個語感就過啦,它不準(zhǔn)。所以語感這個東西它是很精微的,添一份它就肥了,減一份它就瘦了。我也喜歡?So do I.?,它反過來說,它不說?I do so?,它就比較俏皮,你聽著就比較舒服。我就是說這個人性化的問題呢,它跟語言有時候聯(lián)接的比較緊密。所以這個語言的表達的功能不像我們想得那么簡單,語言的表達,它表現(xiàn)著人性。

第二,語言的記憶功能。世界上一切的東西他都是變動不息的,所謂?俯仰之間,已成陳跡?。其實我們在這說著話,說著說著話這一下午就這么過去了。2004年4月14日,這一下午就這么過去了,有一個姓王的老頭子,在這里說的這些話,音波也就在空氣中消散了,就變成陳跡了,就變成了歷史了。變成歷史之后有什么東西留下來了呢?有一些物質(zhì)的,你比如說房屋、用具,如果你真要追究起來的話,比如說這有一只王蒙用過的茶杯,但主要的是文本留下來了,實際上許許多多東西對我們來說是文本,歷史教科書實際上有很多東西是文本。有實物又有文本的東西是給我們留下印象最深的。有文本沒有實物的東西給我們留下的印象是次深的,有實物沒有文本的東西是啞巴東西,你怎么解釋都行,假如五百年后有人對這只茶杯感興趣的話,假如這個茶杯上沒有寫中國海洋大學(xué),它是啞巴。我們講中國史、外國史,實際上他們最后都變成了語言,變成了文本。而很多東西之所以能夠有很高的價值,是由于文本的可愛,而不是由于別的。你比如說,我們的祖國有許許多多的旅游點,有很多美好的地方,有文本的跟沒有文本的給人的感覺是不一樣的。比如西湖,古來吟詠西湖的詩就特別多,包括白娘子的故事,包括秋瑾的詩,對我們來說都是文本!白素貞永鎮(zhèn)雷鋒塔,有一次我們跟韓國人一起開會,正好趕上這種開會的天氣,天天有雨,有很多活動就取消了,韓國人就搶著背誦:?欲把西湖比西子,濃妝淡抹總相宜?,?水光瀲滟晴方好,山色空濛雨亦奇?。韓國人就搶著背,有了這個文本之后啊,就使我們中國的東道主對連續(xù)三天不能好好的暢游西湖啊,我們有了底氣,有文本為證:?雨亦奇?,?山色空濛?,?濃妝淡抹總相宜?。這次我們就是用淡妝來歡迎韓國朋友,有了這個,你的底氣都不一樣啊。岳陽樓,現(xiàn)在你都分不清楚是為了看樓還是為了復(fù)習(xí)范仲淹的《岳陽樓記》,特別是它的?先天下之憂而憂,后天下之樂而樂?。尤其是黃鶴樓,黃鶴樓的遺跡實際上早就不存在了,尤其解放以后,蘇聯(lián)專家?guī)椭蹅冃蘖碎L江大橋,這一帶的地理環(huán)境其實都已經(jīng)改變了,現(xiàn)在的黃鶴樓是又選了一個址,現(xiàn)在的黃鶴樓修了有20多年了,當(dāng)時我們的國家也比較困難,標(biāo)準(zhǔn)也不是很高,怎么不高呢?就是那個木頭柱子,它全部是洋灰,全部是鋼筋水泥,然后再刷上紅漆,但是它仍然吸引了千千萬萬的游客,絡(luò)繹不絕。雖然現(xiàn)在的黃鶴樓是偽黃鶴樓,但是關(guān)于黃鶴樓的崔顥和李白的詩卻流傳至今,?昔人已乘黃鶴去,此地空余黃鶴樓。黃鶴一去不復(fù)返,白云千載空悠悠。晴川歷歷漢陽樹,芳草萋萋鸚鵡洲。日暮鄉(xiāng)關(guān)何處是,煙波江上使人愁。?李白的詩呢,簡單一點:?故人西辭黃鶴樓,煙花三月下?lián)P州。孤帆遠影碧空盡,惟見長江天際流。?這樣一個記憶的寶庫,這樣一個文化心理的保存,使黃鶴樓始終生長在我們的心里,那么你具體蓋起這個黃鶴樓比如油漆的質(zhì)量怎么樣,偽木柱子的成色如何,我們可以寬容,我們可以原諒,為什么呢?因為黃鶴樓的仙氣、靈氣在它的文本上,在它的語言上,在李白和崔顥上,所以只要有李白有崔顥,黃鶴樓永垂不朽。只要有我們的漢語、漢字的文本,中華民族永垂不朽!滕王閣也是這樣,滕王閣修得很精致,它的自然條件不如黃鶴樓,因為南昌那邊滕王閣前面是贛江,就是?秋水共長天一色?里的,沒有長江的那種氣魄。滕王閣是根據(jù)梁思成先生當(dāng)年畫的,也就是他考察、推測的圖把它修起來的,可是這個建筑的依托我覺得也是王勃的《滕王閣序》,沒有他的物華天寶,人杰地靈,沒有他的?落霞與孤鶩齊飛,秋水共長天一色?,這個滕王閣不會這樣的屹立,這樣的崇高,這樣吸引著我們這些中華兒女。這是不得了的,一個有文本的民族和一個沒有文本的民族在世界在人類中的地位是不一樣的,一個有無數(shù)優(yōu)秀文本的民族必勝,很難代替。所以我們就看到語言文字的記載所起到的記憶和文化積淀的作用,一個沒有文化的積淀的民族就好比一個失去了記憶力的人,每個人都有自己的悲哀,而失去了記憶力實在是一個非常大的悲哀,失去了記憶力你連自個兒是誰都不知道,我們還怎么幫助你?你還能有什么幸??裳??還能有什么不幸可言?

第三,語言是一種修辭的手段。這種修辭是泛修辭,我覺得我們的文化在某種意義上說就是修辭,人都有動物的本能,都有求生的本能,而人的這種本能經(jīng)過修辭以后,就如毛澤東所說的有?文野之分?,有了文化與非文化之分,如果沒有修辭手段,你就只能限于本能,這個我想到最多的是阿Q先生,阿Q他是按照人權(quán)的觀點他當(dāng)然和我們都一樣,他有兩次求愛經(jīng)歷,一次是求吳媽,用他的話說就是?小孤孀?吳媽,那么他怎么求愛呢?他突然一天晚上就給吳媽跪下了,然后他說:?吳媽!我要和你困覺!?哎呀,然后呢,吳媽就哭,要抹脖子上吊,然后大家就都認為阿Q干出了毫無人性、違反道德、不守規(guī)矩、欺天害理、不齒于人類的這種事情,阿Q沒有寫檢討因為他不識字,但是他表示了檢討之意,而且還賠了錢,把一年的工錢都給了吳媽,而吳媽卻一直在那里哭,哭,哭。如果阿Q在語言文字的修辭上能夠到咱們中文系上兩節(jié)課,能來這聽講座,他絕對就不會用這種話了!如果他讀過徐志摩的詩呢?那么他見到吳媽就會說:?我是天空里的一片云,偶爾投影在你的波心,你不必訝異更無須歡喜,在轉(zhuǎn)瞬間消滅了蹤影。你我相遇在黑夜的海上,你有你的,我有我的方向……?嘿,他可能就成功了!阿Q和徐志摩作為男人,他們想擇偶,想求愛,這是天經(jīng)地義的,阿Q對吳媽連性騷擾也談不上,因為他是跪下的,他又沒有摸人家,也沒怎么著人家。性騷擾是阿Q對小尼姑,他去摸人家的腦袋,而且最為惡劣的是他說:?和尚摸得為什么我摸不得??他缺乏修辭。修辭能使很多事情甚至于發(fā)生本質(zhì)的變化,從野蠻到文化,從野獸到文明的人,可以有很大的變化,不過我們往寬里說的話,很多事情就是一種修辭,我們可以說它是生活的修辭,比如說婚姻,該有的這些儀式、程序,互相還寫點情書,然后你再進教堂,還要奏結(jié)婚進行曲,非常雄壯,我聽這聲音一直以為是大軍出營征戰(zhàn)。經(jīng)過這樣一個修辭的手段他就不一樣,所以有時候我們從一個人的語言上,所謂的談吐上就能看出這個人素質(zhì)是高還是低,對人是粗暴的還是溫和的這樣一些印象。所以說語言的修辭作用是太大了,為什么我說語言塑造了人呢?這是因為人的許多一些本能的欲望,這些欲望既談不上壞、罪惡,也談不上好、文明,經(jīng)過修辭的作用,經(jīng)過這種長期的修辭的實踐,修辭的習(xí)慣,修辭的素養(yǎng),他變成了一個有教養(yǎng)的,有文化的人。

第四,語言的政治功能。語言在政治當(dāng)中起到相當(dāng)作用,政治家基本上或是按照一般來說他應(yīng)該是個演說家,比如說用語言來選舉,用語言來起鼓動的作用?!段男牡颀垺芬婚_始就說?鼓天下之動者存乎辭?,能發(fā)動天下的是?辭?,以?辭?來打敗政治上強有力的對手。?起來!饑寒交迫的奴隸?,這個語言的力量太大啦!雷霆萬鈞,如火如荼啊!就沖這一句話就該熱血沸騰了。古代?吾與汝偕亡?我沒有能力把你消滅,怎么辦呢?我跟你一塊兒死!還有?人生自古誰無死,留取丹心照汗青?,而且?留取丹心照汗青?這個說法不僅中國有,菲律賓有一個反抗西班牙統(tǒng)治的民族英雄,他又是一個詩人,他在就義前寫過一首詩,這首詩里有一句話說?我流出的血將染紅明天的朝霞?,這就是?留取丹心照汗青?;革命烈士夏明翰的詩?砍頭不要緊,只要主義真;殺了夏明翰,還有后來人?。有一些話我覺得提得真精彩,比如說《共產(chǎn)黨宣言》講社會主義、無產(chǎn)階級革命說是?無產(chǎn)階級在這場革命中失去的是(只有)鎖鏈,而得到的(將)是全(整個)世界。?哎呀,這種語言力透紙背,千鈞之力啊!還有他最后有一句話說:?全世界無產(chǎn)者聯(lián)合起來!?這個是中文的翻譯更悲壯,?全世界無產(chǎn)者聯(lián)合起來!?他原來德語的原文我不會,但是英語我見過,因為我去過馬克思墓,那上邊寫的太簡單了,就是說?All workers unite!?很簡單,?所有的工人要聯(lián)合?。我還想到一些很有名的詞兒,比如說,你們這個年齡的人肯定也知道季米特洛夫,他是共產(chǎn)國際的一個活動家,是保加利亞的一個領(lǐng)導(dǎo)人,這個季米特洛夫曾經(jīng)被希特勒的國會縱火案所誣陷,不知道怎么有個瘋子一把火把德國國會給燒了,于是法西斯集團就借這個機會鎮(zhèn)壓所有的左翼政黨和工人團體,逮捕了季米特洛夫,季米特洛夫在被審判當(dāng)中他不要律師,自我辯護,把敵人的審判變成了他的講臺,在那里慷慨激昂地講話,所以他真是當(dāng)時那個時候共產(chǎn)主義的一個明星,他一句話說?當(dāng)中世紀(jì)的教會把主張地動說的科學(xué)家燒死的時候,這個科學(xué)家說:‘你可以燒死我,但是它在轉(zhuǎn)動著!’?它在轉(zhuǎn)動著!多棒??!還有他說?無產(chǎn)階級和資產(chǎn)階級決戰(zhàn)的時刻到來了!到時候我們不做鐵錘便做鐵砧?,也就是你不砸他他就砸你,當(dāng)然從現(xiàn)在來看對這個事情還是應(yīng)該一分為二,他沒有充分考慮事物的復(fù)雜性,但是從語言來說它是非常有力的,這是政治上的一種情況。還有一種情況就是這種語言和文字又會變成政治家的一種武器,變成了政治家的一種障眼法,他用來遮蔽事實的真相,用來推遲真相的出場。我們文化大革命中有些人宣傳得非常好,而且大家都說這人講得真好,但是那些話是經(jīng)不住研究,是經(jīng)不住推敲的。我這里就不說具體是誰說的了,這人介紹經(jīng)驗的時候說,我們這兒對待自然災(zāi)害,我們的經(jīng)驗有三條,第一,要承認它,第二,不怕它,第三,克服它!講得鏗鏘有力,而且還有第一第二第三,但是他講得全是廢話,和沒講一模一樣??!什么叫第一承認?你承認不承認山水下來把你房子都已經(jīng)沖走了你還不承認?就是有時候他講些同義反復(fù)的東西,比如說有些領(lǐng)導(dǎo)人講話,說什么叫全心全意為人民服務(wù)呢?第一,不是三心二意,第二,不是半心半意!這叫什么?這叫同義反復(fù),你說來說去實際上一句話都沒有說。前幾個月外國媒體評文理不通獎評給拉姆斯菲爾德了,這個拉姆斯菲爾德后來在鳳凰臺上就多次播放他這個講話:?I don’t know what I know or I don’t know, If I think I know I’m not sure I know or I don’t know。?就是當(dāng)時人們問他關(guān)于伊拉克形勢啊,大規(guī)模殺傷性武器追究這些事情的時候,他就說我們并不知道我們知道什么,我們也不知道我們不知道什么,我們以為我們知道什么,但是不見得就是我們知道什么。這就是拉姆斯菲爾德說的。有時候搞政治真是練語言啊,我在香港看過老布什競選的一個新聞片,老布什他講一個問題,就是要不要加稅,他就保證我絕對不加稅,他用手指著嘴(動作),說:?Pay attention to my mouth!I say,NO,NO,NO!?他說請你們注意我的嘴,注意我的口型,我說了不加稅,?NO NO NO?,我三次說了不加稅,然后他上臺三個月后就加稅了,然后他又講,我當(dāng)時說不加稅有當(dāng)時的情況,現(xiàn)在說加稅有現(xiàn)在的情況,情況變了提法自然會有改變!這個語言文字真好啊,沒有語言文字怎么辦?怎么解釋這些事情?中國是很注意外交辭令的,我小學(xué)的時候就學(xué)過晏子使楚,晏子使楚這一類的很多故事那不就是靠語言嗎?他就顯示出了語言的魅力,最后那個楚王就說:?寡人自取辱焉?,就是等于他承認敗在晏子手下。還有說客,《史記》上就有很多說客,說客就靠自己的三寸不爛之舌改變形勢。有時候我開玩笑,其實我的話并沒有惡意,只是聽起來可能有貶義,我發(fā)現(xiàn)好多人包括我自己,我說我們到底算什么勞動者?算體力勞動者?不算!算腦力勞動者?不能一天老用腦子,很累的!后來我歸結(jié)為?口力勞動者?,很多人都是口力勞動者,靠口力勞動改變、調(diào)整著政策,政策的調(diào)整那要靠口力,包括文字也是把口說的東西記載下來,所以我就說這些,政治化的東西我就不多說了,?天機不可泄露?。

第五,語言的審美功能。語言產(chǎn)生之后可以成為審美的對象,其實剛才我講那些我喜歡的詞的時候,已經(jīng)包含了對它的一種審美的感受,一種審美的接受,因為你不能單純的說哪個詞一定好,哪個詞一定不好,褒義詞都是好詞,貶義詞都是壞詞。語言和文字尤其是像中國的文字有這樣巨大的審美功能也是很少有,它的音樂感,我讀文學(xué)作品包括我自己寫東西,我特別追求的就是這種音樂感。好的句子你怎么看怎么舒服,怎么念怎么舒服,別人念你怎么聽怎么舒服,而不好的句子他怎么著都別扭。其實拉姆斯菲爾德獲得文理不通獎的那一段講話從單純的審美意義來說他有一種喜劇的樂趣,用英語來評價的話?He is funny?,我們也可以說是?He is fantastic?,他是奇妙的,而且是喜人的,這是從單純的審美意義來說。我有時候喜歡一些不上經(jīng)傳的作品,我就是覺得它的發(fā)聲好聽,內(nèi)容當(dāng)然可以,不算深刻,也不算感人,也沒有包含巨大的道德的、社會的或是人生的內(nèi)涵,但它的聲音實在是太好聽。比如說蘇軾的,?休對故人思故國,且將新火試新茶。詩酒趁年華。?怎么那么?帥?啊這詩?太帥了!?休對故人思故國,且將新火試新茶。詩酒趁年華?,唱歌你就唱不到這么好,再比如說?細雨夢回雞塞遠,小樓吹徹玉笙寒。?這聲音真是太好聽了。你就單純從它的審美的角度,不去管它的內(nèi)容,而且甚至與那些從內(nèi)容上來說值得推敲的,不一定很積極,不一定很健康,也不一定同情勞動人民,也不一定代表先進的生產(chǎn)力和先進的文化和人民的最大利益。但是這些詞藻,這些詞句,這些文字加在一起給你美感。我為什么喜歡李商隱呢?李商隱他能夠把他那種消極的、悲哀的、乃至于頹喪的情緒審美化。李商隱他能夠用特別美的文字,用特別美的語言,甚至用富貴的詞匯,比如說金玉、蝴蝶、花卉等等,用這些東西他來把頹喪的情緒加以包裝,實際上頹喪的情緒變成一種曲折有致、美不勝收的這么一座宮殿。譬如說我最喜歡他的兩句,這是最頹喪的,?紅樓隔雨相望冷,珠箔飄燈獨自歸。?哎呀,這太悲哀啦!?紅樓隔雨相望冷?,他是不溝通的,他是隔膜的,?相望冷?,他是冷雨,?紅樓隔雨相望冷?,你覺得這個冷啊,讀到這里就有點兒冷到骨頭里去了。?珠箔飄燈獨自歸?,這么美!他為什么這么美?。考t樓在雨里,他是紅樓啊,不是土樓,也不是地堡,?地堡隔雨相望冷?,這就不行了,?珠箔飄燈獨自歸?,他這不是紙燈籠,也不是拿著一根蠟,更不是拿著手電筒,?手持電筒獨自歸?,那就完啦!甚至于這種悲哀的情緒,消極的情緒,失望的情緒,軟弱的情緒,所謂?一春夢雨常飄瓦,盡日靈風(fēng)不滿旗?,這雨都是飄著的,飄到瓦上,不是落到瓦上,雨沒有重量,又有風(fēng),風(fēng)也很小,沒有大風(fēng),大風(fēng)干脆呼嚕呼嚕呼嚕吹一家伙也可以,他連旗子都不能吹滿了,不能吹滿旗子迎風(fēng)飄揚,所以這個語言吶他變成一種審美的呼喚。新詩里頭舒婷有兩句話,實在是寫得非常具有審美的價值,這詩我記得不是很清楚了,她說?也許有過一次呼喚,卻永遠沒有應(yīng)許;也許有過一次約定,卻永遠沒有如期。?Beautiful!太棒了!這就是一種語言的審美化。一念這個我總有一種惡作劇的心理,我就想起我上小學(xué)的時候,男生三四年級的時候就開始發(fā)壞,我們一個同學(xué)就學(xué)著唐山味兒教我一句話,?我說老妹子?。ㄟB讀),你咋不愛(發(fā)后舌音)我呢(nin音)??這就缺少審美價值,顯得粗鄙不堪,至少他這是頑童語言,沒有什么特別惡劣的,可是如果一個同學(xué)在自己私人的感情生活上不太成功,不太順利,那么你就背誦一下?也許有過一次呼喚,卻永遠沒有應(yīng)許?,?也許有過一次約定?,當(dāng)然也許連約定都沒有,?卻永遠沒有如期?,是不是你自個兒也顯得雅一點兒了?也不必鬧得太大發(fā)了,也不必太出丑了,語言他就起了這么個作用。有時候語言會變成詩,有時候會變成名句,叫做?千古麗句?,有時候變成奇文,有時候變成戲劇的臺詞。我看曹禺的《雷雨》,我就特別喜歡其中那些最普通的話,特別是《雷雨》的第二場,就是侍萍和周樸園見到了,侍萍說了:?那已經(jīng)是幾十年以前的事了,那時候還沒有用洋火。?洋火就是火柴,我不知道她是哪一根心弦給我撥動了,她這個?沒有用洋火?就引起我本身那么多滄桑感,因為我經(jīng)歷過,小時候常說?買包洋火去吧?,它叫洋火,不叫火柴,叫火柴已經(jīng)很先進了,現(xiàn)在火柴都很少用了,因為有了其他的取火的方式,也很難買得到了。然后還有一句話說:?侍萍老嘍,老的連老爺都認不出來了……?何等的悲涼啊!這很有一種味兒,和那個?逝者如斯夫,不舍晝夜?本質(zhì)是一樣的,但是他又是另一種悲涼。所以我們覺得語言文字它不但是有聲音,而且是有表情的,它是有動感的,它是有形象的,它是有色彩的,它處處都在感染著你,處處都在觸動著你。

第六,語言的神學(xué)效應(yīng)。語言不但有藝術(shù)的效應(yīng),有藝術(shù)的功能,有表意的功能,政治的功能,社會的功能,而且語言有強大的神學(xué)的功能。第一,很多神學(xué)的最根本的概念,它是語言的產(chǎn)物,是一種語言,譬如說終極,誰看見過終極?終極在哪里?你是活人就看不見終極,你看的只有那幾十年,我們假設(shè)您長壽,您能活一百五十年,那一百五十一年您都看不到,更不用說終極了。譬如說永恒,你上哪去找永恒?但是永恒是一個詞,是一個非常好的詞,是一個非常有神學(xué)功能的詞。本源,至高無上,造物,命運,無限,我們看到的都是有限的,無限是我們思想的產(chǎn)物,因為有這個語言,你的思想才有所豐富。輪回,末日,如果沒有這樣的一些語言,怎么可能有宗教?怎么可能有人的這種神學(xué)的追求和研究,當(dāng)然還有更嚴肅的,上帝啊,佛啊,真主啊這樣一些詞,我們中國除了老天爺,灶王爺,玉皇大帝這些詞以外,我們還有一些?準(zhǔn)?或是?亞?神學(xué)的一些詞,就是說哲學(xué)的,具有無限涵蓋力的詞。譬如說?道?,譬如說?無?,其實我想來想去這個?無?也是看不見摸不著的,你看到的摸到的都是?有?而不是?無?,但是看不見的摸不著的東西,就是說你經(jīng)驗以外的東西,語言和文字可以創(chuàng)造。語言和文字不但有經(jīng)驗性,而且有?超驗性?和?先驗性?,超出你的經(jīng)驗。還有一些偉大的詞語,它們本身就具有一種神性,比如說正義,神圣,永生,就義,它有一種超越,對人生經(jīng)驗的一種超越,是對人生經(jīng)驗的一種升華。所以自古以來就有一種把語言神圣化尤其是把文字神圣化的這樣一種傾向,世界上各個民族都在尋找一種具有神性的語言。比如說我們所知道的芝麻開門的故事,芝麻開門這就是咒語,你到了一座寶山的緊鎖的石洞面前,你叫一聲芝麻開門,這門就開了,然后所有的珠寶歡迎你去摘取,去收獲,他們尋找這樣的語言。福,福祿,福祿其實也離不開字,但是它有一種很特殊的寫法,認為它有避邪的效應(yīng),過去北京有很多家里頭如果有小院子的話,他就立一塊石字碑,上邊寫著?泰山石敢當(dāng)?。就是因為他們認為有些語言文字是有神性的。還有一種魅力,就是有人認為語言文字本身它還有一種神秘,它的背后有一種神的意旨,這個意旨是文字本身所不能直接提供的,是要靠你去鉆研和體會,去探索的。中國有《推背圖》,所謂?河出圖,洛出書?,這里頭他是一個神秘的東西。要是細講起來語言和文字真是非常神秘,我相信語言是經(jīng)過千萬年才慢慢形成的,不可能一下子就這么完備,他在剛開始時候肯定有一點道理。比如我們說?牙齒?,說的時候這個牙齒的運動,牙齒的顯露是很明顯的,英語說tooth,teeth,他也有明顯的牙齒的動作。?舌頭?呢?說?舌?,這是對舌頭的一種使用,英語的tongue,又是一種對舌頭的表述。但是時間長了以后,你就只知其然不知其所以然了,而這時候他就神秘了,怎么會有這么多的話這么多的語言,怎么會是語言代表language?老覺得文本后還有天意,而這個天意是不可測的,所以就有測字,所以有《推背圖》,就要解釋。外國人喜歡搞這個,我看過一本書,叫做《圣經(jīng)密碼》,說他們確認《圣經(jīng)》是密碼,他們請了美國中央情報局的退休的密碼專家來研究,找出了它這個密碼的規(guī)律,根據(jù)這個規(guī)律,他們發(fā)現(xiàn)《圣經(jīng)》已經(jīng)預(yù)言了所有的事情,預(yù)言了蘇聯(lián)的解體,預(yù)言了巴以沖突,預(yù)言了伊拉克戰(zhàn)爭,凡是發(fā)生的事情,密碼都能找得出來。把語言把文字當(dāng)成一種密碼,然后尋找它背后的神秘,這個中國也不是沒有這種傳統(tǒng),中國認為語言文字具有神秘性,說倉頡造字的時候?天雨粟,鬼夜哭?,啊,因為太智慧了,這個智慧是超人間的,超經(jīng)驗的,所以連天都下小米,鬼夜哭,連鬼都害怕,中國人這么厲害啊,倉頡這么厲害啊,所以說語言有神秘性,有神學(xué)的功能。這種把它當(dāng)密碼的游戲表現(xiàn)在占卜上面,他希望通過對一些卦辭的解釋、解讀能找出對人生、命運的預(yù)言來。

第七,語言還有一種心理釋放和撫慰作用。語言和人的心理我覺得關(guān)系太大了。為什么呢?語言可以使你心里的郁結(jié)得到釋放。我讀過美國八十年代一個很有名的短篇小說家,現(xiàn)在已經(jīng)去世了,叫做John Chiver,他的女兒叫Susan Chiver,她寫過一本書回憶他的爸爸。她在這本書一開始的時候就說?在我小的時候遇到不愉快的事,我爸爸就讓我回到房間跪下來做一會兒祈禱。在我大了以后遇到不愉快的事,我爸爸就讓我把這一切都寫下來。在祈禱之后,寫下來之后,我就變得平安多了?,F(xiàn)在我遇到的最不愉快的事就是父親的逝世,我要把這一切寫下來。?這是很普通的,一個人需要說,需要傾訴,一個能夠傾訴的人他是幸福的,一個無人可以傾訴的人,他是可悲的。宗教信徒的懺悔,也起這么個作用。見到神父了,在一個很隱蔽的角落說:?神父啊,我是有罪的。我的鄰居評上高級職稱了,我沒評上,我真恨他呀,昨天我給他的狗投了毒了。?神父說:?啊,我的孩子,你已經(jīng)有了認識了,你快再買一條好狗送給你的鄰居吧。你這個罪不太大,上帝會饒恕你的。?然后摸摸他的腦袋,他就舒服啦!鬧不起神經(jīng)病來。有時候我覺得語言有一種釋毒的作用,他把那種非常負面的,非常消極的有可能成為毒素的,有可能引起癌變的那些經(jīng)驗,那些體驗,那些情緒,把它通過一定的語言文字的形式寫出來以后,就是經(jīng)過了這么一個無害化處理的過程。李商隱也是這樣,李商隱雖然消極,但是他的詩寫得漂亮,他要有很好的音韻,他要有很好的典故,他要有非常精確的對仗,他要有精美的文字的選擇,所有這些都是無害的,這是種無害化處理。我還常舉一個例子,比如《紅樓夢》,晴雯的死寶玉義憤填膺,但是寶玉義憤填膺他又不太可能在榮國府,在大觀園掀起一場抗暴,抗讒言,清理小人的這樣一場運動,連絕食他都沒有,寶玉怎么辦呢?他就寫了一篇《芙蓉女兒誄》,而且這個《芙蓉女兒誄》也是駢體,寫完之后自己在那里搖頭擺尾的念,林黛玉還聽見了,然后林黛玉就過來了,過來以后還跟他提一提說哪個字該怎么改一改,哪個詞該怎么改一改,這樣就把對晴雯死的憤激之情轉(zhuǎn)化為對?誄?這種文體的一種修辭學(xué)的討論,把它雅化了。不但雅化了,而且釋毒了,把它無害化了。所以這本身我就透露了一個消息,語言文字既很好也很厲害,也很冷酷,他把一個對人的死亡的正義的憤怒最后變成了文字的推敲。中國的戲曲和國外的戲曲比較起來,中國戲曲的大仁大孝,大忠大奸,大鑼大鼓相對來說比較強烈,什么原因呢?就是因為他的心里上的積蓄太多,壓抑太多,他需要語言的宣泄,而語言的宣泄無論如何比他上街宣泄要安全得的多。秦香蓮見皇姑,皇姑問?你為什么不跪??秦香蓮說:?按照國法,你是君我是臣,我應(yīng)該給你跪,按照家法,我是大你是小,你應(yīng)該給我跪?(原文記不太清),這個思想本身并不算很先進,但是在當(dāng)時來說她敢這么說話也還是出氣,別的時候不敢說就在戲里頭說一說嘛。通過這個東西她能夠維持一種心理的健康和平衡,這就是語言的心理的釋放乃至于釋毒的作用。

第八,語言是一種游戲。語言文字有很大的游戲性,它消遣,有很多純游戲性的語言游戲和文字游戲,我小時候,學(xué)了很多這種游戲性的東西,而且聽起來不太有道理的,沒有什么意思的這樣一些文字,但是它很有游戲性,比如說模擬的快板兒,說?打竹板兒,邁大步,眼前來到棺材鋪,棺材鋪的棺材真叫好,一頭大來一頭小,裝上活人跑不了,裝上死人活不了。?我覺得這是很天才的,沒有什么意義,也沒有攻擊棺材鋪的意思,也沒有為棺材做廣告的意思,這是一種游戲。還有很多例子,我的孩子小的時候那么多革命的好的童謠教給他,他不學(xué),他就會說什么呢??一個小孩兒學(xué)大字,學(xué),學(xué),學(xué)不了,了,了,了不起,起,起,起不來,來,來,來上學(xué),學(xué),學(xué),學(xué)文化,畫,畫,畫圖畫,圖,圖,圖書館,管,管,管不著,著,著,著火了,火,火,火車頭,頭,頭,打你一個大錛兒頭!?這就是游戲,它有什么意義?既不反動,也不革命,也不進步,語言文字有這種游戲功能,人多了一個玩兒的東西。動物吧,一只貓它可以玩兒個球,玩兒個線團線頭什么的,再沒別的玩兒了它可以

lěi玩兒自己的尾巴。但是人就比較幸福,人可以玩文字,可以玩語言,而且還有各種繞口令,像是?吃葡萄不吐葡萄皮兒,不吃葡萄倒吐葡萄皮兒?,這就是游戲,因為這話是不通的,你不吃葡萄怎么吐葡萄皮兒呢?后來我在德國,在波恩一個漢學(xué)家的家里,我找到了一個二十年代德國漢學(xué)家寫的《北京口語詞典》,其中有這個繞口令,那時候比較規(guī)矩,是?吃葡萄就吐葡萄皮兒,不吃葡萄不吐葡萄皮兒?,它是很合理的,但是游戲性不如這個,?不吃葡萄倒吐葡萄皮兒?,游戲性就增加了,是把它荒誕化了,這都是一些游戲。中國的游戲還多了,比如說回文詩,這詩從第一個字念可以,從第幾個字念也可以,它是循環(huán)的,是轉(zhuǎn)的。蘇小妹三難新郎,其中就有回文詩,所以極具游戲性。我上小學(xué)的時候的大家都喜歡唱岳飛的《滿江紅》,?怒發(fā)沖冠,憑闌處、瀟瀟雨歇?,可是我們的同學(xué)給改了,?來一碗粥,要咸菜不要窩頭!?那么這個同學(xué)是不是對岳飛有不敬呢?是不是有私通秦檜的嫌疑呢?沒有。真沒有。他就是覺得你老唱?怒發(fā)沖冠,憑欄處、瀟瀟雨歇?沒有人笑啊,改成?來一碗粥,要咸菜不要窩頭!?大家就一團樂了。解放以后我們有一個歌叫做?我是一個兵,來自老百姓?,馬上這個同學(xué)又給改了:?我是一塊冰,吃了肚子疼!?人的本性有一種拿語言和文字……,你可以搞得很神圣,也可以把他搞得非常游戲,當(dāng)成個玩意兒隨便玩兒,就像魔方一樣,你可以這么擰,也可以那么擰,放地下轉(zhuǎn)一轉(zhuǎn),碾一碾,擱腳踹一下,總之是玩兒,這是一種游戲。

第九,也是最重要的,語言有一種發(fā)展人的能力。它本身有一種發(fā)展能力,有一種組合能力,有一種衍生能力。就是說語言本身在人把他創(chuàng)造出來以后變成了一個世界,變成了一個有機的,活的東西,這個本身是在不斷的變異,不斷的組合,不斷的發(fā)展,它培養(yǎng)了人,比如說有了數(shù)字,它培養(yǎng)了人的條理;慢慢地我們有了反義詞的概念,很多新詞就創(chuàng)造出來了,譬如說我剛才提到的無限,無限是一個超驗的概念,它是怎么來的呢?因為我們有有限的經(jīng)驗,對有限我們是有經(jīng)驗的,我們知道空間是有限的,時間是有限的,您兜里的錢也是有限的,有了對有限的經(jīng)驗,你就想這個有限的反面是什么呢?是無限,長生不老,天地同輝,這是無限。有了短暫的經(jīng)驗,然后想和短暫對立的是什么呢?是永恒,這樣我們就創(chuàng)造了永恒。對反義詞的思索使我們?nèi)水a(chǎn)生了超驗的概念,有與無,物與神,文與理,都是這樣。比如說很多成語,這些成語出現(xiàn)以后,它是經(jīng)歷著一個變異的過程,這個過程我們現(xiàn)在很難作價值判斷,它是好還是不好,比如說現(xiàn)在,包括國務(wù)院的政府工作報告里經(jīng)常有這么一個詞叫做?知難而進?,這個成語原來是?知難而退?,但是現(xiàn)在已經(jīng)快有人不知道?知難而退?了,只知道?知難而進?,為什么呢?因為毛主席他來了一個?知難而進?,毛主席最喜歡改成語,?前仆后繼?,他來了一個?前赴后繼?,?知難而退?他給改成了?知難而進?,他用他的價值觀念來改成語。但是起碼我剛才說的那兩個改法已經(jīng)被廣泛接受了,到處都在講?知難而進?,到處都在講?前赴后繼?,比較吉利,而?前仆后繼?聽起來就不怎么吉利。所以語言本身有變異的可能,有衍生的能力,還有自我完善的能力。有時候語言的組合豐富了人的思想,當(dāng)然首先是人的思想豐富了語言,但是反過來語言的組合又豐富了人的思想。譬如說?有志者事竟成?,但是我們馬上按照這種模式可以提出幾種不同的命題來,一種是?有志者事不成?,?有志者事未成?,我們可以舉出很多的例子,一個是空有大志,到最后蹉跎一生,并無成就,你不能說這沒有啊,不能說保證百分之百的?有志者事竟成?,能有百分之十的?有志者事竟成?就不錯了。還有就是?無志者事竟成?,有沒有這種例子?至少相聲里是有,比如說?黃蛤蟆?啊等等,有許多無志者他反倒事成了,有志者事反倒不成。還有一種就是?無志者事不成?,這和?有志者事竟成?沒有什么矛盾。還有一種,即不算有志者也不算無志者,事沒算成也沒算多么不成,這樣的一大批。你就從這個?有志者事竟成?上,從這種語言的單純的組合上就可以看出語言本身能夠怎樣的衍生,怎樣的變化。周谷城(副委員長)教授曾經(jīng)給我講過一個例子。四九年的時候他去北京看望毛主席,毛主席躊躇意滿,說:失敗是成功之母,真是這么回事,我們從南昌起義,井岡山,秋收起義,一次一次反圍剿,我們失敗了多少次,最后我們成功了。周谷城當(dāng)時來了一句:?主席,成功也是失敗之母!?——主席略顯不悅,主席問?怎么講??周谷城說?成功容易驕傲啊,驕傲又使人落后……?就差不多這么個意思,?成功了人就容易……腐化啊什么的,這樣的話不就引起失敗了?……當(dāng)然,主席例外!??主席例外!?,他就加了這么一句,這是周谷城親自對我說的,后來我個人就給他這話作批注——畫蛇添足,越描越黑。你如果選擇詞句,用比較正確的說法,你應(yīng)該這樣,毛主席問?怎么講??你說?歷史上有過這樣的事情,成功了之后注意不夠結(jié)果失敗,但是我們現(xiàn)在吸取了教訓(xùn)和歷史經(jīng)驗,我們會擺脫這樣的悲劇……?,這樣說就好了!我要是替周谷城來說這話的話,可能還不止當(dāng)副委員長!但是毛主席當(dāng)時還算挺好,?啪?一拍桌子,說:?你講的有理!?后來我就想成功是失敗之母,失敗是成功之母,成功也可以是成功之母,一個成功引起一個成功,失敗也可是失敗之母,或者成功失敗各不相干,并無母子關(guān)系,這都是可能的。就是說你看著是語言文字的來回的調(diào)換,來回的組合,來回的排列,正義詞改成反義詞,或者是把賓語換成主語,但是本身他豐富了思想。相反的如果我們既沒有成功,也沒有失敗,也沒有之母這樣的概念,我們的思想會貧乏得多。所以一個沒有語言沒有文字的人,一個失去了自己的語言和自己的文字的民族是最可悲的。

語言的陷阱

剛才這些是從語言的正面來說的,但是從反過來說語言本身又是一個陷阱。第一,語言不可能完全準(zhǔn)確,語言怎么能完全準(zhǔn)確呢?我們都知道輪扁論斫

zhuó的故事,有一個造車輪的阿扁,這個阿扁看到齊懷王在讀書,他就問懷王讀什么書?。魁R懷王說我的是圣賢之書,他說這不過是糟糠而已,齊懷王就問?有說乎??,你有道理嗎?給我講講。阿扁就說,以做輪子為例,?斫?以我的體會是類似于砍但又不太一樣的工具,不是斧子,也不是鋸,他說你砍的勁兒大了它就苦了,砍的勁兒小了它就甘,這個苦字到現(xiàn)在我們還用,就是說這東西你去的太多了就苦了,做過了就是弄苦了,用的力量不夠就會甘,就甜了,現(xiàn)在這個不太用了。但是你怎么能夠掌握的合適呢?無法傳達,無法用語言來描述。你只能自己慢慢去砍、砍、砍、砍……,然后就掌握住了,他說連做輪子,語言都是無能為力的,那么你寫一本書來教人的用處就是很小的,你何況是治國平天下的大事呢?你不能說治國平天下的大事比做輪子簡單。所以能夠?qū)懴聛恚軌蛘f出來的全是糟粕。所以老子講,?智者不辯,辯者不智?,禪宗講,?不可說,不可說?,越是最精妙的道理越是不能說。至少到了孔子他有?述而不作?,說還行,但是不能作,為什么呢?因為?說?有很多彈性,有很多靈活性,有語境,有語氣,有對象,像我們今天在這里說,有交流,有呼應(yīng)。但是寫成文字了,比如說我今天講的這個,萬一被你們整理出了一個文本,那是很不幸的一件事。你要再看,那就是胡說八道,信口開河??!是經(jīng)不住推敲的,這樣一來問題就多了。所以它是不準(zhǔn)確的。

第二,把任何東西寫下來以后,它會簡單化,它會教條化,它會呆板化。本來靈活的,極好的一個論斷一個見解,寫出來以后變成死東西了,它就暗藏著變成教條,變成呆板的可能。

第三,你說的越是普及,就越有降低水平的可能,它被通俗化、被庸俗化了,最后,我有一個不雅的詞——被狗屎化了。很好的一個見解,很好的一個說法,尤其是如果一強迫,完了!那么說出來的多半是狗屎,多半不是見解,多半不會有很珍貴的思考的成分在里面,因為它變成人云亦云了,變成各執(zhí)一詞了,就像文化大革命中打語錄仗似的,你背一段語錄,他那里背一段語錄,把語言文字所表達出來的精彩的東西加以歪曲,加以簡單化。所以我們在講到語言文字的重要性的時候,我們無論如何還要看到,尤其在我們中國,有一種反語言文字的,非語言文字的傳統(tǒng)。就是我們現(xiàn)在評價一個人往往還用到一個詞叫厚重少文,什么樣的人厚重呢?他的話不多,而且話文采不多,這人沒有什么文采,拙嘴笨腮,那語言,有話不大會說,他的另一面給人的感覺比較厚重,比較穩(wěn)定,比較可靠,比較忠誠,這個是中國長期形成下來的。正是因為語言文字有這種側(cè)面的反面的東西,你說的太多了令人馬上就想到言過其實,你說得的不準(zhǔn)確,你說得夸張,你是所謂嘴皮子上的功夫。什么原因呢?因為語言太發(fā)達了,他會脫離現(xiàn)實,它和求真務(wù)實的精神不符合了。您要是到上級那里開會,比如黨委書記去省里開會,你這里天花亂墜的給他講一段,你可千萬別以為會對你印象好,肯定把你從后備名單里頭給去掉了。所以我們有很多對語言不利的說法,?夸夸其談?,?口若懸河?,?言語的巨人,行動的矮子?,那么我們現(xiàn)在批評的是行動的矮子,但是我們老是尋找那種結(jié)結(jié)巴巴的話也說不清楚的行動的巨人,這個也有點偏頗。演說,答記者問,記者招待會,既需要行動的巨人,也需要言語的大師,我們應(yīng)該提倡言語的大師和行動的大師。

第四,語言規(guī)定了你的思維。我們現(xiàn)代人出生以后接觸到的現(xiàn)有的語言信息太多了,根本你想看也看不完,想消化也消化不清楚,所以你只剩下學(xué)舌的份兒了,已有的語言已經(jīng)規(guī)定了你的思維,使你的思維不能解放,不可能有什么別的新鮮的想法。本來這是非常好的事,?床前明月光,疑是地上霜。舉頭望明月,低頭思故鄉(xiāng)。?太好了,現(xiàn)在的孩子一般話還沒說全已經(jīng)都會背這首詩了,然后你一看到月亮就?低頭思故鄉(xiāng)?,那這個月亮到底是什么樣兒,你就沒有那種真實的原生的感受,你只有這個詩歌里邊兒的。我就想我小學(xué)學(xué)作文的時候我買過一本叫做模范作文讀本,現(xiàn)在這一類的東西那就是汗牛充棟了,這個讀本給我印象最深就是我學(xué)會了一個詞叫做?皎潔?,皎潔的月亮升起,我過去對月亮有感覺,覺得月亮當(dāng)然她沒有太陽那么亮,又不象星星,不知道該怎么形容,我發(fā)現(xiàn)了皎潔之后又發(fā)現(xiàn)了月亮,我覺得皎潔這兩個字給我的幫助太大了。但是到后來我特別痛恨這兩個字,我說你形容月亮,描寫月亮,感受月亮你什么都行,老是?皎潔?,張三是皎潔的月亮,李四也是皎潔的月亮,弱智者也是皎潔的月亮,漢奸也是看見皎潔的月亮,我就是不?皎潔?!皎潔統(tǒng)治了人的思維。所以。已有的語言和文字的信息既是我們的財富,又是我們真正認識世界,進行創(chuàng)造的一個阻隔。

第五,語言還有一個陷阱就是從理論上說一切已經(jīng)說出來的尤其是寫下的東西都有可能被駁倒,我現(xiàn)在不管事實是不是被駁倒,但就是說你要抬杠,那沒有一句話是不能抬杠的。說人都要吃飯,他說正絕食的人就不吃飯,剛做完腸胃切除手術(shù)的人也不吃飯,年歲過大,靠鼻飼的人也不吃飯。我不想多舉了,就是說沒有什么話是不可以推敲的,是不可以駁倒的,為什么現(xiàn)在文壇上有一批酷評家?因為語言和文字是最容易駁倒的,一個實驗結(jié)果你想駁倒沒那么容易,除非你自己也做一回試驗。一個物理定律你想駁倒他不是那么容易,可是一個語言和文字所構(gòu)成的,所表達的思想,所表達的命題,你可以攻其一點而不及其余,你可以無限夸張。有時候我們在生活中還可以常??吹絻蓚€人在討論一個事情,本來是一個非常細微的差異的見解,但是兩個人各不相讓,你抓你的辮子越來越多,他抓他的辮子也越來越多,你說從來沒見過!什么叫從來沒見過?昨天你還見著呢!那個說你就不信,你不信管什么用???毛主席都信!語言文字可以造成人的溝通,又可以引起人的分歧,可以促進我們的思想、我們的感情更加成熟,更加明晰,或是更加敏銳,也可以阻隔我們的思想和感情來制造許許多多的無聊的沖突。我們可以想想在某種意義上說語言文字所制造的廢品,所制造的垃圾,并不比語言文字所開放的奇葩少。非常抱歉,我也鬧不清我今天講了半天是不是又是一大堆語言的垃圾,請大家原諒。(完)(溫奉橋、侯霞整理)

(本文為王蒙先生2004年4月14日在中國海洋大學(xué)的演講)

第五篇:2014事業(yè)單位面試技巧:如何避開語言陷阱

2014事業(yè)單位面試技巧:如何避開語言陷阱

語言,是事業(yè)單位面試重要的內(nèi)容之一。有些考生可能不知道,在面試中,考官有時候會設(shè)置多重語言陷阱,考察考生的能力,以達到優(yōu)秀人才的錄用。因此,在考場上,考生如何能繞開這些陷阱,更好地展現(xiàn)自己的魅力,取得面試官的青睞就非常重要了。下面,大家就可以跟隨中公教育來一起了解有哪些語言陷阱,應(yīng)對的方法是什么。

一、挑戰(zhàn)式語言陷阱

面試時考生經(jīng)常對自己薄弱的地方表現(xiàn)得不夠自信,回答時容易出現(xiàn)紕漏??脊僖矔柤斑@方面的東西,例如:對于應(yīng)屆畢業(yè)生,面試考官會設(shè)問:“你的相關(guān)工作經(jīng)驗比較欠缺,你怎么看?” 考生有時不了解考官到底想要了解什么,常會回答:“不見得吧”、“我看未必”或“完全不是這么回事”,那么也許你已經(jīng)掉進陷阱了,因為對方希望聽到的是你對這個問題的看法,而不是簡單、生硬的反駁。

對于這樣的問題,考生可以用“這樣的說法未必全對”、“這樣的看法值得探討”、“這樣的說法有一定的道理,但我恐怕不能完全接受”為開場白,然后婉轉(zhuǎn)地表達自己的不同意見。

二、用“激將法”遮蔽的語言陷阱

“激將法”是面試考官用來淘汰應(yīng)考者的慣用手法。采用這種手法的面試考官,往往在提問之前就會用懷疑、尖銳、咄咄逼人的眼神逼視對方,先令對方心理防線步步潰退,然后冷不防用一個明顯不友好的發(fā)問激怒對方。如:“你經(jīng)歷太單純,而我們需要的是社會經(jīng)驗豐富的人”,“你性格過于內(nèi)向,這恐怕與我們的職業(yè)不合適”,“你的專業(yè)與所申請的職位不對口”等等。

面對這種咄咄逼人的發(fā)問,作為考生,首先要做到的就是無論如何不要被“激怒”,如果你被“激怒”了,那么你就已經(jīng)輸?shù)袅?。那么,面對這樣的發(fā)問,考生將如何應(yīng)對呢? 考生不要被語言陷阱迷惑,可采用靈活巧妙的方式回避考官提出的問題。例如:考官說:“你的專業(yè)與所申請的職位不對口?!蹦憧梢郧擅畹鼗卮穑骸?1世紀(jì)最搶手的就是復(fù)合型人才,而外行的靈感也許會超過內(nèi)行,因為他們沒有思維定勢,沒有條條框框?!?/p>

考生若結(jié)結(jié)巴巴,無言以對,抑或怒形于色,據(jù)理力爭,異常激動,那就掉進了對方所設(shè)置的陷阱。

三、“請君入甕”式的語言陷阱

在面試的各種語言陷阱中,“請君入甕”式的語言陷阱使很多考生感到苦惱,因為它所列舉的問題都是現(xiàn)實工作中經(jīng)常遇到的。例如:“如果你擬定了一個方案,你的直接領(lǐng)導(dǎo)不滿意,但是局長卻非常的滿意,你將如何處理? ”考生在回答這類問題時常?;卮鸬貌蝗?,容易出現(xiàn)顧上這頭,又忽略了另一頭,漏洞百出,容易讓考官一眼就能察覺出考生是否具備思考問題的縝密性,甚至從這種壓力面試中看到考生是否符合這一職位。考生在回答時要從工作職責(zé)上多思考,切勿違背了做人的根本,只要態(tài)度積極,原則問題把握準(zhǔn)確,也可成功回避這類語言陷阱。

四、誘導(dǎo)式的語言陷阱

這類問題的特點是,面試考官往往設(shè)定一個特定的背景條件,誘導(dǎo)對方做出錯誤的回答,因為也許任何一種回答都不能讓對方滿意。這時候,考生的回答就需要用模糊語言來表示。

如:“依你現(xiàn)在的水平,報考這一職位沒有感覺到屈才嗎?有沒有想過另謀職位?”考生在回答這類問題時,直接回答是或不是都不是最佳的答案,回答這類問題考生可以先用“不可一概而論”作為開頭,然后回答以職位發(fā)展、人才培養(yǎng)等長遠意義上的內(nèi)容回答,給考官一個既不偏激又很大體的答案。

還有一種誘導(dǎo)式的語言陷阱是,考官的提問似乎是一道單項選擇題,如果考生選了,就會掉進陷阱。比如說,對方問:“你認為金錢、名譽和事業(yè)

哪個重要?”很多考生可能會選擇說三者都重要來回避考官的陷阱。但有時考官的提問卻在誤導(dǎo)你,讓考生認為“這三者是相互矛盾的,只能選其一”。這時候切不可中了考官的圈套,必須冷靜分析,考生可以首先明確指出這個前提條件是不存在的,再解釋三者對我們的重要性及其統(tǒng)一性。

五、測試式的語言陷阱

這類問題的特點是虛構(gòu)一種情況,然后讓考生做出回答。比如“今天參加面試的有近10位候選人,如何證明你是最優(yōu)秀的?”無論你給自己列舉多少優(yōu)點,別人總有你不具備的優(yōu)點,因此正面回答這樣的問題毫無意義,考生可以從側(cè)面回答這個問題,了解考官的真正考點在哪里,然后針對這個考點尋找突破口,就像上面提到的這個問題,它往往是考察應(yīng)考者隨機應(yīng)變的能力,考生可以從不否認其他人優(yōu)點的情況下,突出描述一下自己具備的區(qū)別于他人的技能,從而使自己在眾多候選人中脫穎而出。

面試的成功,是需要考生良好的心理素質(zhì)和過硬的知識水平的。為了更好的面對考試,取得成功,備考前的練習(xí)必不可少??忌欢ㄒ盐蘸眠@段時間,多積累內(nèi)容,掌握技巧,爭取考試成功!

中公教育新疆事業(yè)單位:http:///xinjiang/

下載C語言陷阱和缺陷(精選五篇)word格式文檔
下載C語言陷阱和缺陷(精選五篇).doc
將本文檔下載到自己電腦,方便修改和收藏,請勿使用迅雷等下載。
點此處下載文檔

文檔為doc格式


聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻自行上傳,本網(wǎng)站不擁有所有權(quán),未作人工編輯處理,也不承擔(dān)相關(guān)法律責(zé)任。如果您發(fā)現(xiàn)有涉嫌版權(quán)的內(nèi)容,歡迎發(fā)送郵件至:645879355@qq.com 進行舉報,并提供相關(guān)證據(jù),工作人員會在5個工作日內(nèi)聯(lián)系你,一經(jīng)查實,本站將立刻刪除涉嫌侵權(quán)內(nèi)容。

相關(guān)范文推薦

    Chapter1語言的功能與陷阱(五篇模版)

    大學(xué)語文題庫 一.語言的功能與陷阱 1 大學(xué)語文主要培養(yǎng)的是(C)。 ?A、背誦 ?B、書寫 ?C、語感 ?D、文采 (往年考過)2.(鏡像問題)王蒙的(A)這部作品給使他被錯劃為右派。 ?A、《組織部來了......

    事業(yè)單位考試:面試技巧之如何避開語言陷阱

    文章來自赤峰人事考試信息網(wǎng):http://chifeng.offcn.com 事業(yè)單位考試:面試技巧之如何避開語言陷阱事業(yè)單位:語言,是事業(yè)單位面試重要的內(nèi)容之一。有些參加事業(yè)單位考試的考生可......

    合同陷阱

    新房首頁 > 資訊 > 買房知識 > 全文 合同:合同陷阱摘要:實例1:《××居商品房認購書》中規(guī)定:“若乙方支付定金之日起十天內(nèi)未能依時簽署《商品房買賣合同》及交付首期房價款,則......

    薪酬陷阱

    薪酬陷阱:求職業(yè)要小心啊 找工作不是難事,找到一份合適自己又喜歡的工作就是難上加難了,而找到一份自己喜歡而工資又高的簡直就比登天還難了,所以大家總是在不斷地尋找高工資的......

    陷阱怎么造句

    陷阱拼音【注音】: xian jing陷阱解釋【意思】:(1)為捉野獸或敵人而挖的坑,上面浮蓋偽裝的東西,踩在上面就掉到坑里。(2)比喻害人的圈套。陷阱造句陷阱造句:1、狐貍在陷阱里被套住。2、......

    面試中要注意的語言陷阱與應(yīng)答技巧

    就業(yè)面試經(jīng)典問題及最佳答案 工作動機 個人愿望篇 自考生就業(yè)面試經(jīng)典問題”見招拆招“ 工作動機、個人愿望· 請給我們談?wù)勀阕约旱囊恍┣闆r·你是哪年出生的?你是哪所大學(xué)......

    面試中你必須要知道的語言陷阱5篇

    面試中你必須要知道的語言陷阱如:“你經(jīng)歷太單純,而我們需要的是社會經(jīng)驗豐富的人”,“你性格過于內(nèi)向,這恐怕與我們的職業(yè)不合適”,“我們需要名牌院校的畢業(yè)生,你并非畢業(yè)于名牌......

    缺陷管理制度

    缺陷管理制度 1:消毒供應(yīng)中心工作人員必須具有高度的責(zé)任感,遵守醫(yī)院規(guī)章制度,認真履行崗位職責(zé),嚴格遵守各項規(guī)章制度和技術(shù)操作流程。 2:制定并落實各種缺陷防范預(yù)案,護士長,組長......

主站蜘蛛池模板: 亚洲欧美日本国产高清| 国语对白刺激精品视频| 久久久久99人妻一区二区三区| 欧美熟妇另类久久久久久不卡| 亚洲性线免费观看视频成熟| 亚洲婷婷综合色高清在线| 亚洲中文无码精品卡通| 久久精品国产99国产精品导航| а天堂中文最新一区二区三区| 国精产品一线二线三线av| 又色又爽又高潮免费视频国产| 国产成人精品亚洲日本专区61| 国产亚洲午夜高清国产拍精品| 无码人妻少妇色欲av一区二区| 无遮挡1000部拍拍拍免费| 综合无码一区二区三区| 精品999久久久久久中文字幕| 亚洲色成人网站www永久四虎| 妺妺跟我一起洗澡没忍住| 亚洲精品国产精品国自产小说| 亚洲乱亚洲乱妇无码| 丰满人妻| 九九热爱视频精品视频| 亚洲精品你懂的在线观看| 国产精品办公室沙发| 欧美亚洲国产一区二区三区| 狠狠色噜噜狠狠狠狠97俺也去| 国产综合久久99久久| 久久99亚洲精品久久69| 亚洲熟女乱色一区二区三区| 亚洲第一最快av网站| 337p粉嫩大胆色噜噜噜| 国产永久免费高清在线| 色婷婷亚洲六月婷婷中文字幕| 日韩一区国产二区欧美三区| 丰满人妻熟妇乱又伦精品视频三| 日韩爆乳一区二区无码| 亚洲欧美日韩中文在线制服| 国产精品美女久久久久网站浪潮| 久久精品人人做人人综合试看| 亚洲第一天堂国产丝袜熟女|