C语言程序设计.docx
- 文档编号:29398162
- 上传时间:2023-07-23
- 格式:DOCX
- 页数:20
- 大小:52.90KB
C语言程序设计.docx
《C语言程序设计.docx》由会员分享,可在线阅读,更多相关《C语言程序设计.docx(20页珍藏版)》请在冰豆网上搜索。
C语言程序设计
第2章数据类型、运算符和表达式
C程序是一个字符序列,编译器首先把程序的字符序列分解成称之为记号(token)的词法元素,再根据确定的语法规则检查这些记号是否构成合法的串。
记号是程序中具有语义的最基本组成元素。
本章将首先介绍词法元素和语法规则的概念,然后详尽地讨论组成C程序的基本元素,包括基本数据类型、常量和变量、运算符、表达式等。
2.1字符及词法元素
2.1.1字符集
C语言源程序是由字符序列构成的,C语言字符集是7位ASCII码的子集,组成如下:
英文字母:
a~z和A~Z
数字字符:
0~9
特殊字符:
!
"#%&'()*+,-./:
;<>=?
[]\
^_{}|~
空白字符:
空格、换行符、水平制表符(HT)、垂直制表符(VT)、换页符(FF)
有些国家的本国字符集不包括上述所有特殊字符。
标准C语言定义了一组三字符序列,使C语言程序可以用ISO646-1083不变代码集编写,这是7位ASCII代码集的子集,是许多非英语国家字符集公用的代码集。
三字符序列以两个连续问号开头,见表2-1。
所有的三字符序列都要用相应的单个字符替换,这种替换发生在其他任何处理之前。
标准C语言只能识别这9个三字符序列,其他字符序列(如?
?
&)不进行替换。
表2.1三字符序列
三字符序列
替换
三字符序列
替换
三字符序列
替换
?
?
=
?
?
/
?
?
'
#
\
^
?
?
(
?
?
)
?
?
!
[
]
|
?
?
<
?
?
>
?
?
-
{
}
~
例如,inta?
?
(4?
?
)=?
?
<0?
?
>;被替换成inta[4]={0};后再进行词法分析。
字符串常量"?
?
/?
?
/"被替换成"\\",表示包含一个反斜线的字符串。
注:
TurboC2.0不能识别三字符序列,而Visualc++6.0能。
2.1.2词法元素
编译器对C程序中的字符序列进行词法分析,即按照特定规则分解成称之为记号的词法元素,记号是程序中具有语义的最基本组成单元。
记号共分5类:
标识符、关键字、常量、运算符和标点符号。
编译器从左至右收集字符,总是尽量建立最长的记号,即使结果并不构成有效的C语言程序。
相邻记号可以用空白符或注释语句分开。
在进行严格的语法定义之前,先看一些简单的例子,学习一些程序中的记号。
例2.1sum=x+y
编译器将这些字符分解成sum、=、x、+和y共5个记号。
其中sum、x、y是标识符,=和+是运算符。
因此可以写成:
sum=x+y,但不能写成:
sum=x+y,因为编译器会把sum中的每一个字符处理为单独的标识符,这是无效的C语法。
例2.2inta,b=10;
被分解成int、a、,、b、=、10和;共7个记号。
其中int是关键字,a和b是标识符,=是运算符,,和;是标点符号,10是常量。
上式不能写成:
inta,b=10;这样会把inta看成一个标识符记号。
例2.3x+++++y
被分解成x、++、++、+、y共5个记号,这是无效的C语法。
如组合成记号后的x+++++y是有效语法,但不允许这种记号组合。
2.2语法规则
由C语言字符集中的单个或多个字符按照确定的规则组成语义上关联的记号,再由记号根据确定的规则构成语句乃至整个程序文本,这些规则被称为C的语法规则。
编译器根据语法规则检查程序的合法性,并根据语法规则识别每一条语句,同时进行相应的语义处理。
为了说明如何形式化地描述语法规则,首先来看一个简单的描述语法的式子:
<数字>:
:
=0|1|2|3|4|5|6|7|8|9
用文字叙述:
数字可以是字符0,或者字符1,……,或者字符9。
这种描述语法结构的式子被称为生成式。
为了通过生成式给出语法的形式化定义,需要提供如下4个方面的信息:
(1)终结符,C语言字符集中的符号。
(2)非终结符,表示语法单位,在定义中将被利用相应的生成式进一步展开。
(3)生成式规则,每条规则确定一个结构的各个成分,生成式以一个非终结符作为它的左部,而后是符号:
:
=,然后取自终结符和非终结符集合的一个串作为右部。
(4)开始符号,代表语法定义的主结构,任何语法定义过程都从某个非终结符开始。
开始符号的生成式总是写在最前面。
以上4个部分构成了一个语言的文法。
而用于描述文法的生成式很多,Backus-Naur范式(BNF)就是这样的一种生成式,
2.2.1BNF范式
它被用于组织Algol60程序设计语言的报告,以后就流行起来了。
例如,一个有关实数的BNF范式:
<实数>:
:
=<整数部分>.<小数部分>
<整数部分>:
:
=<数字>|<整数部分><数字>
<小数部分>:
:
=<数字>|<数字><小数部分>
<数字>:
:
=0|1|2|3|4|5|6|7|8|9
在BNF里,非终结符用特殊符号<和>括起来,空串写成
符号“:
:
=”可读作“可以是”,符号“|”可读作“或者”。
因此,
<小数部分>:
:
=<数字>|<数字><小数部分>
就可以读作:
“一个小数部分可以是一个数字,或者是一个数字后随另一个小数部分”。
例如,小数部分153,其中的数字是“1”,而后随的是“53”。
每个由|分隔的选项实际上是一个独立规则,因此上述写法也可以重复为下面等价的形式:
<小数部分>:
:
=<数字>
<小数部分>:
:
=<数字><小数部分>
例如,假定0.5中的0是可选的,这个语法的BNF范式:
<实数>:
:
=<整数部分>.<小数部分>
<整数部分>:
:
=
<小数部分>:
:
=<数字序列>
用文字描述:
一个实数有一个整数部分,一个小数点和一个小数部分,其中整数部分是一个可选的数字序列,小数部分是一个数字序列。
根据规则:
<整数部分>:
:
=
2.2.2EBNF
EBNF是BNF的一种扩充,用它能直接描述元素的序列和可选元素,减少生成式和非终结符的数目,但不会改变所能描述的范围,任何在EBNF中能描述的也都可以用BNF描述。
EBNF中符号有:
{}:
括起来的部分重复0次或多次。
[]:
括起来的部分出现0次或1次。
():
表示结成一组。
例如,在实数表示中有一个可选整数部分,用EBNF范式描述为:
<实数>:
:
=[<整数部分>].<小数部分>
与其等价的BNF范式为:
<实数>:
:
=<整数部分>.<小数部分>|.<小数部分>
象{和}这类在语言的描述中有特殊作用的符号被称作元符号,同样符号可能又出现在某个语言的语法中,例如,例如,a[i]下标i不是可选的。
因此需要区分单词和元符号,通过需要时用单引号括起单词的方式,就可以避免单词和元符号之间的混淆。
例如描述简单表达式生成式的EBNF范式:
<表达式>:
:
=<项>{(+|-)<项>}
<项>:
:
=<因子>{(*|/)<因子>}
<因子>:
:
='('<表达式>')'|名字|数字
2.2.3语法图
语法图是另一种表示文法的常见方式,对应于每个非终结符有一个子图,图中每条路径对应于该非终结符的一个生成式,路径上的是生成式右端的终结符和非终结符。
终结符用弧形框表示,非终结符用矩形框表示。
上述简单表达式文法可表示为图2.1的形式。
图2.1简单表达式语法图
2.3标识符、关键字及分隔符
2.3.1标识符
标识符是用来标识用户定义的常量、变量、数据类型和函数等名字的符号。
C语言规定,标识符由字母、数字和下划线组成,但首字符必须是字母或下划线,字母要区分大小写。
标识符的语法规则可用下述BNF范式描述,语法图可表示为图2-2的形式。
<标识符>:
:
=(a|b|c|…|z|A|B|…|Z|_)<字母数字>
<字母数字>:
:
=(0|1|2|…|9|a|b|c|…|z|A|B|…|Z|_)<字母数字>
标识符的EBNF范式为:
<标识符>:
:
=(a|b|c|…|z|A|B|…|Z|_){0|1|2|…|9|a|b|c|…|z|A|B|…|Z|_}
图2.2标识符的语法图
下面是一些合法的标识符:
K_idx_coordtime1
但以下的表示均不是标识符:
20_sum(不能以数字开头)
not#me(特殊符号#不是组成字符)
创建标识符是为了对程序中的对象进行唯一地命名,不能使用类似double和if这样的C语言关键字为自己的对象命名,同时也要避免使用C语言程序库中函数和常量的名称,例如scanf和sin。
C语法规定,一个标识符所包含的字符个数(即长度)不限,但能被编译程序识别的那一部分字符的个数称为标识符的有效长度。
早期有些C语言版本规定标识符有效长度为前8个字符,ANSIC规定为31个字符,而C99增加到63个字符。
良好的编程风格要求程序员选择有助于记忆且有一定含义的标识符,这样可增强程序的可读性和程序的文档性。
2.3.2关键字
关键字也称作保留字,是被系统赋予特定含义并有专门用途的标识符,不能作为普通标识符,但可以作为宏名,因为所有预处理发生在识别这些关键字之前。
表2.1列出了标准C定义的关键字,其中加“*”号的均是C99标准中新增加的。
表2.2标准C的关键字
auto_Bool*breakcasechar_Complex*
constcontinuedefaultrestrict*dodouble
elseenumexternfloatforgoto
if_Imaginary*inline*intlongregister
returnshortsignedsizeofstaticstruct
switchtypedefunionunsignedvoidvolatile
while
此外,有些C编译系统中还规定了其它的关键字,在不同的系统中,附加的关键字是不同的。
例如BorlandC中附加的关键字有:
asm、cdecl、far、huge、interrupt、near、pascal,其含义参考具体编译程序的有关资料。
2.3.3分隔符
分隔符统称为空白字符,包括空格符、制表符、换行符、换页符及注释符,它们在语法上仅起分隔单词的作用。
当程序中两个相邻的单词之间如果不用标识符就不能将两者区分开时则必须加分隔符(通常用空格符)。
为了使程序更加清晰,便于阅读,任何单词之间都可以加适当空格符。
例如,变量定义“intx,y;”不能写成“intx,y;”,因为intx是一个标识符,但可以写成“intx,y;”,x和y之间有逗号隔开,所以可加或不加空格。
2.4基本数据类型
数据是程序处理的对象。
数据的取值范围以及能对该数据施行的运算由该数据的类型决定。
数据类型也就是数据的存储方式,它告知编译程序为该类型的对象在内存分配多大的空间,数据类型直接反映了一种语言的数据表达能力。
2.4.1数据类型的分类
C的数据类型非常丰富,包括几种类型的整型、浮点型、复数类型、指针、数组、结构和联合等。
可以把C的数据类型组织成表2-2所示的类别,分为基本类型、导出类型和空类型(void),其中加“#”号的均是C99标准中新增加的。
整型包括所有形式的整型、字符、枚举和布尔型。
基本类型包括整型、浮点型和复数类型,复数类型在复数域中,而整型和浮点型在实数域中。
导出类型是由基本类型构造出的,以后将要讨论的数组、指针、结构和联合就属于导出类型。
空类型有三种用途:
(1)明确地表示函数不返回任何值;
(2)说明函数无参数;(3)说明指针的类型,表示指针类型未确定,每种用途的含义及用法将在后续章节讨论。
表2.3C语言类型与类别
C语言类型
类型分类
short,int,long,longlong#(signed和unsigned)
char(signed和unsigned)
_Bool#
enum{…}
整型
基本类型
float,double,longdouble
_Complex#,_Imaginary#
浮点型
复数类型
T*
T[…]
struct{…}
union{…}
指针类型
数组类型
结构类型
联合类型
导出类型
void
空类型
2.4.2基本类型的名字
C99新增的数据类型在2.9节介绍,枚举类型在2.10节介绍,本小节介绍所有其它基本类型,即:
字符型、整型、浮点型和双精度浮点型,类型关键字分别是char、int、float和double。
char变量用于保存8位的ASCII字符,也可存储8位二进制数。
int变量用于保存整型数。
float和double变量用于保存带小数的、非常大或非常小的数。
float和double变量之间的差别是所表示的最大数和最小数不同。
除float外,其它三种基本数据类型的前面都可以加类型修饰符,用于改变基本类型的含义,以准确地适应不同情况下的要求。
类型修饰符有:
signed(有符号)、unsigned(无符号)、short(短的)和long(长的)。
signed和unsigned可用于字符型和整型、short和long可用于整型,long还可用于双精度浮点型。
表2.4依据ANSIC标准列举了这些基本数据类型及它们的长度和取值范围。
表2.4基本类型的名字和长度
完整类型名
简写类型名
字节长度
值域
char
signedchar
unsignedchar
signedint
unsignedint
signedshortint
signedlongint
unsignedshortint
unsignedlongint
float
double
longdouble
char
signedchar
unsignedchar
int
unsigned
short
long
unsignedshort
unsignedlong
float
double
longdouble
1
1
1
2或4
2或4
2
4
2
4
4
8
≥8
有符号:
-128~127
无符号:
0~255
-128~127
0~255
2字节:
-32768~32767
4字节:
-2147483648~2147483647
2字节:
0~65535
4字节:
0~4294967295
-32768~32767
-2147483648~2147483647
0~65535
0~4294967295
|3.4e-38|~|3.4e+38|
|1.7e-308|~|1.7e+308|
由具体实现定义
通常,整型前面的signed可以省略,即对任何整型类型缺省就是有符号的。
同样,当int前面有其它修饰符时,关键字int也可以省略。
例如,signedint等价于int和signed,signedshortint、shortint、signedshort和short都是等价的。
由于较短的名字容易输入,所以经常使用短类型名。
2.4.3字符和数据类型char
char的存储长度是一字节。
signed和unsigned的区别在于它们的最高位的定义不同。
signed的最高位为符号位,而unsigned没有符号位,因此,signedchar变量的取值范围为-128~127(在采用补码的机器上),unsignedchar变量的取值范围为0~255。
普通char对象是有符号还是无符号取决于具体机器,但可打印字符总是正的,多数系统中char与signedchar是同一类型。
字符数据以ASCII码存储在内存的一个字节中,例如,字符'a'被存储为01100001,这个二进制数的值就是字符'a'的ASCII码为97。
由于字符数据的存储形式和整数的存储形式类似,使得字符数据和整型数据之间可以通用。
在不要求大整数的情况下,可用字符型代替整型,整型也能用于表示字符。
一个字符数据可以以字符形式输出,也可以以整数形式输出。
同样,可以按字符格式或整数格式输出任何整数。
2.4.4整型类型
通常把int型值存储在一个机器字中。
有些机器的字长为2字节,还有一些机器的字长为4字节,甚至其它的可能。
也就是说,int的长度随机器字长不同而不同。
假设机器字长为2字节,那么int变量的取值范围为-215~215-1(即-32768~32767),unsigned变量的取值范围为0~216-1(即0~65535)。
考虑下面的代码是否正确。
#defineBIG30000
voidmain(void)
{
intx,y,z;
x=y=BIG;
z=x+y;/*整数溢出*/
……
}
以上代码语法上是正确的,然而,在16位机上运行时,会赋给变量z一个错误值。
表达式的逻辑值是60000,这大于int的最大值,该加法运算要引起整数溢出。
当出现整数溢出时,程序会继续执行,但逻辑结果是错误的。
因此,程序员必须时刻保证整数表达式的值在合理范围内。
引入short和long的目的是为了提供各种满足实际要求的不同长度的整数。
int通常反映特定机器的自然大小,一般为2字节或4字节,short对象一般为2字节,long对象一般为4字节。
因此,当关心存储时,用short;当需要较大的整数值时,用long。
整型类型的实际长度和值域等信息记录在头文件limit.h中。
2.4.5浮点类型
ANSIC只有3种浮点类型:
float、double和longdouble,它们的区别是浮点表示法所占的位数不同,因此它们可表示的数值范围和精度也不同。
精度描述了浮点值中有意义的十进制位的个数,范围描述了浮点变量能表示的正的最大浮点值和最小浮点值。
一个浮点数N可表示成带符号的m乘以基b的指数x次幂,即:
在计算机中,用二进制形式分别存储实数的各个部分。
浮点数的存储区分为3个区域:
符号区、指数区和尾数区。
图2.2显示了一种4字节float数表示法表示的数值-10.0。
这种表示法将32位划分成3个区域:
符号区(1b)、指数区(8b)和尾数区(23b),基b=2。
尾数的结果应是规格化的,即它的形式为:
0.1XX…X,这里X代表0或1。
这种形式总是小数点后以1开始。
图2.2浮点数的二进制表示法
尾数所占的位数决定值的精度,指数所占的位数决定值的范围,标准C没有规定浮点类型的长度和浮点数的格式,由具体实现定义。
因此,float的精度大约为7位,范围约为10-38~10+38。
double占8字节存储空间,其中符号1位,指数11位,尾数52位,其精度大约为15位,范围约为10-308~10+308。
longdouble的存储空间通常要多于double,很多编译器将其处理为double,在某些系统中,它占用10或12字节。
由于double对绝大多数计算均已够用,因此很少使用这种长类型。
浮点类型的实际精度和指数范围等信息在标准头文件float.h中可查到。
必须要注意的是:
在值的可表示范围内,整数一定是精确表示,而浮点数的表示可能只是近似的。
其值与表示法之间的差称为“可表示误差”。
例如:
floatx=4.8;printf("\nx=%0.8f",x);结输出结果为:
x=4.80000019
计算也可能造成可表示误差。
根据数学公式,z*(y/z)的结果应等于y。
但计算机计算后的结果会受到浮点数可表示误差的影响,两者可能不相等。
由此可见,不能使用==和!
=运算符比较float和double类型的数据。
可以用两个数值之差同一个预定的小正数比较的方法解决这个问题。
对任何应用程序,均可选出一个比最小可测试数据小一点的小正数epsilon,测试两个数值之差的绝对值是否小于epsilon。
测试大于或小于条件时,也需用epsilon值弥补可表示误差。
如果浮点数绝对值太大或太小,超出其类型可表示范围,则产生溢出。
在数轴上,一个double浮点数可以表示如图2-3,其中E区间和F区间是浮点数可表示范围;C区间和D区间数据绝对值小于最小可表示数值,产生下溢;A区间和B区间数据绝对值大于最大可表示数值,产生上溢。
图2.3double型数在数轴上的表示
下溢时,有些系统保存一个非规则化数值,即指数域全为0,尾数域非0,有些系统将其简单地用0表示。
上溢后,用称为“无穷大”的特殊位模式表示,即指数域全为1,尾数域0。
有些系统中将输出+Infinity或-Infinity表示上溢的数据;在其它系统中,上溢数是一个指数非常大的数值。
程序员必须能识别本地编译器和系统的上溢和下溢值,能识别并修正造成溢出的错误运算。
2.5常量与变量
数据是程序处理的对象,分为常量和变量两种。
常量是程序执行期间其值保持不变的数据,变量是程序执行期间其值可以改变的数据。
每个数据对象,不管是常量还是变量,都有确定的类型和它相联系。
常量有文字常量和符号常量两种表示形式。
文字常量就是在代码中使用文字书写的,例如3.0是一个double文字常量,C语言中每一种类型的数值都可以书写文字常量。
符号常量指的是用标识符赋予名称的常量。
2.5.1文字常量
C有4种类型的常量:
整型、浮点型、字符型和字符串型。
常量的数值和类型从其文字书写格式即可判别。
下面介绍前述各种基本类型常量的书写格式,C99中新增的复数浮点型、宽字符型和宽字符串型常量的表示将在2.9节介绍。
1.整型常量
整型常量可以用十进制、八进制或十六进制书写。
三种不同进制通过前缀字符加以区分:
前缀为0时,是八进制;前缀为0x或0X时,是十六进制;无前缀时,是十进制。
例如,十进制整数31可以写成八进制数037,也可以写成十六进制数0x1f或0X1F。
整型常量可以带有后缀,用以指定其类型:
●字母u或U表示unsigned
●字母l或L表示long
●字母ul或UL表示unsignedlong
●字母ll或LL表示longlong(C99)
●字母ull或ULL表示unsignedlonglong(C99)
●无后缀时,表示int
后缀字母大小写任意,小写字母l很容易和数字1混淆,应避免使用。
例如,0XFUL是一个unsignedlong常量,其值等于十进制数15。
如果不发生溢出,整型常量的值总是非负数。
如果前面出现负号,则是对常量使用的一元运算符,而不是常量的一部分。
当常量值超出指定类型的范围时,其实际类型取决于数值大小、前缀、后缀和C语言实现确定的类型表示精度,确定类型的规则很复杂,在标准化前的C语言、C89和C99中各不相同。
2.浮点型常量
浮点型常量有两种表示方式。
第一种是通常的带小数点的十进制数形式,任何包含小数点的数均为浮点表示法,既可以小数点开头,也可以小数点结尾,如23.7,14.,.126等。
第二种是指数形式,即科学计数法,将指数部分跟在尾数部分后面。
尾数部分的书写规则与第一种相同,但可以没有小数点,指数部分由一个字母e或E、一个可选的符号+或-(缺省为+)和一个
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言程序设计
![提示](https://static.bdocx.com/images/bang_tan.gif)