转自:http://www.kuqin.com/language/20090529/53686.html
本文来自:http://cocre.com/?p=830
语言是人与人相互沟通的途径,而计算机语言则是人和计算机沟通的途径。就算是任何再完美的自然语言都会有歧义,但是又是什么让人和计算计算机间产生了歧义呢?
下面这篇文章来自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 "
,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 "
);
06.
}
07.
08.
void
OS_Windows_print()
09.
{
10.
printf
(
"Windows - Microsoft "
);
11.
}
12.
13.
void
OS_HP-UX_print()
14.
{
15.
printf
(
"HP-UX - Hewlett Packard "
);
16.
}
17.
18.
int
main()
19.
{
20.
int
num;
21.
printf
(
"Enter the number (1-3): "
);
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 :-) "
);
37.
break
;
38.
}
39.
return
0;
40.
}
解答:
程序员要以计算机的语言进行思考,不上上面那段程序导致的结果不止是歧义这么简单,而直接的结果是,导致计算机”听不懂”你在说什么。导致计算机听不懂的原因是HP-UX中的’-"是减号?还是其他什么?
puzzle 3
下面这段程序会输出什么,为什么?
01.
enum
{
false
,
true
};
02.
03.
int
main()
04.
{
05.
int
i=1;
06.
do
07.
{
08.
printf
(
"%d "
,i);
09.
i++;
10.
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 "
, h(f(1,2)));
09.
printf
(
"%s "
, 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 ”);
break;
case ‘2′:
printf(”TWO ”);
break;
defau1t:
printf(”NONE ”);
}
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 "
);
13.
else
14.
printf
(
"f is NOT 1.0 "
);
15.
16.
return
0;
17.
}
解答:
不要让两个浮点数相比较,本题的答案是”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 "
);
05.
}
06.
07.
void
foobar2()
08.
{
09.
printf
(
"In foobar2 "
);
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 "
);
05.
}
06.
07.
void
foobar2()
08.
{
09.
printf
(
"In foobar2 "
);
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语言的题目,不禁让我想起了巴别塔,计算机语言作为如此严谨的语言都有可能带来如此多的歧义,更何况自然语言,更何况相互不通的自然语言。要杜绝歧义,我们就必须清晰的了解计算机语言每一个指令的语义。就如同人类,人类要和平就要相互了解各自的文化。
对于最后一题,可再参考如下文章://by orc
http://stackoverflow.com/questions/5075530/use-of-void-in-function-parameter
Possible Duplicate: C void arguments
Just started with C and I can't find answer to this...
Is there any difference between
int foo() { } int foo(void) { }
Which should I prefer and why?
Note that this question also goes for: int main . Should it be: int main or int main(void) when I don't want any command line arguments.
|
up vote
9 down vote
accepted
|
The two canonical forms of main are, according to the standard (see C99 section 5.1.2.2.2 "Program startup"):
int main (void); int main (int argc, char *argv[]); // or equivalent such as char **argv
Others are specifically allowed but those are the required ones.
As to the preferred form between fn(void) and fn() , I prefer the former since I like to explicitly state that there are no parameters.
There is also a subtle difference. C99 section 6.7.5.3 "Function declarators", paragraph 10, states:
The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.
Paragraph 14 of that same section shows the only difference:
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.
In other words, it means the same as void in the function definition but does not mean that in a standalone declarator (i.e., the prototype). int fn(); means that no information on the parameters is yet known but int fn(void); means there are no parameters.
That means that:
int fn(); int fn (int x) { return x; } int main (void) { return fn(0); }
is valid but:
int fn(void); int fn (int x) { return x; } int main (void) { return fn(0); }
is not.
|
http://www.velocityreviews.com/forums/t706644-empty-parameters-list-and-function-definition.html
Guest
Posts:
n/a
|
Hello,
I read section 6.7.5.3p14 from C99 several times but it looks I missed
something in my understanding...
I put the revelant section for convenience:
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.
So to check that I understood correctly this, I wrote and tried to
compile the following code:
--- 8< ---
/*
* A function declaration with empty identifier list which is part of
* the definition specifies that the function has no parameters.
*/
int foo()
{
return 1;
}
/* A function declaration alone with empty list identifier specifies
* that no information about the number or types of the parameters is
* supplied.
*/
int bar();
void test(void)
{
foo(1); /* should fail */
bar(1); /* should be ok */
}
--- >8 ---
Gcc compiles this without any warnings (gcc -Wall -std=c99 -c func.c).
However I would expect gcc to issue an error or a warning since 'foo'
function takes no parameters (in my understanding).
Could anybody enlight me ?
Thanks
|
|
|
|
|
|
|
Guest
Posts:
n/a
|
Francis Moreau <> writes:
> I read section 6.7.5.3p14 from C99 several times but it looks I missed > something in my understanding... > > I put the revelant section for convenience: > > 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. > > So to check that I understood correctly this, I wrote and tried to > compile the following code: > > --- 8< --- > > /* > * A function declaration with empty identifier list which is part of > * the definition specifies that the function has no parameters. > */ > int foo() > { > return 1; > } > > /* A function declaration alone with empty list identifier specifies > * that no information about the number or types of the parameters is > * supplied. > */ > int bar(); > > void test(void) > { > foo(1); /* should fail */ > bar(1); /* should be ok */ > } > > --- >8 --- > > Gcc compiles this without any warnings (gcc -Wall -std=c99 -c func.c). > However I would expect gcc to issue an error or a warning since 'foo' > function takes no parameters (in my understanding).
That might be a good idea, but it does not have to. Calling a
function with the wrong parameter list is not a constraint violation
unless there is a prototype in scope. Add (void) to the function
definition and declaration and both will act as a prototype as well.
gcc will then warn you that they are being called incorrectly.
Add -Wstrict-prototypes and you will be told that neither the
definition of foo nor the declaration of bar acts as a prototype. I
always have this warning on since it lets me know that I have written
a function whose calls won't be checked.
--
Ben.
|
|
|
|
|
|
|
Guest
Posts:
n/a
|
On Nov 26, 4:21*pm, Ben Bacarisse < [email protected]> wrote:
> Francis Moreau <[email protected]> writes: > > Gcc compiles this without any warnings (gcc -Wall -std=c99 -c func.c).. > > However I would expect gcc to issue an error or a warning since 'foo' > > function takes no parameters (in my understanding). > > That might be a good idea, but it does not have to. *Calling a > function with the wrong parameter list is not a constraint violation > unless there is a prototype in scope.
Ah ok, I thought it has to for definition case.
> *Add (void) to the function > definition and declaration and both will act as a prototype as well. > gcc will then warn you that they are being called incorrectly. > > Add -Wstrict-prototypes and you will be told that neither the > definition of foo nor the declaration of bar acts as a prototype. *I > always have this warning on since it lets me know that I have written > a function whose calls won't be checked. >
I'll do the same.
Thanks you
|
|
|
|
|
|
|
Guest
Posts:
n/a
|
On Nov 26, 4:20*pm, Eric Sosman < [email protected]> wrote:
> > * * *A definition is also a declaration, but the two roles are > not always in perfect lock-step. *`int foo() { return 1; }' > defines a function with no parameters, but as a declaration it > gives no information about the parameters. *(Almost none: we > know that foo has no "..." parameters, but that's all.)
Well still, reading 6.7.5.3p14:
"...An empty list in a function declarator that is part of a
definition of that function specifies that the function has no
parameters..."
it's still unclear if the function has no parameters for the
definition or as a declaration.
> > * * *This odd state of affairs may make just a little sense if > you think about the declaration you'd get by chopping off > foo's body and tacking on a semicolon: `int foo();'. *That > declaration gives (almost) no information about foo's parameters, > just like your declaration of bar. *That declaration, roughly > speaking, is the "external appearance" of foo; the additional > details are "private" to the definition itself.
ok I think I got your idea, but I'm still not sure this is equivalent
to 6.7.5.3p14.
But it's a good way to remember the rule.
> > * * *Recommendation: Don't write old-style function definitions > in new code, and rewrite old-style to new-style whenever you have > a good reason to visit a module containing them. *Note that it is > not always possible to write a portable prototype for an old-style > function: in `int barf(s) unsigned short s; { return s >> 3; }', > an `unsigned short' argument promotes to `int' on some platforms > and to `unsigned int' on others, so we don't know which to write > in a prototype. *Do a full change-over and all will be well.
Indeed.
Thanks
|
|
|
|
|
|
|
Guest
Posts:
n/a
|
Francis Moreau <> writes:
> On Nov 26, 4:20Â*pm, Eric Sosman <[email protected]> wrote: >> >> Â* Â* Â*A definition is also a declaration, but the two roles are >> not always in perfect lock-step. Â*`int foo() { return 1; }' >> defines a function with no parameters, but as a declaration it >> gives no information about the parameters. Â*(Almost none: we >> know that foo has no "..." parameters, but that's all.) > > Well still, reading 6.7.5.3p14: > > "...An empty list in a function declarator that is part of a > definition of that function specifies that the function has no > parameters..." > > it's still unclear if the function has no parameters for the > definition or as a declaration.
I think Eric's words are a little confusing. Let me try to be clear.
There are four things to consider:
1. int foo() { return 1; }
2. int foo();
3. int foo(void) { return 1; }
4. int foo(void);
All four act as declarations of the function but only 1 and 3 are also
definitions. 3 and 4 also provide a prototype whereas 1 and 2 do not.
2 says the least. As a declarator with an empty parameter list that
is *not* part of a definition is says nothing about foo's parameters.
1, 3 and 4 all say that foo has no parameters but calls to foo with a
parameter will only be diagnosed in the case of 3 and 4. A compiler
is permitted to complain about foo(42) after seeing 1, but to do so it
need to distinguish between 1 and 2 because foo(42) is *not* wrong
after seeing only 2. I don't know a compiler that does this. For one
thing, it would involve a lot of effort being put into old-style
definitions.
<snip>
--
Ben.
|