| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1465 人关注过本帖
标题:高手指教。这句话怎么理解?
只看楼主 加入收藏
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
原則上說,我們應該講道理,不是講規則。對main()函數要返回值,返回0的時候,大家都沒有任何問題,但問題是如果真的要返回非零,應該返回什麼?你說打開文件失敗,返回-1還是1?好的,假設你說,應該返回-1,那麼操作系統是否認可你返回的-1意味著打開文件失敗?你有跟操作系統簽訂這類錯誤碼協議嗎?操作系統有規定你什麼錯誤返回什麼碼嗎?事實是沒有!那麼,你除了零之外,還能夠返回什麼?如果都是零,規定不規定又有什麼關係呢。Windows系統有數萬條內部錯誤碼(我們不可能全部都熟悉,何況那個碼表是會變化的),但仍然有保留空間給用戶自定義錯誤碼,那部分自定義的代碼,就是操作系統無法處理的,你返回給它,也是沒用,因為它根本就不知道那是什麼意思。只有在我們編排操作系統,如果這個程序返回什麼那麼就接著做什麼,那這種返回碼才是有用的,此時相當於程序未完成,接著寫,調用別的模塊而已,還是返回在程序內部寫函數的模式,因此main()的返回本質上跟普通函數的返回是一樣的,這就是1樓那句話的意思。

雖然我沒看過C89或C99的文本,但照道理上說,main()有返回值,應該只是“推薦”或“建議”級別,而不是“必須”,更不是“禁止(不返回)”。void main(void)並不是錯誤,不必總盯著這個來說。其實,這個規定也是違反C語言常規的,0在C語言中意味著假(false),但在這裡卻表示成功(true),意義完全相反,在Windows API函數,才會用==0表示成功,也用BOOL TRUE值表示成功,最好避免用if(*p)之類,用if(*p!='\0')更好。因此,糾纏這樣的問題,不是挺搞笑的嗎?如果C99那麼重視main()的返回值,何必引入bool類型,傳統C就能處理的了。


[ 本帖最后由 TonyDeng 于 2012-3-1 14:00 编辑 ]

授人以渔,不授人以鱼。
2012-03-01 13:42
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:0 
ISO/IEC 9899:1999 (E)
5.1.2.2.1 Program startup
1 The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
        int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
        int main(int argc, char *argv[]) { /* ... */ }
or equivalent; or in some other implementation-defined manner.

5.1.2.2.3 Program termination
1 If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument; reaching the } that terminates the main function returns a value of 0. If the return type is not compatible with int, the termination status returned to the host environment is unspecified.

大意如下:
5.1.2.2.1
1 在程序开始执行时,调用一个被称作 main 的函数。实现不为这个函数声明原型。它应被定义为:int 返回类型,且没有参数,即 int main(void),或者是带两个参数 ......
5.1.2.2.3
1 如果 main 函数的返回类型是兼容 int 的类型(在这里就是指应用整型提升(integral promotion)规则之后可以变为 int 的类型,如 char 之类的),则从 main 函数中返回等价于以该返回值做参数调用 exit 函数(因为标准在这之前已经详细阐述过了 exit 的作用)。到达终止 main 函数的 } 时返回 0 值。如果返回类型与 int 不兼容,则返回宿主环境的终止状态是未明确的。(就是说这是一种未明确行为。C语言标准里有三种行为,分别是标准明确,未明确和未定义。讨论过很多次了,不知道是什么意思的翻翻老帖,或者自己百度。)

另外 C++ 也有类似的规定。不过比 C 标准里规定的这些情况还要复杂一些。


[ 本帖最后由 pangding 于 2012-3-2 01:16 编辑 ]
2012-03-02 01:09
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:0 
另外 TonyDeng 好像混淆了错误码和退出码之前的关系。
任何错误码都是内部逻辑,但退出码是外部接口的一部分。

内逻辑只有内部用,外部的人不用管,当然怎么用都可以。除非你愿意做内部成员的一部分,那你就必须遵守一致的内部码。
C 语言里 errno.h 规定的那些码就可以视做内部码。它在标准库里使用,不过很遗憾,C标准明确规定的 errno.h 里的宏太少了。所以绝大多数实现用了其它一些标准里规定的东西,或者是自己另辟溪径了。不过无论如何,它都详细了声明了,也不是瞎用的。而且需要遵守标准里的其它一些约定,比如要遵守 perror 的要求。但是如果你不用标准库,而是自己写东西,这些错误码就完全不必操心。

另外外部接口的概念,大家应该是很清楚了吧。
就好像 printf 就可以看成 C 语言的一个接口。你可以没听说过这个东西,但谁用谁就得知道。它的功能是固定,是要明确告诉使用者的。同时使用者也必须遵守约定,比如用 %d 的时候要传一个 int 过去。另外就算你知道 printf 用 %d 输出 int,你也并没有义务知道 scanf 用 %d 输入 int。如果你只关心输出的话。
工具的退出状态是它的外部接口,意思也差不多。每一个工具的返回值是有明确意义的。返回 1 是什么意思,返回 2 是什么意思都,要明确地在手册里说清楚。你不关心是你的事,但关心的人要有能力了解到这些东西。不同的工具用不同的退出码指明相同的状态,或者用相同的退出码指明不同的状态是它们的自由。

所有的工具,或者接口都是给人提供服务的,谁用谁知道。操作系统当然不管,也不用和操作系统约定。
操作系统的主要工作是分配资源。它巴不得一个程序结束了,好赶紧回收它占用的资源呢。操作系统只想做好自己的本职工作,并不关心你调用那个软件是为你做的什么服务的。但是当你关心那个程序的退出状态时,你有权询问系统,而且系统必须做答(虽然它不知道这之间有什么逻辑)。
2012-03-02 01:40
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:0 
从操作系统不关心返回值的这个意义上讲。main 的返回值比一般函数的返回值还没意义。因为最起码一般函数的返回值,我们还是很关心的。
2012-03-02 01:45
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
现在讨论的是“返回值”。返回值有两种类型,一是返回所请求的东西,比如getchar()应该返回读入的数据;二是返回结果信息,即这个函数的退出码,通常用错误码作为退出码,或者用逻辑值表示真假成败。在传统汇编和C中,因为资源紧张,历来采用返回非法数据标识错误状态的手法,getchar()就是其中之典型。

在普通函数中,如果没有返回值(void函数),那么这个函数不能被赋值,比如void max(int* x, int*y),就无法使用z = max(&x, &y),此时,函数的结束状态未定义,语句只能是单独的一行max(&x, &y),这样的未定义行为是很常见的。即令函数有返回值,结束状态有定义很明确,我们往往也没有使用,比如printf()函数的返回值,估计从来没有人用过,甚至连这个函数有返回值都可能不知道。没有返回值的函数返回状态未定义,与有返回值的函数返回被舍弃返回值,在程序中的行为都是一样的,在操作系统中连续调度程序序列中也是一样。

最简单函数执行完毕的表示,无非是成败与否,那是逻辑值。在C中,逻辑值兼容int值(其实传统C没有逻辑类型,在早期,到底是用0/1还是0/!0,有过一番纠结,选用0/!0是一种习惯而已,这种情况在后来产生了麻烦,故现在的编译器要强制0/!0转化为0/1),故此标准要求返回值必须是可兼容提升为int类型的。返回零,其实是返回一个逻辑标志,它以成功只有一种而失败原因可以无数的理由,只能选用唯一值为零来表示成功,用无数的可能值非零来表示失败,同时也兼用于错误码。对函数或程序逻辑,除非真有错误码,事实上就只应返回true或false,而不是0/!0。标准的main()定义,要么是int main(...),要么是bool main(...),而void main(...)也不见得是非法。在程序的设计者不希望被赋值时,就刻意不返回任何值。设计wait()函数,等待按任意键继续,就不需要任何返回值。对这样功能的函数或程序,你希望它有什么有定义的返回状态?事实上,它返回什么都是误导性质的,逻辑上它就不应该有返回值!

在现在的Windows API和C++编程中,对返回值的类型就区分得很清楚。要返回有意义代码的,就返回int(甚至更清晰地用errno_t表示),要返回成功与否的,就返回bool,确实不需要返回值的,就void。将bool和char与int混在一起搞大杂烩的,是C。其实在编程规范建议中,都不主张写'A'+32这类的代码,只是很多C主义者要写成这样显得知识多罢了。在程序中,main()如果确实没什么需要返回的,就不返回,没必要强制返回什么,这是逻辑问题,生造一个返回值,反而失去严谨性。

[ 本帖最后由 TonyDeng 于 2012-3-2 13:49 编辑 ]

授人以渔,不授人以鱼。
2012-03-02 13:22
chan_
Rank: 3Rank: 3
来 自:武汉
等 级:论坛游侠
帖 子:84
专家分:122
注 册:2012-2-29
收藏
得分:1 
来学习的,字好小啊
2012-03-02 14:07
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
其实,“未定义”并不可怕,只要我们不使用就行。变量或指针未初始化,是没有问题的,但使用之前必须初始化。在函数的开始声明char* s,这个指针是未定义的,到中部才s = &x,那么这个未定义变量就一直潜伏在这中间的代码地带,这种危险,在传统C中太多了,因为没有办法,它的变量声明或定义必须在函数开始,但不也是大量在用吗。新标准,吸纳了C++的做法,容许在使用的最近位置声明或定义变量,无非是这个原因。诸如i++,++i的运算顺序这个经典问题,也一样是未定义行为,但只要我们不使用这样的代码,未定义行为也没有什么可怕之处。main()函数没有返回值,宣称有未定义状态,但我并没有指挥操作系统使用这个程序的返回值,未定义又有何问题呢,如果我写一个程序,希望操作系统有所行为,那么必定要跟它沟通好做出合作协议,其中包括返回值。所以,这些都不可能是“必须”或“禁令”,我相信,通观标准的文本全部,应该能够找到那部分文字是属于“推荐”级别的信息——如果是禁令,就不可能有某些编译器容许而另一些不容许的情况出现。

其实,以C和Unix的关系,初衷是C程序都是操作系统内核的一部分代码,故把程序的返回值视同程序内部函数返回值一样罢了。从现在的操作系统观念和软件角度看,这个规定纵使是“必须”(何况未必真是必须的),也无需过于重视,它属于过时法律,必定要修改的。

[ 本帖最后由 TonyDeng 于 2012-3-2 14:24 编辑 ]

授人以渔,不授人以鱼。
2012-03-02 14:16
chan_
Rank: 3Rank: 3
来 自:武汉
等 级:论坛游侠
帖 子:84
专家分:122
注 册:2012-2-29
收藏
得分:0 
学习
2012-03-02 15:19
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:0 
TonyDeng 谈到的对 C 语言理解方面的很多话题我都不是很赞同。而且你貌似对未明确和未定义的理解也不是很正确。

不过不同的人有不同的理解也不是什么不可以的事。
很明显你对 C 的历史,C 与 unix 的关系,标准的意义之类的东西理解的不是很深。但这些东西其实没什么实用价值,不知道也挺好的。

从语言的角度、系统的角度、软件的角度看同一个问题,确实有不同的侧重点。反正我现在是觉得讨论的有点跑题了。

这个问题点到为止就行了,我想楼主也不需要知道这么多。
2012-03-02 18:18
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
不同意很正常,但你最终有一天必然会回到这个问题上来。意见说过就好了,坚持写return 0就坚持,void main()编译能通过也没问题。到此为止吧。

授人以渔,不授人以鱼。
2012-03-02 18:25
快速回复:高手指教。这句话怎么理解?
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.034654 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved