C 语言语法之一数据类型与运算符
大纲
数据类型
数据类型概览
常量与变量
对于基本数据类型量,按其取值是否可改变又分为常量和变量两种。在程序执行过程中,其值不发生改变的量称为常量,其值可变的量称为变量。它们可与数据类型结合起来分类,例如可分为整型常量、整型变量、浮点常量、浮点变量、字符常量、字符变量、枚举常量、枚举变量。在程序中,常量是可以不经说明而直接引用的,而变量则必须先定义后使用。(整型量包括整型常量、整型变量)
符号常量
在 C 语言中,可以用一个标识符来表示一个常量,称之为符号常量。符号常量在使用之前必须先定义,其一般定义形式为:#define 标识符 常量
。其中 #define
也是一条预处理命令(预处理命令都以”#” 开头),称为宏定义命令,其功能是把该标识符定义为其后的常量值。一经定义,以后在程序中所有出现该标识符的地方均代之以该常量值。习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。
整型常量的表示方法
整型常量就是整常数,在 C 语言中,使用的整常数有十进制、八进制和十六进制三种:
- 十进制整常数:十进制整常数没有前缀,其数码为 0~9,以下各数是合法的十进制整常数:237、-568、65535、1627
- 八进制整常数:八进制整常数必须以 0 开头,即以 0 作为八进制数的前缀,数码取值为 0~7,八进制数通常是无符号数。以下各数是合法的八进制数:015 (十进制为 13)、0101 (十进制为 65)、0177777 (十进制为 65535)
- 十六进制整常数:十六进制整常数的前缀为 0X 或 0x,其数码取值为 0
9,AF 或 a~f。以下各数是合法的十六进制整常数:0X2A (十进制为 42)、0XA0 (十进制为 160)、0XFFFF (十进制为 65535) - 整型常数的后缀:在 16 位字长的机器上,基本整型的长度也为 16 位,因此表示的数的范围也是有限定的。十进制无符号整常数的范围为 0~65535,有符号数为 - 32768~+32767。八进制无符号数的表示范围为 0~0177777。十六进制无符号数的表示范围为 0X0~0XFFFF 或 0x0~0xFFFF。如果使用的数超过了上述范围,就必须用长整型数来表示,长整型数是用后缀 “L” 或 “l” 来表示的。
整型变量的分类
注意:整型变量占多少个字节,这个跟系统和编译器的规定有关!
- 基本型:类型说明符为 int,在内存中占 4 个字节
- 短整型:类型说明符为 short int 或 short,在内存中占 2 个字节
- 长整型:类型说明符为 long int 或 long,在内存中占 8 个字节
- 无符号型:类型说明符为 unsigned,在内存中占 4 个字节
整型变量在内存中的存放形式
内存中的整型变量以二进制存储,一个字节 (byte) = 8 位 (bit)。其中数值是以补码表示,正数的补码和原码相同,负数的补码则是将该数的绝对值的二进制形式按位取反再加一。
1 | 例如:求-10的补码 |
实型常量的表示方法
实型也称为浮点型,实型常量也称为实数或者浮点数。在 C 语言中,浮点数只采用十进制表示,其中有二种形式:十进制小数形式、指数形式。标准 C 语言允许浮点数使用后缀,后缀为 “f” 或 “F” 即表示该数为浮点数,如 356f 和 356. 是等价的。
- 十进制数形式:由数码 0~ 9 和小数点组成,例如 0.0、25.0、5.789、0.13、5.0、300.、-267.8230 等均为合法的实数 (必须有小数点)
- 指数形式:由十进制数、阶码标志 “e” 或 “E”、阶码 (只能为整数,可以带符号) 组成,其一般形式为:a E n(a 为十进制数,n 为十进制整数),例如 2.1E5 (等于 2.1105)、-2.8E-2 (等于 - 2.810-2)、0.5E7 (等于 0.5*107)
实型变量的分类
实型变量分为:单精度(float 型)、双精度(double 型)和长双精度(long double 型)三类。在 Turbo C 中单精度型占 4 个字节(32 位)内存空间,其数值范围为 3.4E-38~3.4E+38,只能提供七位有效数字。双精度型占 8 个字节(64 位)内存空间,其数值范围为 1.7E-308~1.7E+308,可提供 16 位有效数字。
实型变量在内存中的存放形式
实型变量一般占 4 个字节 (32 位) 的内存空间,按指数形式存储,实数 3.14159 在内存中的存放形式如下:
- 小数部分占的位 (bit) 数愈多,数的有效数字愈多,精度愈高
- 指数部分占的位数愈多,则能表示的数值范围愈大
字符常量
字符常量是用单引号括起来的一个字符,例如:’a’、’b’、’=’、’+’、’?’都是合法字符常量。在 C 语言中,字符常量有以下特点:
- 字符常量只能用单引号括起来,不能用双引号或其它括号
- 字符常量只能是单个字符,不能是字符串
- 字符可以是字符集中任意字符,但数字被定义为字符型之后就不能参与数值运算。例如’5’和 5 是不同的,’5’是字符常量,不能参与运算
转义字符
转义字符是一种特殊的字符常量,转义字符以反斜线”" 开头,后面跟一个或几个字符。转义字符具有特定的含义,不同于字符原有的意义,故称 “转义” 字符。例如 printf
函数的格式串中用到的 \n
就是一个转义字符,其意义是 “回车换行”。转义字符主要用来表示那些用一般字符不便于表示的控制代码。
字符变量
字符变量用来存储字符常量,即单个字符。字符变量的类型说明符是 char,字符变量类型定义的格式和书写规则都与整型变量相同。例如:char a, b;
字符变量在内存中的存放形式
每个字符变量被会被分配一个字节的内存空间,因此只能存放一个字符。字符值是以 ASCII 码的形式存放在变量的内存单元之中的,如 x 的十进制 ASCII 码是 120,y 的十进制 ASCII 码是 121。若对字符变量 a、b 分别赋予’x’和’y’值:a =‘x’; b = 'y';
,实际上是在 a、b 两个单元内存放 120 和 121 的二进制值。
字符串常量
字符串常量是由一对双引号括起的字符序列,例如:”CHINA”、”C program” 等都是合法的字符串常量。字符串常量和字符常量是不同的量,它们之间主要有以下区别:
- 字符常量由单引号括起来,字符串常量由双引号括起来
- 字符常量只能是单个字符,字符串常量则可以含一个或多个字符
- 可以把一个字符常量赋予一个字符变量,但不能把一个字符串常量赋予一个字符变量,例如:可以是 char a = ‘a’ 但不能是 char a = “a”
- 字符常量占一个字节的内存空间,字符串常量占的内存字节数等于字符串的字节数加一,额外增加的一个字节用于存放字符
\0
(ASCII 码为 0),这是字符串的结束标志
运算符
各类数值型数据之间的混合运算
变量的数据类型是可以转换的,转换的方法有两种,一种是自动转换,一种是强制转换。自动转换发生在不同数据类型的量混合运算时,由编译系统自动完成。自动转换遵循以下规则:
- 若参与运算量的类型不同,则先转换成同一类型,然后进行运算
- 转换按数据长度增加的方向进行,以保证精度不降低,如 int 型和 long 型运算时,先把 int 量转成 long 型后再进行运算
- 所有的浮点运算都是以双精度进行的,即使仅含 float 单精度量运算的表达式,也要先转换成 double 型,再作运算
- char 型和 short 型参与运算时,必须先转换成 int 型
- 在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入,类型自动转换的规则为:double <- long <- unsigned <- int <- char、short
自动类型转换
如果赋值运算符两边的数据类型不相同,系统将自动进行类型转换,即把赋值号右边的类型换成左边的类型,具体规定如下:
- 实型赋予整型,舍去小数部分
- 整型赋予实型,数值不变,但将以浮点形式存放,即增加小数部分 (小数部分的值为 0)
- 字符型赋予整型,由于字符型为一个字节,而整型为四个字节,故将字符的 ASCII 码值放到整型量的低八位中,高八位为 0。整型赋予字符型,只把低八位赋予字符量
强制类型转换
强制类型转换是通过类型转换运算来实现的,其一般形式为:(类型说明符) (表达式),其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。例如: (float) a
表示把 a 转换为浮点型,(int)(x+y)
表示把 x+y 的结果转换为整型,在使用强制转换时应注意以下问题:
- 类型说明符和表达式都必须加括号 (单个变量可以不加括号),如把
(int)(x+y)
写成(int)x+y
则成了把 x 转换成 int 型之后再与 y 相加了 - 无论是强制转换或是自动转换,都只是为了本次运算的需要而对变量的数据长度进行的临时性转换,而不改变数据说明时对该变量定义的类型
基本的算术运算符
- 加法运算符 “+”:加法运算符为双目运算符,即应有两个量参与加法运算。如 a+b, 4+8 等,具有右结合性
- 减法运算符 “-”:减法运算符为双目运算符,但 “-” 也可作负值运算符,此时为单目运算,如 - x, -5 等,具有左结合性
- 乘法运算符 “*”:双目运算,具有左结合性
- 除法运算符 “/”:双目运算,具有左结合性,参与运算量均为整型时,结果也为整型,舍去小数部分;如果运算量中有一个是实型,则结果为双精度实型
运算符的优先级与结合性
- 运算符的优先级:C 语言中,运算符的运算优先级共分为 15 级。1 级最高,15 级最低。在表达式中,优先级较高的先于优先级较低的进行运算。而在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理
- 运算符的结合性:C 语言中各运算符的结合性分为两种,即左结合性 (自左至右) 和右结合性 (自右至左)。例如算术运算符的结合性是自左至右,即先左后右。如有表达式 x-y+z 则 y 应先与 “-” 号结合,执行 x-y 运算,然后再执行 + z 的运算,这种自左至右的结合方向就称为 “左结合性”。而自右至左的结合方向称为 “右结合性”。 最典型的右结合性运算符是赋值运算符,如 x=y=z,由于 “=” 的右结合性,应先执行 y=z 再执行 x=(y=z) 运算。C 语言运算符中有不少为右结合性,应注意区别,以避免理解错误
运算符优先级与结合性一览表
点击查看运算符优先级与结合性一览表,表中可以总结出如下规律:
- 结合方向只有三个是从右往左,其余都是从左往右
- 所有双目运算符中只有赋值运算符的结合方向是从右往左
- 另外两个从右往左结合的运算符也很好记,因为它们很特殊:一个是单目运算符,一个是三目运算符
- C 语言中有且只有一个三目运算符
- 逗号运算符的优先级最低,要记住
- 对于优先级:算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符,逻辑运算符中 “!” 除外
自增、自减运算符
- 自增 1 运算符:记为
++
,其功能是使变量的值自增 1 - 自减 1 运算符:记为
--
,其功能是使变量值自减 1 - 自增 1,自减 1 运算符均为单目运算,都具有右结合性,可有以下几种形式:
++i
,i 自增 1 后再参与其它运算--i
,i 自减 1 后再参与其它运算i++
,i 参与运算后,i 的值再自增 1i--
,i 参与运算后,i 的值再自减 1
1 | int i = 8; |
赋值运算符和赋值表达式
- 赋值运算符记为 “=”,由 “=” 连接的式子称为赋值表达式,其一般表现形式为: 变量 = 表达式,例如
x = a + b
- 赋值表达式的功能是计算表达式的值再赋予左边的变量,因此赋值运算符具有右结合性,例如
a=b=c=5
可理解为a=(b=(c=5))
复合的赋值运算符
- 在赋值符 “=” 之前加上其它二目运算符可构成复合赋值符,例如 +=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=
- 复合赋值符这种写法,十分有利于编译处理,能提高编译效率并产生质量较高的目标代码,例如
a+=5
等价于a=a+5
,r%=p
等价于r=r%p
本节重点内容
- 整型变量在内存中的存放形式
- 实型变量在内存中的存放形式
- 字符变量在内存中的存放形式
- 各类数值型数据之间的混合运算
- 自动类型转换
- 运算符的优先级与结合性