语言的歧义

语言是人与人相互沟通的途径,而计算机语言则是人和计算机沟通的途径。就算是任何再完美的自然语言都会有歧义,但是又是什么让人和计算计算机间产生了歧义呢?
下面这篇文章来自Gowri Kumar的Puzzle C一文。我做了一些整理,挑选了其中的一些问题,并在之后配上相应的答案(这些答案是我加的,如果需要原版的答案可以直接和本文作者Gowri Kumar联系,作者的联系方式可以从这里得到)。

puzzle 1

此段程序的作者希望输出数组中的所有元素,但是他却没有得到他想要的结果,是什么让程序员和计算机产生歧义?

#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的话,就会产生类似如上的人机歧义。

puzzle 2

看上去非常完美的程序,是什么导致了编程程序不通过?

#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中的’-‘是减号?还是其他什么?

puzzle 3

下面这段程序会输出什么,为什么?

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的含义。

puzzle 4

下面这段程序的输出结果是:

#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),为什么会这样呢?因为这是宏,宏的解开不象函数执行,由里带外。

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

下面这段程序的输出什么?

#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 )

puzzle 7

下面两个函数是否具有相同的原型?

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语言的题目,不禁让我想起了巴别塔,计算机语言作为如此严谨的语言都有可能带来如此多的歧义,更何况自然语言,更何况相互不通的自然语言。要杜绝歧义,我们就必须清晰的了解计算机语言每一个指令的语义。就如同人类,人类要和平就要相互了解各自的文化。愿世界上人们清晰了解别人的语言的语义,愿世界不再因为文化的不同而战争,原世界和平。

转自:http://coolshell.cn/articles/830.html

原创文章,作者:s19930811,如若转载,请注明出处:http://www.178linux.com/2255

(0)
s19930811s19930811
上一篇 2016-05-07
下一篇 2016-05-09

相关推荐

  • 脚本编程之变量

    简单跟大家介绍一下脚本编程中的变量。 大家都知道,脚本编程,主要由三个部分组成,一是命令,二是变量,三是控制语句。 变量的使用,可以让脚本变得更加简洁,高效。 我们主要简单讲一下变量的概念,变量的类型,变量的种类,不同种类的变量是如何定义的,变量的基本操作,以及变量的配置文件。 了解了这些,我们还可以聊一聊变量的高级操作。 首先,什么是变量? 通俗一点讲:变…

    Linux干货 2017-04-17
  • linux磁盘管理及其磁盘分区工具的使用

    一、 几种分区工具: 1.图形化工具gnome-disks使用简单,在此不在赘述。 2.fdisk使用: fdisk支持MBR,也支持GPT分区,对于一块硬盘最多只能理解15个分区,一般使用fdisk做MBR分区,gdisk做GPT分区。下面为fdisk分区示例: [root@centos7 ~]# fdisk /dev/sde…

    Linux干货 2016-08-29
  • 系统管理至grub故障排错及自建linux

    第二章    系统启动故障排除     1、grub配置文件写错,无法进入系统     步骤:(修复完成后记得修改配置文件为正确的文件)     方法一:进入启动菜单项后,修改菜单项为正确的内容,然后…

    Linux干货 2016-09-13
  • shell变量的浅谈

    变量本质上是存储数据的一个或多个计算机内存地址,变量的命令规则包括: 1) 不能使用程序中的保留字,如if, for 2) 变量由字母、下划线和数字组成,且不能以数字开头 3) 要求风名知义 4) 统一命名规则:驼峰命名法 变量主要分为本地变量、环境变量、局部变量、位置变量和特殊变量 (1)本地变量:只对当前shell…

    2017-08-05
  • sed命令、crontab任务、简单脚本练习(21期网络班第六周博客作业)

    vim使用: 直接使用sed模式空间演示,基本语法与vim命令模式类似(需注意vim默认定界为当前行,一般需要在前面加1,$定界为全文,而sed不需要) 1、 复制/etc/rc.d/rc.sysinit文件至/tmp目录,将/tmp/rc.sysinit文件中的以至少一个空白字符开头的行的行首加#;      sed &#03…

    Linux干货 2016-08-22
  • 文本编辑器—sed

    一、sed介绍 sed 一种流式编辑器。一个流式编辑器通常对来自输入流(一个文件或者是管道的输入)的文本进行转换处理。在某些方面类似支持脚本编辑的编辑器,sed在多输入情况下只开放一个通道工作,因此更加效率。sed与其他编辑器最大的区别在于,能对管道输入的文本进行过滤处理。 二、sed工作机制 sed保持两个数据缓冲区:主要活动的模式空间,以及辅助性的保持空…

    Linux干货 2016-08-12