语言是人与人相互沟通的途径,而计算机语言则是人和计算机沟通的途径。就算是任何再完美的自然语言都会有歧义,但是又是什么让人和计算计算机间产生了歧义呢?
下面这篇文章来自Gowri Kumar的Puzzle C一文。我做了一些整理,挑选了其中的一些问题,并在之后配上相应的答案(这些答案是我加的,如果需要原版的答案可以直接和本文作者Gowri Kumar联系,作者的联系方式可以从这里得到)。
此段程序的作者希望输出数组中的所有元素,但是他却没有得到他想要的结果,是什么让程序员和计算机产生歧义?
#include ‹stdio.h› #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) int array[] = {23,34,12,17,204,99,16}; int main() { int d;
for(d=-1;d ‹= (TOTAL\_ELEMENTS-2);d++)
printf("%d\\n",array\[d+1\]);
return 0;
}
解答:
运行上面的程序,结果是什么都没有输出,导致这个结果的原因是sizeof的返回值是一个unsinged int,为此在比较int d 和TOTAL_ELEMENTS两个值都被转换成了unsigned int来进行比较,这样就导致-1被转换成一个非常大的值,以至于for循环不满足条件。因此,如果程序员不能理解sizeof操作符返回的是一个unsigned int的话,就会产生类似如上的人机歧义。
看上去非常完美的程序,是什么导致了编程程序不通过?
#include ‹stdio.h›
void OS_Solaris_print() { printf("Solaris - Sun Microsystems\n"); }
void OS_Windows_print() { printf("Windows - Microsoft\n"); }
void OS_HP-UX_print() { printf("HP-UX - Hewlett Packard\n"); }
int main() { int num; printf("Enter the number (1-3):\n"); scanf("%d",&num);
switch(num)
{
case 1:
OS\_Solaris\_print();
break;
case 2:
OS\_Windows\_print();
break;
case 3:
OS\_HP-UX\_print();
break;
default:
printf("Hmm! only 1-3 :-)\\n");
break;
}
return 0;
}
解答:
程序员要以计算机的语言进行思考,不上上面那段程序导致的结果不止是歧义这么简单,而直接的结果是,导致计算机”听不懂”你在说什么。导致计算机听不懂的原因是HP-UX中的’-‘是减号?还是其他什么?
下面这段程序会输出什么,为什么?
enum {false,true};
int main() { int i=1; do { printf("%d\n",i); i++;
if(i ‹ 15)
continue;
}while(false);
return 0;
}
解答:
1到14?不对,结果是1,因为continue的含义是不执行循环体之后语义,而直接到循环点。明显while(false)不属于循环体。导致这段程序的歧义就是:程序员没有完全理解计算机语言中continue的含义。
下面这段程序的输出结果是:
#include ‹stdio.h› #define f(a,b) a##b #define g(a) #a #define h(a) g(a)
int main() { printf("%s\n", h(f(1,2))); printf("%s\n", g(f(1,2))); return 0; }
当然,你首先要了解##和#的用法,如果不懂的话,本题你可以直接跳过。
解答:
看到这段程序你可能会认为,这两个printf输出的同一个结果,可是答案却非如此,本题的输出是12和f(1,2),为什么会这样呢?因为这是宏,宏的解开不象函数执行,由里带外。
下面这段程序的输出是什么
› #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”
下面这段程序的输出什么?
#include ‹stdio.h›
int main() { float f=0.0f; int i;
for(i=0;i‹10;i++)
f = f + 0.1f;
if(f == 1.0f)
printf("f is 1.0 \\n");
else
printf("f is NOT 1.0 \\n");
return 0;
}
解答:
你是否似曾相识?不错这个问题在酷壳之前的博文《你能做对下面这些JavaScript的题吗?》中曾今提到过,不要让两个浮点数相比较。所以本题的答案是”f is NOT 1.0″,如果你真想比较两个浮点数时,你应该按一定精度来比较,比如你一定要在本题中做比较那么你应该这么做if( (f – 1.0f)‹0.1 )
下面两个函数是否具有相同的原型?
int foobar(void); int foobar();
下面这两段程序将会帮你找到上题的答案
程序1
#include ‹stdio.h› void foobar1(void) { printf("In foobar1\n"); }
void foobar2() { printf("In foobar2\n"); }
int main() { char ch = 'a';
foobar1();
foobar2(33, ch);
return 0;
}
程序2
#include "stdio.h" void foobar1(void) { printf("In foobar1\n"); }
void foobar2() { printf("In foobar2\n"); }
int main() { char ch = 'a';
foobar1(33,ch);
foobar2();
return 0;
}
解答
程序片段一,没有问题,程序片段二编译报错,这两个程序告诉我们,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语言的题目,不禁让我想起了巴别塔,计算机语言作为如此严谨的语言都有可能带来如此多的歧义,更何况自然语言,更何况相互不通的自然语言。要杜绝歧义,我们就必须清晰的了解计算机语言每一个指令的语义。就如同人类,人类要和平就要相互了解各自的文化。愿世界上人们清晰了解别人的语言的语义,愿世界不再因为文化的不同而战争,原世界和平。