語言是人與人相互溝通的途徑,而計(jì)算機(jī)語言則是人和計(jì)算機(jī)溝通的途徑。就算是任何再完美的自然語言都會(huì)有歧義,但是又是什么讓人和計(jì)算計(jì)算機(jī)間產(chǎn)生了歧義呢?
下面這篇文章來自Gowri Kumar的Puzzle C一文。我做了一些整理,挑選了其中的一些問題,并在之后配上相應(yīng)的答案(這些答案是我加的,如果需要原版的答案可以直接和本文作者Gowri Kumar聯(lián)系,作者的聯(lián)系方式可以從這里得到)。
puzzle 1
此段程序的作者希望輸出數(shù)組中的所有元素,但是他卻沒有得到他想要的結(jié)果,是什么讓程序員和計(jì)算機(jī)產(chǎn)生歧義?
02 |
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) |
03 |
int array[] = {23,34,12,17,204,99,16}; |
08 |
for (d=-1;d <= (TOTAL_ELEMENTS-2);d++) |
09 |
printf ( "%d\n" ,array[d+1]); |
解答:
運(yùn)行上面的程序,結(jié)果是什么都沒有輸出,導(dǎo)致這個(gè)結(jié)果的原因是sizeof的返回值是一個(gè)unsinged int,為此在比較int d 和TOTAL_ELEMENTS兩個(gè)值都被轉(zhuǎn)換成了unsigned int來進(jìn)行比較,這樣就導(dǎo)致-1被轉(zhuǎn)換成一個(gè)非常大的值,以至于for循環(huán)不滿足條件。因此,如果程序員不能理解sizeof操作符返回的是一個(gè)unsigned int的話,就會(huì)產(chǎn)生類似如上的人機(jī)歧義。
puzzle 2
看上去非常完美的程序,是什么導(dǎo)致了編程程序不通過?
03 |
void OS_Solaris_print() |
05 |
printf ( "Solaris - Sun Microsystems\n" ); |
08 |
void OS_Windows_print() |
10 |
printf ( "Windows - Microsoft\n" ); |
15 |
printf ( "HP-UX - Hewlett Packard\n" ); |
21 |
printf ( "Enter the number (1-3):\n" ); |
36 |
printf ( "Hmm! only 1-3 <IMG class=wp-smiley alt=:-) src=" http: ///wp-includes/images/smilies/icon_smile.gif"> \n"); |
解答:
程序員要以計(jì)算機(jī)的語言進(jìn)行思考,不上上面那段程序?qū)е碌慕Y(jié)果不止是歧義這么簡單,而直接的結(jié)果是,導(dǎo)致計(jì)算機(jī)”聽不懂”你在說什么。導(dǎo)致計(jì)算機(jī)聽不懂的原因是HP-UX中的’-'是減號(hào)?還是其他什么?
puzzle 3
下面這段程序會(huì)輸出什么,為什么?
解答:
1到14?不對(duì),結(jié)果是1,因?yàn)閏ontinue的含義是不執(zhí)行循環(huán)體之后語義,而直接到循環(huán)點(diǎn)。明顯while(false)不屬于循環(huán)體。導(dǎo)致這段程序的歧義就是:程序員沒有完全理解計(jì)算機(jī)語言中continue的含義。
puzzle 4
下面這段程序的輸出結(jié)果是:
08 |
printf ( "%s\n" , h(f(1,2))); |
09 |
printf ( "%s\n" , g(f(1,2))); |
當(dāng)然,你首先要了解##和#的用法,如果不懂的話,本題你可以直接跳過。
解答:
看到這段程序你可能會(huì)認(rèn)為,這兩個(gè)printf輸出的同一個(gè)結(jié)果,可是答案卻非如此,本題的輸出是12和f(1,2),為什么會(huì)這樣呢?因?yàn)檫@是宏,宏的解開不象函數(shù)執(zhí)行,由里帶外。
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;
}
解答:
本題我故意將語法敏感插件去掉,為了就是能得到更好的效果,這道題又是什么讓歧義再次發(fā)生,如果不仔細(xì)你可能永遠(yuǎn)都找不到答案,如果真到的到了那個(gè)時(shí)候,你是否會(huì)因?yàn)閷?duì)default語義的懷疑,而不敢再使用default?本題的歧義點(diǎn)就是default,看好了是defau1t而不是default,不是關(guān)鍵字!為什么計(jì)算能”聽懂”這樣的defau1t,算然它聽懂了,但它的理解卻是標(biāo)號(hào)”defau1t”
puzzle 6
下面這段程序的輸出什么?
12 |
printf ( "f is 1.0 \n" ); |
14 |
printf ( "f is NOT 1.0 \n" ); |
解答:
你是否似曾相識(shí)?不錯(cuò)這個(gè)問題在酷殼之前的博文《你能做對(duì)下面這些JavaScript的題嗎?》中曾今提到過,不要讓兩個(gè)浮點(diǎn)數(shù)相比較。所以本題的答案是”f is NOT 1.0″,如果你真想比較兩個(gè)浮點(diǎn)數(shù)時(shí),你應(yīng)該按一定精度來比較,比如你一定要在本題中做比較那么你應(yīng)該這么做if( (f – 1.0f)<0.1 )
puzzle 7
下面兩個(gè)函數(shù)是否具有相同的原型?
下面這兩段程序?qū)?huì)幫你找到上題的答案
程序1
04 |
printf ( "In foobar1\n" ); |
09 |
printf ( "In foobar2\n" ); |
程序2
04 |
printf ( "In foobar1\n" ); |
09 |
printf ( "In foobar2\n" ); |
解答
程序片段一,沒有問題,程序片段二編譯報(bào)錯(cuò),這兩個(gè)程序告訴我們,foobar1(void)和foobar2()是有不同原型的的。我們可以在《ISO/IEC 9899》的C語言規(guī)范找到下面兩段關(guān)于函數(shù)聲明的描述
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)是沒有參數(shù),而foobar1()等于forbar1(…)等于參數(shù)類型未知。
總結(jié)
看到這些C語言的題目,不禁讓我想起了巴別塔,計(jì)算機(jī)語言作為如此嚴(yán)謹(jǐn)?shù)恼Z言都有可能帶來如此多的歧義,更何況自然語言,更何況相互不通的自然語言。要杜絕歧義,我們就必須清晰的了解計(jì)算機(jī)語言每一個(gè)指令的語義。就如同人類,人類要和平就要相互了解各自的文化。愿世界上人們清晰了解別人的語言的語義,愿世界不再因?yàn)槲幕牟煌鴳?zhàn)爭,原世界和平。