C 语言语法之一数据类型与运算符

大纲

数据类型

数据类型概览

c-data-type

常量与变量

对于基本数据类型量,按其取值是否可改变又分为常量和变量两种。在程序执行过程中,其值不发生改变的量称为常量,其值可变的量称为变量。它们可与数据类型结合起来分类,例如可分为整型常量、整型变量、浮点常量、浮点变量、字符常量、字符变量、枚举常量、枚举变量。在程序中,常量是可以不经说明而直接引用的,而变量则必须先定义后使用。(整型量包括整型常量、整型变量)

符号常量

在 C 语言中,可以用一个标识符来表示一个常量,称之为符号常量。符号常量在使用之前必须先定义,其一般定义形式为:#define 标识符 常量。其中 #define 也是一条预处理命令(预处理命令都以”#” 开头),称为宏定义命令,其功能是把该标识符定义为其后的常量值。一经定义,以后在程序中所有出现该标识符的地方均代之以该常量值。习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。

整型常量的表示方法

整型常量就是整常数,在 C 语言中,使用的整常数有十进制、八进制和十六进制三种:

  1. 十进制整常数:十进制整常数没有前缀,其数码为 0~9,以下各数是合法的十进制整常数:237、-568、65535、1627
  2. 八进制整常数:八进制整常数必须以 0 开头,即以 0 作为八进制数的前缀,数码取值为 0~7,八进制数通常是无符号数。 以下各数是合法的八进制数: 015 (十进制为 13)、0101 (十进制为 65)、0177777 (十进制为 65535)
  3. 十六进制整常数:十六进制整常数的前缀为 0X 或 0x,其数码取值为 09,AF 或 a~f。 以下各数是合法的十六进制整常数: 0X2A (十进制为 42)、0XA0 (十进制为 160)、0XFFFF (十进制为 65535)
  4. 整型常数的后缀:在 16 位字长的机器上,基本整型的长度也为 16 位,因此表示的数的范围也是有限定的。十进制无符号整常数的范围为 0~65535,有符号数为 - 32768~+32767。八进制无符号数的表示范围为 0~0177777。十六进制无符号数的表示范围为 0X0~0XFFFF 或 0x0~0xFFFF。如果使用的数超过了上述范围,就必须用长整型数来表示,长整型数是用后缀 “L” 或 “l” 来表示的。

整型变量的分类

注意:整型变量占多少个字节,这个跟系统和编译器的规定有关!

  1. 基本型:类型说明符为 int,在内存中占 4 个字节
  2. 短整型:类型说明符为 short int 或 short,在内存中占 2 个字节
  3. 长整型:类型说明符为 long int 或 long,在内存中占 8 个字节
  4. 无符号型:类型说明符为 unsigned,在内存中占 4 个字节

整型变量在内存中的存放形式

内存中的整型变量以二进制存储,一个字节 (byte) = 8 位 (bit)。其中数值是以补码表示,正数的补码和原码相同,负数的补码则是将该数的绝对值的二进制形式按位取反再加一。

1
2
3
4
5
6
7
例如:求-10的补码

10的原码: 00001010
取反: 11110101
再加1,得-10的补码: 11110110

提示:第一位是符号位!

实型常量的表示方法

实型也称为浮点型,实型常量也称为实数或者浮点数。在 C 语言中,浮点数只采用十进制表示,其中有二种形式:十进制小数形式、指数形式。标准 C 语言允许浮点数使用后缀,后缀为 “f” 或 “F” 即表示该数为浮点数,如 356f 和 356. 是等价的。

  1. 十进制数形式:由数码 0~ 9 和小数点组成,例如 0.0、25.0、5.789、0.13、5.0、300.、-267.8230 等均为合法的实数 (必须有小数点)
  2. 指数形式:由十进制数、阶码标志 “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 在内存中的存放形式如下:

c-data-type-float

  • 小数部分占的位 (bit) 数愈多,数的有效数字愈多,精度愈高
  • 指数部分占的位数愈多,则能表示的数值范围愈大

字符常量

字符常量是用单引号括起来的一个字符,例如:’a’、’b’、’=’、’+’、’?’都是合法字符常量。在 C 语言中,字符常量有以下特点:

  1. 字符常量只能用单引号括起来,不能用双引号或其它括号
  2. 字符常量只能是单个字符,不能是字符串
  3. 字符可以是字符集中任意字符,但数字被定义为字符型之后就不能参与数值运算。例如’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” 等都是合法的字符串常量。字符串常量和字符常量是不同的量,它们之间主要有以下区别:

  1. 字符常量由单引号括起来,字符串常量由双引号括起来
  2. 字符常量只能是单个字符,字符串常量则可以含一个或多个字符
  3. 可以把一个字符常量赋予一个字符变量,但不能把一个字符串常量赋予一个字符变量,例如:可以是 char a = ‘a’ 但不能是 char a = “a”
  4. 字符常量占一个字节的内存空间,字符串常量占的内存字节数等于字符串的字节数加一,额外增加的一个字节用于存放字符 \0 (ASCII 码为 0),这是字符串的结束标志

运算符

各类数值型数据之间的混合运算

变量的数据类型是可以转换的,转换的方法有两种,一种是自动转换,一种是强制转换。自动转换发生在不同数据类型的量混合运算时,由编译系统自动完成。自动转换遵循以下规则:

  1. 若参与运算量的类型不同,则先转换成同一类型,然后进行运算
  2. 转换按数据长度增加的方向进行,以保证精度不降低,如 int 型和 long 型运算时,先把 int 量转成 long 型后再进行运算
  3. 所有的浮点运算都是以双精度进行的,即使仅含 float 单精度量运算的表达式,也要先转换成 double 型,再作运算
  4. char 型和 short 型参与运算时,必须先转换成 int 型
  5. 在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入,类型自动转换的规则为:double <- long <- unsigned <- int <- char、short

自动类型转换

如果赋值运算符两边的数据类型不相同,系统将自动进行类型转换,即把赋值号右边的类型换成左边的类型,具体规定如下:

  1. 实型赋予整型,舍去小数部分
  2. 整型赋予实型,数值不变,但将以浮点形式存放,即增加小数部分 (小数部分的值为 0)
  3. 字符型赋予整型,由于字符型为一个字节,而整型为四个字节,故将字符的 ASCII 码值放到整型量的低八位中,高八位为 0。整型赋予字符型,只把低八位赋予字符量

强制类型转换

强制类型转换是通过类型转换运算来实现的,其一般形式为:(类型说明符) (表达式),其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。例如: (float) a 表示把 a 转换为浮点型,(int)(x+y) 表示把 x+y 的结果转换为整型,在使用强制转换时应注意以下问题:

  1. 类型说明符和表达式都必须加括号 (单个变量可以不加括号),如把 (int)(x+y) 写成 (int)x+y 则成了把 x 转换成 int 型之后再与 y 相加了
  2. 无论是强制转换或是自动转换,都只是为了本次运算的需要而对变量的数据长度进行的临时性转换,而不改变数据说明时对该变量定义的类型

基本的算术运算符

  1. 加法运算符 “+”:加法运算符为双目运算符,即应有两个量参与加法运算。如 a+b, 4+8 等,具有右结合性
  2. 减法运算符 “-”:减法运算符为双目运算符,但 “-” 也可作负值运算符,此时为单目运算,如 - x, -5 等,具有左结合性
  3. 乘法运算符 “*”:双目运算,具有左结合性
  4. 除法运算符 “/”:双目运算,具有左结合性,参与运算量均为整型时,结果也为整型,舍去小数部分;如果运算量中有一个是实型,则结果为双精度实型

运算符的优先级与结合性

  1. 运算符的优先级:C 语言中,运算符的运算优先级共分为 15 级。1 级最高,15 级最低。在表达式中,优先级较高的先于优先级较低的进行运算。而在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理
  2. 运算符的结合性:C 语言中各运算符的结合性分为两种,即左结合性 (自左至右) 和右结合性 (自右至左)。例如算术运算符的结合性是自左至右,即先左后右。如有表达式 x-y+z 则 y 应先与 “-” 号结合,执行 x-y 运算,然后再执行 + z 的运算,这种自左至右的结合方向就称为 “左结合性”。而自右至左的结合方向称为 “右结合性”。 最典型的右结合性运算符是赋值运算符,如 x=y=z,由于 “=” 的右结合性,应先执行 y=z 再执行 x=(y=z) 运算。C 语言运算符中有不少为右结合性,应注意区别,以避免理解错误

运算符优先级与结合性一览表

点击查看运算符优先级与结合性一览表,表中可以总结出如下规律:

  1. 结合方向只有三个是从右往左,其余都是从左往右
  2. 所有双目运算符中只有赋值运算符的结合方向是从右往左
  3. 另外两个从右往左结合的运算符也很好记,因为它们很特殊:一个是单目运算符,一个是三目运算符
  4. C 语言中有且只有一个三目运算符
  5. 逗号运算符的优先级最低,要记住
  6. 对于优先级:算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符,逻辑运算符中 “!” 除外

自增、自减运算符

  1. 自增 1 运算符:记为 ++,其功能是使变量的值自增 1
  2. 自减 1 运算符:记为 --,其功能是使变量值自减 1
  3. 自增 1,自减 1 运算符均为单目运算,都具有右结合性,可有以下几种形式:
    ++i,i 自增 1 后再参与其它运算
    --i,i 自减 1 后再参与其它运算
    i++,i 参与运算后,i 的值再自增 1
    i--,i 参与运算后,i 的值再自减 1
1
2
3
4
5
6
7
int i = 8;
printf("%d\n",++i); // 打印9,此时i=9
printf("%d\n",--i); // 打印8,此时i=8
printf("%d\n",i++); // 打印8,此时i=9
printf("%d\n",i--); // 打印9,此时i=8
printf("%d\n",-i++); // 打印-8,此时i=9
printf("%d\n",-i--); // 打印-9,此时i=8

赋值运算符和赋值表达式

  1. 赋值运算符记为 “=”,由 “=” 连接的式子称为赋值表达式,其一般表现形式为: 变量 = 表达式,例如 x = a + b
  2. 赋值表达式的功能是计算表达式的值再赋予左边的变量,因此赋值运算符具有右结合性,例如 a=b=c=5 可理解为 a=(b=(c=5))

复合的赋值运算符

  1. 在赋值符 “=” 之前加上其它二目运算符可构成复合赋值符,例如 +=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=
  2. 复合赋值符这种写法,十分有利于编译处理,能提高编译效率并产生质量较高的目标代码,例如 a+=5 等价于 a=a+5r%=p 等价于 r=r%p

本节重点内容

  • 整型变量在内存中的存放形式
  • 实型变量在内存中的存放形式
  • 字符变量在内存中的存放形式
  • 各类数值型数据之间的混合运算
  • 自动类型转换
  • 运算符的优先级与结合性