語言的歧義
趙錕
zhaokun.km (at)gmail.com
語言是人與人相互溝通的途徑,而計算機語言則是人和計算機溝通的途徑。就算是任何再完美的天然語言都會有歧義,可是又是什麼讓人和計算計算機間產生了歧義呢?
下面這篇文章來自Gowri Kumar的
Puzzle C一文。我作了一些整理,挑選了其中的一些問題,並在以後配上相應的答案(這些答案是我加的,若是須要原版的答案能夠直接和本文做者Gowri Kumar聯繫,做者的聯繫方式能夠從
這裏獲得)。
puzzle 1
此段程序的做者但願輸出數組中的全部元素,可是他卻沒有獲得他想要的結果,是什麼讓程序員和計算機產生歧義?
01.#include<stdio.h>
02.#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
03.int array[] = {23,34,12,17,204,99,16};
04.int main()
05.{
06. int d;
07.
08. for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
09. printf("%d\n",array[d+1]);
10.
11. return 0;
12.}
解答:
運行上面的程序,結果是什麼都沒有輸出,致使這個結果的緣由是sizeof的返回值是一個unsinged int,爲此在比較int d 和TOTAL_ELEMENTS兩個值都被轉換成了unsigned int來進行比較,這樣就致使-1被轉換成一個很是大的值,以致於for循環不知足條件。所以,若是程序員不能理解sizeof操做符返回的是一個 unsigned int的話,就會產生相似如上的人機歧義。
puzzle 2
看上去很是完美的程序,是什麼致使了編程程序不經過?
01.#include<stdio.h>
02.
03.void OS_Solaris_print()
04.{
05. printf("Solaris - Sun Microsystems\n");
06.}
07.
08.void OS_Windows_print()
09.{
10. printf("Windows - Microsoft\n");
11.
12.}
13.void OS_HP-UX_print()
14.{
15. printf("HP-UX - Hewlett Packard\n");
16.}
17.
18.int main()
19.{
20. int num;
21. printf("Enter the number (1-3):\n");
22. scanf("%d",&num);
23.
24. switch(num)
25. {
26. case 1:
27. OS_Solaris_print();
28. break;
29. case 2:
30. OS_Windows_print();
31. break;
32. case 3:
33. OS_HP-UX_print();
34. break;
35. default:
36. printf("Hmm! only 1-3 :-)\n");
37. break;
38. }
39.
40. return 0;
41.}
解答:
程序員要以計算機的語言進行思考,不上上面那段程序致使的結果不止是歧義這麼簡單,而直接的結果是,致使計算機」聽不懂」你在說什麼。致使計算機聽不懂的緣由是HP-UX中的’-'是減號?仍是其餘什麼?
puzzle 3
下面這段程序會輸出什麼,爲何?
01.enum {false,true};
02.
03.int main()
04.{
05. int i=1;
06.
07. do
08. {
09. printf("%d\n",i);
10. i++;
11. if(i < 15)
12. continue;
13. }while(false);
14.
15. return 0;
16.}
解答:
1到14?不對,結果是1,由於continue的含義是不執行循環體以後語義,而直接到循環點。明顯while(false)不屬於循環體。致使這段程序的歧義就是:程序員沒有徹底理解計算機語言中continue的含義。
puzzle 4
下面這段程序的輸出結果是:
01.#include <stdio.h>
02.#define f(a,b) a##b
03.#define g(a) #a
04.#define h(a) g(a)
05.
06.int main()
07.{
08. printf("%s\n",h(f(1,2)));
09. printf("%s\n",g(f(1,2)));
10. return 0;
11.}
固然,你首先要了解##和#的用法,若是不懂的話,本題你能夠直接跳過。
解答:
看到這段程序你可能會認爲,這兩個printf輸出的同一個結果,但是答案卻非如此,本題的輸出是12和f(1,2),爲何會這樣呢?由於這是宏,宏的解開不象函數執行,由裏帶外。
puzzle 5
下面這段程序的輸出是什麼
#include <stdio.h>
int main()
{
int a=10;
switch(a)
{
case ‘1′:
printf(」ONE\n」);
break;
case ‘2′:
printf(」TWO\n」);
break;
defau1t:
printf(」NONE\n」);
}
return 0;
}
解答:
本題我故意將語法敏感插件去掉,爲了就是能獲得更好的效果,這道題又是什麼讓歧義再次發生,若是不仔細你可能永遠都找不到答案,若是真到的到了那個時候,你是否會由於對default語義的懷疑,而不敢再使用default?本題的歧義點就是default,看好了是defau1t而不是default,不是關鍵字!爲何計算能」聽懂」這樣的defau1t,算然它聽懂了,但它的理解倒是標號」defau1t」
puzzle 6
下面這段程序的輸出什麼?
01.#include <stdio.h>
02.
03.int main()
04.{
05. float f=0.0f;
06. int i;
07.
08. for(i=0;i<10;i++)
09. f = f + 0.1f;
10.
11. if(f == 1.0f)
12. printf("f is 1.0 \n");
13. else
14. printf("f is NOT 1.0\n");
15.
16. return 0;
17.}
解答:
你是否似曾相識?不錯這個問題在酷殼以前的博文《你能作對下面這些JavaScript的題嗎?》中曾今提到過,不要讓兩個浮點數相比較。因此本題的答案是」f is NOT 1.0″,若是你真想比較兩個浮點數時,你應該按必定精度來比較,好比你必定要在本題中作比較那麼你應該這麼作if( (f - 1.0f)<0.1 )
puzzle 7
下面兩個函數是否具備相同的原型?
1.int foobar(void);
2.int foobar();
下面這兩段程序將會幫你找到上題的答案
程序1
01.#include <stdio.h>
02.void foobar1(void)
03.{
04. printf("In foobar1\n");
05.}
06.
07.void foobar2()
08.{
09. printf("In foobar2\n");
10.}
11.
12.int main()
13.{
14. char ch = 'a';
15.
16. foobar1();
17. foobar2(33, ch);
18.
19. return 0;
20.}
程序2
01.#include <stdio.h>
02.void foobar1(void)
03.{
04. printf("In foobar1\n");
05.}
06.
07.void foobar2()
08.{
09. printf("In foobar2\n");
10.}
11.
12.int main()
13.{
14. char ch = 'a';
15.
16. foobar1(33,ch);
17. foobar2();
18.
19. return 0;
20.}
解答
程序片斷一,沒有問題,程序片斷二編譯報錯,這兩個程序告訴咱們,foobar1(void)和foobar2()是有不一樣原型的的。咱們能夠在《ISO/IEC 9899》的C語言規範找到下面兩段關於函數聲明的描述
10.The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters
14.An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.124)
上面兩段話的意思就是:foobar1(void)是沒有參數,而foobar1()等於forbar1(…)等於參數類型未知。
總結
看到這些C語言的題目,不由讓我想起了巴別塔,計算機語言做爲如此嚴謹的語言都有可能帶來如此多的歧義,更況且天然語言,更況且相互不通的天然語言。要杜絕歧義,咱們就必須清晰的瞭解計算機語言每個指令的語義。就如同人類,人類要和平就要相互瞭解各自的文化。願世界上人們清晰瞭解別人的語言的語義,願世界再也不由於文化的不一樣而戰爭,原世界和平。
(本文由我代我同窗發佈,因此轉載時請註明做者和出處。未經許可,請勿用於商業用途)