浅谈Unix和Linux下的int

网友投稿 976 2022-09-24

浅谈Unix和Linux下的int

浅谈Unix和Linux下的int

这也算和平同学的点题作文了,呵呵。

他对于Linux下很多时候,api函数大量使用int作为常用类型感到不解,问我,我呢,就试着回答一下哈。

原帖在这里:primer>建议,表示像“容量”这样的变量时,因为不可能出现负数,所以建议用size_t类型。但是linux下的程序,好像比较喜欢使用int。

为什么不使用size_t(或者unsigned int)而使用int?表示范围不是少了一半吗?

我的回答:

嗯,这个问题算是比较偏门了,不过,我做过Windows开发,也做过Linux开发,两个平台都用过,没什么道理哈,仅仅谈点自己的感觉,嗯,不一定准确,大家有高见,还可以补充。

我觉得这个问题首先是一个文化问题。什么叫文化,就是做这类事情的人们的一个通常的共识,就是大家都习惯这么做事。我很早,嗯,07年开始转向Linux开发的时候,就发现这点不同。

Windows呢,是微软公司开发的,大公司,强调严谨的开发风格,大家可以从它推崇匈牙利命名法就看的出来。它对于变量命名,类型命名是有严格规定的,要求尽量准确,不给后来者留歧义。比如,很多Struct*,它会使用typedef显式定名为一种新类型PStruct来管理,这样,大家从字面上就可以一眼看出来,而不用到用的时候,跑去数星星。数星星很容易数错的。我就干过坏事,嘿嘿。

这个道理也很简单,微软是开发OS的,说白了,它的主要产品功能,除了Windows的操作功能,还需要提供大量的api给广大程序员用,没办法啊,如果没人给他的操作系统开发应用程序,他的OS卖不动的。

这就要求微软不仅仅关注终端用户的体验,也特别关注程序员用户的体验,而明示的api,显然是一种很好的用户体验,程序员不容易犯错误,被api的提供者约束着做正确的事情,程序员bug少,成功率就高,进而开发成本就低,于是,形成良性循环。

同时,这也为微软的客服部门减少好多投诉哦,大家换位思考一下,如果微软的api含糊不清,大家是不是要发飙?呵呵。

这里面体现出来一个很重要的思想,微软是把广大应用程序员,也作为什么都不懂的终端用户在看待,试图从api上构建最大的开发友好度,因此,它对于命名法很严格,api定名表意很清晰,同时,对于各种变量、类型,不厌其烦,多次定义,为了是让各个行业的程序员, 用起来都尽量贴合自己的行业习惯。

这是有道理的,比如我现在在电力系统,我们定义变量类型,喜欢用Int16、Int32、Int64,Float16、Float32、Float64这类命名,int、double这些C/C++基本类型,反而不太流行。为啥,很多时候工业现场的人,不知道你这个类型到底是多少bits的,就会出错。这是为了用户看着清晰,也是行业习惯,所以,我很多时候开始写程序前,都要先去定义这么一批新的变量类型,方便和同事们沟通代码啊。

而Unix呢,不太一样,我看过《Unix编程艺术》这本书,这里面讲了很多Unix程序员的文化。这么说吧,我简单点,Unix的开发者,默认使用者是和其水平相当的程序员,大家所有沟通的语言,都是计算机本专业的,大家很多时候用默认,暗示,就好了。

所以,Unix下的习惯,没有那么多分门别类的变量类型,大家还有个好习惯,呵呵,int包打一切。我看到几乎大多数Unix的函数,就是gcc的基本库,函数老是int来,int去,其实,给我的感觉,在Unix下,什么都是int,为啥,Unix很讲究把同类资源数组化管理,比如打开的文件句柄,就是一个整数,进程ID,整数,线程ID,整数,用户ID,整数,甚至,设备都是整数表示。嗯,socket不说了,Windows下是一个特定数据类型SOCKET,而Unix下,你猜对了,没错,int,整数。

这叫什么?其实是向量化管理,在系统内部检索的时候,可以想象,Unix系统的开发者,大量使用数组,利用int这个整数在哈希检索目标,达到效率最高。反正,我们不管什么资源,在Unix内部,就是一个int型的ID表示,这其实已经是Windows句柄的概念了。

这两种命名方法,其实各有优缺点,Windows的变量类型多,程序员学习的时候,成本高,但是,学会了就不容易出错。而Unix的学习曲线低,没那么多类型名要去背,不过呢,用起来出不出错,自理啊,Unix的开发者,相信大家的实力。嗯,只是我自己有点信不过我自己,嘿嘿。

不过要我说呢,还真说不好谁对谁错,其实都有道理,关键看OS的设计者,心目中的目标用户的水平如何了。

回到和平同学的问题,用int,少一半,其实是有道理的,因为int是有符号的,有一半的表示范围是负数,在表示很多容量的时候,比如说吧,malloc的数组,或者socket的表示范围,确实负数没有意义。因此,看起来被浪费了。

这其实不然,为什么,原因很简单。

int就算少一半,你能用多少?和平同学别见怪啊,我说句话,你问这个问题就表示还有点学生思维,总是想×××,就是我要把所有的资源都纳入我的管理,呵呵。其实我们做工作做久了的,对于数据的边界、范围,反而有个思想,够用就好,没必要多。

int表示少一半,2G有吧,嗯,大多数用户的数组,可不可能超过1G?其实,大多数时候,我们在这个地方用int,表示的范围都很少的。少一半也够用。socket不说了吧,理论上只有65536,int再少几半也够用,呵呵。

够用就行了,不用担心的,真要是需要用大表示,自己做unsigned long来表示,也是可以的。

而且,Unix这么设计,有个最大的优点,就是数据的自描述特性得到了前所未有的发挥。我们都知道,在表示范围、容量的时候,int只使用了正数部分,那负数部分做什么?我来回答你,表示非法值,这个很重要。

试想一下,如果我们用的数据类型里面,无法表示非法值,那麻烦了,api设计的时候,必须单独设计一个参数,来传递非法值,这又涉及到&传址调用,或者*直接传指针调用,程序设计复杂度直线上升,设计的人也累,学的人也累,用起来更累。

这么说可能不直观,我举个例子:

比如我要设计一个read函数,从某个文件id读取一段数据,大家注意啊,Unix下,文件ID可以表示任何串行化设备的。包括socket,可以用resv这类伯克利socket api,也可以直接使用C基本库的read,都对,因为使用int表示,Unix可以把所有串行化设备统一编址处理,用一类函数处理完。

函数原型我这么设计,设计了两个,大家比较一下:

Code: int ReadFrom(int fd,char* szBuffer,int nBufferSizeMax);       unsigned int ReadFrom(int fd,char* szBuffer,int nBufferSizeMax);

可以吧,这虽然用了匈牙利命名法,但是,这是标准的C函数,没错吧。嗯,第一个应该是Unix的习惯,用int,第二个应该是Windows的习惯,用了无符号正整数,好,我们来看一种情况。

如果我们读失败了,要返回一个错误信息给上层调用者,怎么办?

第一个很简单,由于其只使用了int的正数部分来表示读成功多少字节,那好,-1来表示失败就好了。

第二个就麻烦了,由于它只返回正整数,上层看见都都是对的,它没有办法返回一个错误标示。说白了,这个api设计很失败,无法满足所有的应用返回需求。那怎么办?也有两种做法:

Code: unsigned int ReadFrom(int fd,char* szBuffer,int nBufferSizeMax,bool& bSuccessFlag);    #define READ_FAIL 0xFFFFFFFF    //出错返回READ_FAIL    unsigned int ReadFrom(int fd,char* szBuffer,int nBufferSizeMax);

大家别说用0啊,很多时候,Read到0,不是错误,是一种正常的状态,比如socker的read,read到0Bytes,很可能是对方没有发送,并不是说socket失败了,需要重建链路。

嗯,大家再来看看,第一种,没办法,只有多设计一个传址的参数,让函数返回是否成功读取的标志,上层来决定怎么处理。

第二种呢,显式定义一个返回值为错误,这个返回值永远不被理解为正确的值,就是正确业务不使用,仅供返回错误。

大家去比较一下Win32api,里面很多用的都是第二种方法。

不过,这样一来,大家觉得麻烦不麻烦,第一种,起码要多做一个参数,程序员学习量增加,记住哦,一个函数好学,100个、1000个函数让你背,你就觉得这个bSuccessFlag很讨厌了。

第二个更无礼,首先,它限制了业务,导致了业务缺陷,至少,unsigned int表示的最大值,被用来做fail了,就是业务不能用了,其次,多了个宏,同样的返回值unsigned int,需要区别对待,大家说用起来麻烦不麻烦?

别怕麻烦啊,呵呵,去看看Win32Socket的函数,它的SOCKET就是用了unsigned int表示的最大值,作为错误的SOCKET标识,其实就是按照上面第二种办法设计的api,没办法,谁让他把SOCKET定义为无符号正整数呢。自己给自己下绊子,呵呵。

嗯,还有什么beginthreadex,CreateWindowsEx,这类函数,大家去看看HANDLE的数据定义,就知道为什么参数设计得那么繁琐了。

很多时候,我们看api好用不好用,其实就看这些细节。

这里多说一点吧,正是因为Unix和Linux下,很多api函数都用int做返回值,-1已经被公认为非法,失败的标志,大家用起来反而简单,一般说来,看见一个Unix的api函数,猜都猜得出它怎么表示返回值,反正都是int嘛,-1就是失败啦,0和正数都是成功。Windows下就麻烦了,查MSDN都要查半天,主要看它数据类型,以及相应的接口定义。

因此,别看Windows做了很多努力,做了很多程序员友好度的工作,有时候啊,我评论一句,还真不如Unix什么都丢给程序员。

不过呢,也不好都说不对,如果应用程序员不是计算机界的专业人士,比如说,一个其他专业的工程师,需要临时写一小段程序解决个问题,Windows能保证他不出错。为啥,数据类型定义错了,编译都过不去,自己查MSDN去。

所以,说来说去,我认为这个问题还是一个文化问题,两种平台的开发文化,导致了今天的局面。说不上谁好谁坏。

大家看着用吧。

嗯,我私人呢,喜欢Windows的匈牙利命名法,喜欢严谨的定名,但是,不是很喜欢它那么多数据类型,还是喜欢int,呵呵。

很多时候,我设计的api大家觉得有点怪,其实就来自于此,又用Windows的命名法,但是,api函数参数设计却更靠Unix一些,算两面的综合吧。

《0bug-C/C++商用工程之道》里面,我的工程库用的就是我的习惯,两种风格兼而有之,因为我觉得,不管白猫黑猫,抓到耗子就是好猫,哪个地方合用用哪个,哪个办法合用用哪个,没什么门户之见的。

这不绝对啊,上述仅仅是我个人的习惯,大家还是自己选合用的办法好了,没必要和我一样。

肖舸

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:RedHat系统常见的日志文件详解一
下一篇:不容易!亚冠战罢第三轮中超球队终于拿到1分!(亚冠中超出局)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~