欢迎
游客
,
注册
|
登录
|
会员
|
界面
|
简洁版本
|
在线
|
帮助
商都网教育宝典宝库
商都网教育宝典宝库
计算机
等级考试
C语言辅导:ANSI/ISO标准
本主题被查看843次, 共1个帖子, 1页, 当前为第
1
页 选择页数: 1 跳转到第
页
上一主题
下一主题
标题: C语言辅导:ANSI/ISO标准
huahua
-[尕硴]
超级版主
UID: 71
来自:
精华:
130
积分: 14003
帖子: 12909
注册: 2007-10-22 17:59:00
状态:
离线
威望: 444.00
金钱: 3355.00 元
发短消息
用户资料
树型
回复
引用
只看楼主
2007-11-01 11:21
C语言辅导:ANSI/ISO标准
如果你不理解C语言标准的价值,你就不会知道你是怎样地幸运。
一个C程序员会期望一个C程序无论是在哪里开发的,在另一个编译程序中都能通过编译。实际上不能完全做到这一点,因为许多头文件和函数库都是针对某些特定的编译程序或平台的。有些(很少!)语言扩充性能,例如基于Intel的编译程序所使用的near和far关键字以及寄存器伪变量,也只不过是某种平台的开发商们所认可的一种标准。
如果你认为靠一种标准走遍天下是理所当然的,就象左脚踩加速器,右脚踩刹车一样,那么你的视野未免有些狭窄。有两种不同的BASIC标准,但都没有得到广泛的支持;世界上最流行的Pascal编译程序并不符合正式的标准;现在正在发展的C++标准,由于变化太快,也没有得到广泛的支持;有些实现遵循一种严格的Ada标准,但Ada标准也没能大规模地占领世界市场。
从技术上讲有两种C语言标准,一种来自ANSI(American National Standard Institute,美国国家标准协会)X3J11委员会,另一种来自ISO(International Standard Organization,国际标准协会)9899—1990。由于ISO标准中的某些改进优于ANSI标准,而ANSI标准也接受了这个国际版本,因此"ANSI/ISO标准”是一种正确的说法。
那么,这种标准对你有什么帮助呢?你可以买到一份该标准的副本,即Herbert Schildt所著的((The Annotated ANSI C Standard》(Osborne McGraw-Hill出版,ISBN O-07-881952-O)一书,该书对语言和库都作了介绍,并带有注释。这本书比大多数正式标准要便宜多了,后者由ANSI和ISO出售,以解决建立标准所需的部分费用。并不是每一个C程序员都需要这样一本书,但它是最权威的。
最重要的一点是,ANSI/ISO标准是对“什么是c?”这一问题的权威解答。如果编译程序开发商所做的某些实现不符合这一标准,你可以把它作为错误指出来,这不会引起争论。
ANSI/ISO标准也不是包罗万象的。具体地说,它没有涉及c程序可能会做的许多有趣的事情,例如图形或多任务。许多兼容性不强的标准包含了这些内容,其中的一些将来可能会成为权威的标准,因此你不必完全拘泥于ANSI/ISO标准。
顺便提一句,除编程语言之外,还有许多东西也有ANSI标准,其中的一种就是ANSI为全屏幕文本操作的退出序列集合而写的标准,在第17章中所介绍的MS—DOS的"ANSI驱动程序”指的就是这种标准(有趣的是,MS-DOS的ANSI.SYS只实现了ANSI标准序列中的一小部分)。
16.1 运算符的优先级总能起作用吗?
有关运算符优先级的规则稍微有点复杂。在大多数情况下,这些规则确实是你所需要的,然而,有人也指出其中的一些规则本来是可以设计得更好的。
让我们快速地回顾一些有关内容:“运算符优先级”是这样一些规则的集合——这些规则规定了“运算符”(例如+,-,等等)的优先性,即哪一种运算符先参加运算。在数学中,表达式“2×3+4×5”和“(2×3)+(4×5)”是等价的,因为乘法运算在加法运算之前进行,也就是说乘法的优先级比加法高。
在c中,有16级以上的运算符优先级。尽管这么多的规则有时使c程序不易阅读,但也使C程序写起来容易多了。虽然这不是唯一的一种折衷方法,但这就是C所采用的方法。表16.1总结了运算符的优先级。
表16.1 运算符优先级总结(从高到低)
----------------------------------------------------------------------------------
优先级 运算符
----------------------------------------------------------------------------------
1 x[y](下标)
x(y)(函数调用)
x.y(访问成员)
x->y(访问成员指针)
x++(后缀自增)
x--(后缀自减)--
2 ++x(自增)
--x(自减)
&x(取地址)
*x(指针引用)
+x(同x,和数学中相同)
-x(数学求负)
!x(逻辑非)
~x(按位求反)
sizeof x和sizeof(x_t)(字节数大小)
3 (x_t)y(强制类型转换)
4 x*y(乘法)
x/y(除法)
x%y(求余)
5 x+y(加法)
x-y(减法)
6 x<<y(按位左移)
x>>y(按位右移)
7 x<y,x>y,x<=y,x>=y(关系比较)
8 x==y,x!=y(相等比较)
9 x&y(按位与)
10 x^y(按位异或) .
11 x | y(按位或)
12 x&&y(逻辑与)
13 x||y(逻辑或)
14 x?y:z(条件)
x=y,x*=y,x/=y,x+=y,x-=y,<<=,>>=,&=,^=,|=(赋值,右结合性)
16 x,y(逗号)
--------------------------------------------------------------------------------------
优先级最高的是后缀表达式,即运算符跟在一个表达式后面;其次是前缀或单目表达式,即运算符位于一个表达式的前面;再次是强制类型转换表达式。
注意:关于运算符优先级,最重要的是知道*p++和*(p++)是等价的。也就是说,在*p++中,++运算符作用在指针上,而不是作用在指针所指向的对象上。象“*p++=*q++;这样的代码在C中是随处可见的,其中的优先级和“(*(p++))=(*(q++))”中的是相同的。这个表达式的含义是“q+1,但仍用q原来的值找到q所指向的对象;p加1,但仍用p原来的值;把q所指向的对象赋给p所指向的对象”,整个表达式的值就是原来q所指向的对象。在C中你会经常看到这样的代码,并且你会有许多机会去写这样的代码。对于其它运算符,如果你记不住其优先级,可以查阅有关资料,但是,一个好的c程序员应该连想都不用想就能明白*p++的含义。
最初的C编译程序是为这样一种计算机编写的——它的某些指令对象*p++和*p++=*q++这样的代码的处理效率高得令人难以置信,因此,很多C代码就写成这种形式了。进一步地,因为象这样的C代码实在太多了,所以新机型的设计者会保证提供能非常高效地处理这些C代码的指令。
再下一级的优先级是乘法、除法和求余(也叫取模),再往后是加法和减法。与数学中的表达式相同,“2*3+4*5”和“(2*3)+(4*5)”是等价的。
再下一级是移位运算。
再往后两级分别是关系比较(例如x<y)和相等比较(x==y和x!=y)。
再往后三级分别是按位与、按位异或和按位或。
注意:关于运算符优先级,再次重要(即在知道*p++和x=y=z的含义之后)的是要知道x&y==z和(x&y)==z是不一样的。因为按位操作的运算符的优先级低于比较运算符,所以x&y==z和x&(y==z)是等价的。这两个表达式的含义都是“先看y和z是否相等(相等为1,不等为0),然后让比较结果和x进行按位与运算”,这与“先让x和y进行按位与运算,再比较其结果是否等于z”相差甚远。有人可能会争辩,按位与运算符的优先级应该高于比较运算符,但为时已晚,因为相应的标准是早在二十年前被定义的。如果你想把按位与的结果与别的东西进行比较,你就需要使用括号。
再往后两级是逻辑运算符,例如x&&y和x||y。注意,逻辑与(AND)运算符的优先级高于逻辑或(OR)运算符,这与人们讲话的方式是一致的。例如,请看下面的代码:
if(have_ticket&&have_reservation
||have_money && standby_ok){
goto_airport();
}
这段代码的含义可以这样来描述:“如果你有机票并且预定了航班,或者你有钱并且可以买到备用票,那么你就可以出发去机场了。”如果你用括号改变优先级,你就会得到一种截然不同的条件:
/* not a recommended algorithm!*/
if(have_ticket
&&(have_reservation || have_money)
&&standby_ok){
goto airport ();
}
这段代码的含义可以这样来描述:“如果你有机票,并且你预定好了航班或者有钱,并且可以买到备用票,那么你就可以出发去机场了。”
再下一级是条件表达式,例如x?y:z。这是一个if-then-else结构的表达式,而不是一条语句。条件表达式有时可以使程序简洁,有时也会造成语意的模糊。条件表达式具有右结合性,也就是说
a?b:c?d:e
等价于
a?b:(c?d:e)
这一点与else—if结构很相似。
再下一级是赋值运算。所有的赋值运算符都具有相同的优先级。与C的其它双目运算符不同,赋值运算具有“右结合性”,即它是从右向左进行的,而不是从左向右进行的。x+y+z等价于(x+y)+z,x*y+z等价于(x*y)+z,而x=y=z等价于x=(y=z)。
注意:关于运算符优先级,次重要(即在知道*p++的含义之后)的是要知道x=y=z的含义。因为赋值运算具有右结合性,所以这个表达式等价于x=(y=z),其含义是“将z的值赋给y,然后再将该值赋给x”。象a=b=c=d=O;
这样的代码是很常见的,按从右向左的顺序,它把。赋给d,再赋给c,再赋给b,最后赋给a。
c中优先级最低的是逗号运算符。它连接两个表达式,先计算第一个表达式的值,扔掉后,再计算第二个表达式的值。只有当第一个表达式具有副作用时,例如赋值或函数调用,使用逗号运算符才有意义。逗号和赋值运算符经常在for循环语句中搭配使用:
for(i=0,count=O;i<MAX;++i){
if(interestmg(a[i])){
++count:
}
}
请参见:
1.6 除了在for语句中之外,在哪些情况下还要使用逗号运算符?
1.12 运算符的优先级总能保证是“自左至右”或“自右至左”的顺序吗?
1.13 ++var和var++有什么区别?
1.14 取模运算符“%”的作用是什么?
2.13 什么时候应该使用类型强制转换(type cast)?
2.14 什么时候不应该使用类型强制转换(type cast)?
7.1 什么是间接引用(indirection)?
16.2 函数参数类型必须在函数参数表中或紧跟其后的部分中说明吗?
函数参数必须在参数表中说明,除非你使用的是一种过时的编译程序,在这种情况下,你应该通过#ifdef指令来同时实现两种可能的说明方式。
定义函数有两种方法。例如,以fool()和foo2()这样两个函数为例,它们都以一个字符指针作为参数,并且返回一个整型值。假设它们是按如下形式定义的:
/* old style*/
int
foo1(p)
char *p;
{
/*body ot function goes here*/
}
/*new style*/
int
foo2(char *p)
{
/*body of function goes here*/
}
旧方式的唯一好处在于当参数表很长时它显得更美观。
新方式的好处在于它在提供函数定义的同时,还提供了函数原型。这样,在定义了foo2()以后,如果相同的“.c”文件中有对foo2()的调用,编译程序就会根据定义中的参数检查函数调用中的参数。如果参数不匹配,编译程序就会报告出现严重错误(标准并不要求有这一步,但大多数编译程序中都有)。如果函数调用中的参数可以被转换为定义中的参数,它们就会被转换。只有当函数按新方式定义或使用了函数原型时,才会进行以上处理。如果函数按旧方式定义,或者没有使用函数原型,那么就不会进行参数转换,而且很可能也不会进行参数检查。
新方式的唯一缺陷在于至今仍有不支持它的编译程序(这些大多数是基于UNIX的编译程序,它们随操作系统一起提供给用户,并且不另外收费。另一方面,许多版本的UNIX也提供了遵循ANSI标准的C编译程序)。
如果你可能需要使用ANSI标准以外的C编译程序,你最好使用一个宏,它可以在支持函数原型和新的函数定义方式时被定义。在知道能支持函数原型的情况下,你可以让一个相应的头文件自动定义该宏:
#ifdef __ANSI__
#ifndef USE_PROTOS
#define USE_PROTOS 1
#endif
#endif
函数说明可以是这样的:
#ifdef USE_PROTOS
int fool(char*);
Int foo2(char*);
#else
int foo1();
int foo2():
#endif
函数定义可以是这样的:
int
#ifdef USE_PROTOS
foo1(char *p)
#else
foo1(p)
char *p;
#endif
{
/*body of function goes here*/
}
如果你的软件只运行在MS-DOS,MS-Windows或Macintosh个人计算机上,你就不必考虑旧方式,只管用新方式好了。
请参见:
8.1 什么时候说明函数?
8.2 为什么要使用函数原型?
14.10 函数参数的类型必须在函数头部或紧跟在其后说明吗?为什么?
16.3 程序中必须包含main()的原型吗?
在14.11中,曾经回答了类似的一个问题,这里从另一个角度回答这个问题。
main()是一个函数,在大多数情况下,它与别的函数相同。但是,在定义main()时,其参数列表至少有两种可能:
int main(void);
(无参数)或
int main(int argc,char **argv);
注意:main()的参数不必被叫做argc和argv,但它们几乎总是使用这两个名字。与给main()的参数起新名字相比,还有许多地方更值得你去发挥聪明才智。
在第二种情况下,argc是运行时传递给程序的参数个数;argv[0]是程序名;argv[1]到argv[argc-1]是传递给程序的参数,即命令行的各个参数;argv[argc]是一个空指针。
main()还有其它形式的合法定义,例如:
int main(int argc,char**argv,char**envp)。
其中,envp是一个与getenv()所使用的相同的环境列表。与argv一样,envp也以一个空
指针结束。
没有一个原型可以匹配main()的所有合法定义,标准规定编译程序不必为main()提供一个原型,从这个角度来看,你也不必这样做。但是,如果你的程序需要使用main()函数的参数,则应该包含相应的main()函数原型。
没有原型,程序就不可能明确地调用main()进行参数检查。尽管这样的调用并没有被标准所禁止,但它很可能不是一个好主意。
注意:C++程序明确被禁止调用main()(有些编译程序允许你这样做,但它们是错误的)。c++编译程序在main()中加入了一些奇妙的代码,从而可以初始化(“构造”)全局变量。
如果一个C++程序可以执行两次main(),这种初始化就会发生两次,这恐怕是一件坏事。
请参见:
8.2为什么要使用函数原型?
14.11程序应该总是包含main()的一个原型吗?
16.4 main()应该总是返回一个值吗?
当然,除非它调用了exit()。
当一个程序运行时,它在结束时通常会带有某种指示成功的信息或错误码。一个c程序会用以下两种方式中的一种(或两种)来处理这些指示信息,它们的效果是相同的:
·从main()返回一个值(表示成功或失败的代码)。
·调用exit(),把表示成功或失败的代码作为参数传递给exit()。
如果程序“在main()的末尾结束”而没有采取以上处理,那么就无法保证表示成功或失败的代码是什么了,这是一件坏事。
在写c程序时,你最好快速地检查一下main()函数,它的最后一条语句应该是return语句或者是对exit()的调用(唯一的例外是当最后一条语句永远不会结束时,例如一个不带break语句的无穷for循环。在这种情况下,编译程序会提醒你此后加入到main()函数尾部的
语句永远不会被执行)。
#1
大
中
小
本主题被查看843次, 共1个帖子, 1页, 当前为第
1
页 选择页数: 1 跳转到第
页
论坛跳转...
胎教早教
准备怀孕
怀孕期
出生与分娩
婴儿期(0-1岁)
幼儿期(1-3岁)
学龄前(3-6岁)
中小学
课件
试题
中招
中招动态
招生快讯
相关政策
考试辅导
语文
数学
英语
物理
化学
生物
政治
历史
地理
家长交流
历年中招资料
满分作文
高考
高考动态
高考状元
历年试题
家长必读
志愿填报
心理减压
复习技巧
考生经验
备考辅导
高考大纲
历年分数线
高考常识
高考指南
语文
数学
外语
物理
化学
生物
历史
地理
政治
文综
理综
高考满分作文
家长
教师
读书
作文
郑州新东中学
学校动态
学生天地
高等教育
移民留学
成人高考
自学考试
考研
论文
外语
雅思
托福
四六级
实用英语
职称外语
公共英语(PETS)
商务英语
英语口语
小语种
英思力美语
在线留言
学校动态
课程
学生作业
英思力相册
学英语资料
计算机
办公应用
软件开发
平面动画设计
IT资格认证
等级考试
网络工程
初学者之路视频教程
计算机基础
Windows
Word2003
Execl2003
Powerpoint
Flash
Deamweaver
Fireworks
黑客基础
北大青鸟
青鸟新闻
开班信息
青鸟师资
在线问答
学员相关
技术文章
职业资格
企业培训师
资料库
历年试题
案例探讨
相关政策
财务会计
公务员考试
营养师
营养动态
职业前景
健康权威
学习指导
营养知识
健康食谱
心理咨询师
心理俱乐部
学习指导
花季少年
爱情婚姻
心理障碍
人在职场
职业前景
经典案例
导游
司法考试
建筑工程资格
外贸资格
医药资格考试
教师资格考试
电子商务师
人力资源师
资料库
历年试题
案例探讨
相关政策
技能培养
汽车驾驶
美容美发
美容
美发
化妆
整体形象设计
相关课程
人才信息
茶艺
调酒
厨师烹饪
手机维修
音乐乐器
舞蹈健身
企业管理
人力资源
市场营销
管理激励
管理书籍
管理视频
信诺专栏
职场招聘
招聘信息
职场沙龙
现在的时间是 2008-07-25 03:39:20
版权所有
商都网教育宝典
Powered by
Discuz!NT
1.0.6666 Copyright © 2001-2008
Comsenz Inc
.
Processed in 0.064 seconds