首页 > 英文翻译 > int main() vs. void main()

int main() vs. void main()

2011年11月25日 发表评论 阅读评论 11,695 次浏览

原文:http://www.eskimo.com/~scs/readings/voidmain.960823.html
作者:Steve
题注:只翻译了前面这一半讲原理的,后面的内容就暂不翻译了。

[This article was originally posted on August 23, 1996, and again with annotations on January 14, 1997 and March 20, 1998. I have edited the text further for this web page.]
[这篇文章最开始于1996年8月23号发布,后又于1997年1月14号和1998年3月20号对其做了注解说明。我已经为本网页做了进一步的文本编辑。]

[A few of the points I make in this article are becoming dated. It is becoming less and less kosher to assume that a function declared without an explicit return type will be implicitly declared as returning int; or to fail to return a value from a non-void function. In fact, the new revision of the ANSI/ISO C Standard (“C99″) actively disallows the first of these, and at least partially the second.]
[我在这篇文章里提到的一些观点目前来看已经变得过时。假定一个未明确指定返回值类型的函数将默认返回int类型,或者是对一个非void返回值类型的函数不进行值返回操作都将变得越来越不正确。事实上,在ANSI/ISO C标准的最新修订版本(“C99″)中,已经明确禁止了前面提到的第一种情况,而对于第二种情况也进行了部分禁止。]

Newsgroups: comp.lang.c
From: scs@eskimo.com (Steve Summit)
Subject: Re: int main() ???? wrong?
Message-ID:
References: 新闻组: comp.lang.c
来自: scs@eskimo.com (Steve Summit)
主题: Re: int main() ???? wrong?
消息-ID:
引用:

In article , sidlip@lip.microserve.com writes:
> I see alot of reference to int main () being incorrect syntax..
> Ive gotten into the habit of doing it and would like to know what
> errors could pop up in my code from not voiding main.
在文章 , sidlip@lip.microserve.com 写到:
> 我看到有很多地方提及 int main () 是一种不正确的写法。
> 我就经常习惯这么写,并且很想知道
> 在我的代码里将从非void main函数那引发出什么样的错误?

I assume you mean, “what errors could pop up from not declaring main correctly.” Speaking generally, the same sorts of errors could pop up as occur any time a global variable or function is misdeclared.
我猜想你的意思是:没有正确声明的main函数将会引发什么样的错误?一般来说,在任何时候使用未正确声明的全局变量或函数的同时就会引发一系列错误。

Turning for a moment from main() and void to ints and doubles, let’s consider the (incorrect) code

	#include <stdio.h>
	extern int sqrt();
	main()
	{
	printf("%d\n", sqrt(144.));
	return 0;
	}

先放开main()和void的话题讨论,看看ints和doubles的情况,考虑如下一段不正确的代码:

	#include <stdio.h>
	extern int sqrt();
	main()
	{
	printf("%d\n", sqrt(144.));
	return 0;
	}

This code is clearly trying to compute and print the square root of 144, but it almost as clearly will not work. It declares that the library function sqrt() returns an int, and it tries to print sqrt’s return value using %d which expects an int. Yet, in actuality, sqrt() of course returns a double. The fact that sqrt() returns a double, while the caller acts as if it returns an int, will almost certainly prevent the program from working.
很明显,这段代码是尝试计算并打印144的平方根,但同样明显的是它无法正常工作,它把库函数sqrt()申明为返回int类型值,并且尝试通过使用接收int类型变量的格式串%d打印这个返回值,然而实际情况是,sqrt()返回一个double类型值。sqrt()返回double类型值与调用者却当它返回int类型值这个前后不一致的情况,几乎毫无疑问的会导致程序将无法正常工作。

Why won’t it work, exactly? It depends on the details of the particular machine’s function call and return mechanisms. We haven’t said which machine we’re using, and we shouldn’t have to know the details of these mechanisms (they’re the compiler’s worry, and to worry about them for us is one of the reasons we’re using a higher-level language like C in the first place), so we can speak only in general terms. Perhaps the machine has one general-purpose register which is designated as the location where functions return values of integer types, and one floating-point register which is designated as the location where functions return values of floating-point types. If so, sqrt() will write its return value to the floating-point return register, and the calling code will read a garbage int value from the general-purpose return register. Or, perhaps integer and floating-point returns use the same locations. sqrt() will write a floating-point value to the location, but the calling code will incorrectly interpret it as an integer value. You’d then get approximately the same result as you’d get from executing

	double d = 12.;
	int *ip = (int *)&d;
	printf("%d\n", *ip)

为什么它不能正常工作,具体情况是如何呢?这取决于特定机器的函数调用与返回机制的相关细节。我们没有说我们使用的是何种机器,我们也无法获知这些机器的详细情况(这些详细情况应该是编译器关注的内容,编译器为我们关注这些详细情况是因为我们可能一开始就在使用像C这样的高级语言。),因此我们这里仅从一般意义上去讨论。也许机器有一个被用来作为存放函数整型返回值位置的通用寄存器,还有一个被用来存放函数浮点返回值的浮点寄存器,如果是这样的话,函数sqrt()将把它的返回值写到浮点寄存器内,但调用函数却将从通用寄存器读到一个int类型的垃圾数据。亦或者,虽然整型返回值与浮点型返回值都使用同一个存放位置,但是函数sqrt()把一个浮点返回值写进去,而调用函数却以不正确的整型类型解析读取它。你将获得一个和执行如下代码相类似的结果:

	double d = 12.;
	int *ip = (int *)&d;
	printf("%d\n", *ip)

In both cases, the bit pattern of the floating-point number is interpreted as if it were an integer. Since the bit-level representations of integer and floating-point data are invariably different, confusing and inaccurate answers result. (Note that in neither case — the incorrect declaration of sqrt(), nor the int/double pointer game — does the compiler emit any code to do double-to-int conversion. Any such conversions have been effectively circumvented by the peculiarities of these two code fragments. In the case of calling sqrt(), you’d get a proper double-to-int conversion only if sqrt() were properly declared as returning double, and the return value were assigned or cast to an int.) Finally, suppose that values are returned on the stack, but that sizeof(double) is greater than sizeof(int) (as is usually the case). sqrt() will push a double-sized result on the stack, but the caller will pop an int-sized one. Not only will the caller print a garbage answer (interpreting those bits it did pop as if they represented an int), but the shards of the double remaining on the stack could screw later uses of the stack up enough that the program could crash.
在前面提到的这两种情况下,浮点数的位模式都被当成整型来解析,而既然整型数据的位表示与浮点数据的位表示总是不同的,就导致了混乱的错误结果。(请注意在未正确的sqrt()函数申明与使用以及整数/浮点数这两种情况下,编译器都不会加入任何进行浮点型到整型转换的代码,而这两段独特的代码实际上也避免了进行任何这样的类型转换。在sqrt()函数调用这一示例,只有当sqrt()被正确申明为浮点类型返回值的函数并且把返回值赋值或显示转换到int类型变量时,你才会获得正确的浮点到整型的转换。)最后,假设函数返回值是通过临时存放到栈空间来进行返回的,sqrt()函数会将一个占double大小的结果压入栈,而调用者却只从栈上弹出一个int大小的值,如果sizeof(double)大于sizeof(int)(一般正常情况下都是如此),那么不但调用者将打印一个垃圾数据(以整型来解析从栈上弹出的int大小的比特位),而且残留在栈空间里的剩余的原double比特位将使得后面的栈空间使用变得混乱而足够导致程序崩溃。

In the preceding example, the calling code was incorrect, because it misdeclared the return value of the sqrt() function. We’re not allowed to choose what we want the return value of sqrt() to be, because neither the defined return type of sqrt() (as fixed by the Standard and long practice) nor the actual return type of sqrt() (as implemented in the library provided with our compiler) are under our control. Issuing an external declaration for sqrt() declaring that it returns an int does not make it return an int. (Nor does the misdeclaration instruct the compiler to convert sqrt’s return value from double to int; if the declaration is wrong, how could the compiler even know that the type to be converted from was double?) Abraham Lincoln used to ask, “If we call a tail a leg, how many legs does a dog have?” His answer was “Four — calling it a leg doesn’t make it one.”
在前面的例子中,调用代码是错误的,因为它没有正确的申明sqrt()函数的返回值类型。我们没有被允许随意的选择我们想要的sqrt()函数返回值类型,因为sqrt()函数的返回值类型定义(被标准和惯例所固定)和sqrt()函数实际的返回值类型(由我们的编译器提供的库所实现提供)都不受我们的控制。外部申明sqrt()函数返回一个int类型值并不能使其真正返回int类型值。(这个不正确的函数申明指令也无法使得编译器能够将sqrt()函数的double类型返回值转换为int类型。既然申明是错误的,编译器又怎么知道是要从double类型转换到int类型呢?)亚伯拉罕·林肯曾经问到,“如果我们把尾巴叫做腿,那么狗有几条腿?”他的回答是“4条,把尾巴叫做腿并不意味着尾巴就是腿。”

When we write an implementation of main() with a return type of void, the situation is similar, but the roles are reversed. Now, it is the caller that is fixed and beyond our control, and that caller is assuming that main() returns an int. You may imagine that somewhere (it’s actually in the compiler vendor’s source for the C run-time library) there is some code which looks something like this:
当我们编写实现一个返回值类型为void的main()函数时,情况与此类似,但是调转了角色,现在是固定的调用者不在我们控制之下,而那个调用者假定了main()函数将返回一个int类型值。你可以想象在一些地方(事实上就在编译器提供商的C运行时库源码里)的代码看起来像如下这样:

	extern int main(int, char *[]);
	int argc;
	char *argv[MAXARGS];
	int status;
	...
	status = main(argc, argv);
	exit(status);

If the caller declares main() as returning int, and you define main() as returning void, the declarations are mismatched, just as the declarations of sqrt() were in the previous example. In theory, the resulting program can fail in just the same sorts of ways. But, to reiterate, here we can’t fix the problem by fixing the caller (because the caller is, er, fixed). Instead, we have to fix main’s declaration, which is under our control, to match the caller’s expectation.
如果调用者把main()函数申明为返回int类型,而你把main()函数实现为返回void类型,那么申明和实现就出现不匹配了,就如同前面例子里的sqrt()函数申明。理论上来说,编译生成的程序也会以(前面说描述的)同样的方式运行失败。但是,需要反复说明的是,在这种情况下我们无法通过修改调用者来修复该问题(因为调用者是,额,固定的)。作为替代方案,我们必须通过修改main()函数的申明以匹配调用者的预期来解决该问题,而这我们可以做到。

Even if the program with the misdeclared main() “works” (that is, compiles without error, and runs without crashing), it does result in a garbage (random) exit status being returned to the calling environment. You or your command invocation environment may not be noticing that particular glitch right now, but it is a glitch, and it may bite you later.
即便未正确申明main()函数的程序能够正常工作(也就是说,编译无错误,执行没崩溃),但它的确导致把一个垃圾(随机)退出状态码返回给了调用环境。你或者你的命令行环境现在还没有注意到这个特别的小差错,但它的确就是一个问题并且可能在迟些时候再困恼你。

At one time, I had not gotten into the habit of making sure that main() called exit() or returned an explicit value. (I wasn’t declaring main() as void, but I was getting the same sorts of random exit status values.) Whenever I wrote a little program (usually a special-purpose preprocessor) to automate some step in the building of a large program, and stuck the program into one of the productions in a Makefile, make would randomly abort the build, saying that there had been an error, whenever the random status returned by the little program was nonzero (i.e. not EXIT_SUCCESS). Eventually, fixing enough of those drove home to me the importance of always exiting with an appropriate, explicit status, even in seemingly trivial programs.
以前,我也没有养成确保main()函数显示调用exit()函数或返回一个确切值的习惯。(我没有把main()函数申明为void,但我同样获得一个随机的退出状态值。)每当我为超大应用程序的自动化编译写一些小程序(通常是一个针对特定目的的预处理程序)并把它们作为工具使用在Makefile中时,make经常随机性的异常中断编译过程并提示发生了一个错误,而此时往往是由于小程序返回了一个非零的随机状态值(即,非EXIT_SUCCESS)。最后,这些经验足够让我认识到应用程序总是以一个恰当的、明确的状态值退出的重要性,即使是一些看似不重要的应用程序。

Another place you’ll notice the effect of a random exit status is if you run a program with one in the background using a job-control shell under Unix; when it finishes, you’ll get a message like “Done(1)” or “Exit 1″ which, if you’ve gotten used to the presence of the number indicating the presence of an error, will mislead you into thinking that the command failed.
另一个你可能会注意到随机退出状态值影响的地方是,如果你在Unix下使用作业控制shell来后台执行一个应用程序,并在它执行完毕时,输出一条类似”Done(1)”或”Exit 1″的消息,如果你习惯通过判断程序退出状态数值来标志是否发生了一个错误,那么这将可能误导你,使你认为这条命令执行失败。

转载请保留地址:http://www.lenky.info/archives/2011/11/340http://lenky.info/?p=340


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

分类: 英文翻译 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.