程序设计基础 第 二 章 谌 卫 军 清华大学软件学院 2008年秋季
第 二 章 数据类型与表达式
2.1 引言 这是什么? 是某种可爱 的动物? 或仅仅是一 些ASCII字符 的组合?
在计算机科学中,基本信息单元是bit(位)! 任何信息都可用若干个bit所组成的位流来表示(即编码:整数、实数、字符等);(why bit?) 二进制的bit容易用计算机硬件来表示和实现,如电路的接通与断开、电压的高与低,等等; 但是对于人来说,二进制的位流是一个“梦魇”,因此需要把数据的外在含义和内部实现区别 开来,即程序员的角度和计算机的角度。
计算机 程序员 0 1 0 0 0 0 1 1 0 1 0 1 0 0 1 1 0 1 0 0 0 1 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 0 ‘C’ ‘S’ ‘E’ ‘1’ 2 二进制形式 VS 字符形式
回忆一下:计算机硬件的体系结构 中央处理器 内存 存储运行原理
内存的工作原理 一个内存中包含有许多 存储单元,每个单元可 以存放一个适当单位的 信息(如:8个bit,即 一个字节,byte); 7 6 全部存储单元按一定顺序编号,这种编号称为存储器的地址。对各个存储单元的读写操作就是通过它们的地址来进行的。 0 1 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 0 1 0 7 6 5 4 3 2 1
2.2 数据类型 问题: 数据是各种各样的,有大 有小、有长有短。有的数据只需要一个存储单元即可,而有的数据可能需要多个连续的存储单元才能放得下,也就是说,不同的数据,可能需要不同长度的存储空间,在这种情形下,怎么办? 数据3 数据2 数据1 7 6 5 4 3 2 1 起始地址+长度
解决之道 —— 数据类型 把所有的数据归纳为有限的几种类型; 同一种类型的数据具有相同的长度,占用相同大小的内存空间; 每一种类型的数据依然是以二进制的形式存放在内存当中; 在访问一个数据时,根据它在内存的起始地址和类型来确定它所占用的存储单元,并把这些单元所存放的内容解释为相应的数据。
整数类型 基本类型 字符类型 实数类型(浮点类型) 数组类型 结构体类型 C的数据类型 构造类型 共用体类型 枚举类型 指针类型
C语言的四种基本类型: 字符类型:用 char 来表示; 整数类型:用 int 来表示; 单精度浮点类型:用 float 来表示; 双精度浮点类型:用 double 来表示。 此外,C语言还有一些类型修饰符:short、long、 signed、unsigned。
2.2.1 整数类型 整数类型可分为:基本型、短整型和长整型三种。 基本型,用 int 表示; 2.2.1 整数类型 整数类型可分为:基本型、短整型和长整型三种。 基本型,用 int 表示; 短整型,用 short int 表示,或略写为 short; 长整型,用 long int 表示,或略写为 long; C语言标准并没有具体规定各种整数类型的数据所 占用的内存字节数,只是要求: long型数据长度 int型数据长度 short型数据长度
计算长度的方法:sizeof 运算符,如 sizeof(int)。 数据类型 字节数 比特数 取值范围 int 4 32 在32位系统上各种整型数据的长度 数据类型 字节数 比特数 取值范围 int 4 32 -231 (231 – 1) short 2 16 -215 (215 – 1) long long型数据长度 = int型数据长度 > short型数据长度 32 32 16
无符号型:有些数据值是永远非负的,如学号、 库存量、人的年龄等。 -32768 -32767 … -2 -1 0 1 2 … 32766 32767 负数区 非负区 短整型的表数范围 无符号整数类型:unsigned int,unsigned short 和 unsigned long。 无符号整型数据的取值范围 数据类型 字节数 比特数 取值范围 unsigned int 4 32 0 (232 – 1) unsigned short 2 16 0 (216 – 1) unsigned long
R进制? 人们在日常生活中最熟悉的是十进制数,但在与计 算机打交道时,会接触到二进制、八进制和十六进 制,但不管是哪种,其共同之处都是进位计数制。 一般来说,如果某种数制只采用R个基本符号,则 称为基R数制(R进制),R称为数制的“基数”,而 数制中每一固定位置所对应的单位值称为“权”。 进位计数制的编码符合“逢R进一,借一当R”的规则, 各个位的权是以R为底的幂,一个数可按照权展开成 多项式。例如一个十进制数2564可按权展开为: 2564=2×103 + 5×102 + 6×101 + 4×100
几种常用的进位数制 二进制 R=2 基本符号 0, 1 八进制 R=8 十进制 R=10 十六进制 R=16 0, 1, 2, 3, 4, 5, 6, 7 十进制 R=10 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 十六进制 R=16 0 – 9, A, B, C, D, E, F 其中,十六进制的符号A~F分别对应于十进制的 10~15。
几种进制之间的转换 (1)R进制转换为十进制 基数为R的数,只要将其各位数字与相应的权相乘,其积相加,和数就是相应的十进制数。 例:110010102 例:34078 例:3C16 =121 + 123 + 126 + 127 =202 = 780 + 081 + 482 + 383 = 1799 = C160 + 3161 = 60
(2)十进制转换为R进制 十进制整数转换成R进制的整数,可以用该十进制数连续地除以R,得到的余数即为R系统的各位系数。此方法称为除R取余法。 例如:将5910转换为二进制数: 59 2 余数 29 2 1 1 0 1 1 1 低位 14 2 所以: 5910=1110112 7 2 3 2 1 2 高位 提问:138的八进制数和十六进制数? (212)8、(8A)16
(3)二、八、十六进制的相互转换 由于这三种进制的权之间有内在的联系,即23=8,24=16,因此它们之间的转换比较容易,即每位八进制数相当于三位二进制数,每位十六进制数相当于四位二进制数。在转换时,位组的划分是以小数点为中心向左右两边延伸,中间的0不能省略,两头不够时可以补0。 例如:将10110102转换成八进制和十六进制数: 1011010 001 011 010 = 1328 = 1 3 2 1011010 0101 1010 = 5A16 = 5 A
将十六进制数F728转换为二进制数: F728 F 7 2 8 = 11110111001010002 = 1111 0111 0010 1000 将八进制数2563转换为二进制数: 2563 2 5 6 3 = 10 101 110 0112 = 010 101 110 011 如何将八进制数和十六进制数进行相互转换呢?
二进制 十进制 八进制 十六进制 0000 0 0 0 0001 1 1 1 ..... 0111 7 7 7 1000 8 10 8 1001 9 11 9 1010 10 12 A 1011 11 13 B 1100 12 14 C 1101 13 15 D 1110 14 16 E 1111 15 17 F
无符号二进制数的取值范围 假设我们有4个二进制位: 它所能够表示的最小数为00002=010; 它所能够表示的最大数为11112=1510; 假设我们有n个二进制位: 它所能够表示的最小数为 ; 它所能够表示的最大数为 ; 2n-1 例如: n = 8,取值范围:0 ~ 255(28-1) ; n = 16,取值范围:0 ~ 65535(216-1) ; n = 32,取值范围:0 ~ 232-1。
有符号的情形 数值是以补码的形式来表示; 假设我们有4个二进制位:xxxx 补码 十进制 补码 十进制 0000 0 1000 -8 补码 十进制 补码 十进制 0000 0 1000 -8 0001 1 1001 -7 0010 2 1010 -6 0011 3 1011 -5 0100 4 1100 -4 0101 5 1101 -3 0110 6 1110 -2 0111 7 1111 -1 数值是以补码的形式来表示; 正数的补码即为原码; 负数的补码等于其绝对值的二进制形式按位取反再加1; 最高位即符号位。
有符号二进制数的取值范围 假设我们有4个二进制位: 它所能够表示的最小数为 -8; 它所能够表示的最大数为 7; 假设我们有n个二进制位: 它所能够表示的最小数为 ; 它所能够表示的最大数为 ; –2n-1 2n-1–1 例如: n = 8,取值范围:-128(-27) ~ 127(27 – 1) ; n=16,取值范围: -215 ~ 215 – 1; n=32,取值范围: -231 ~ 231 – 1 。
有符号短整型数的取值范围 short x = 32767; x = x + 1; unsigned ? …… …… 1 =-32768 1 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1 =-32768 1 1 =-32767 …… 符号位 = 0 …… 1 1 1 1 1 1 1 1 1 1 1 1 1 1 = 32766 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 = 32767 short x = 32767; x = x + 1; unsigned ?
2.2.2 实数类型 实数类型(浮点类型):分为单精度浮点类型 (float)、双精度浮点类型(double)和长双精度 浮点类型(long double)三种。 实型数据的存放形式: 小数部分+指数部分 实数3.14159: + .314159 1 符号位 小数部分 指数部分 + .314159 × 101 =3.14159
约224 小数部分占的位数越多,数据的有效数字越多, 精度越高;指数部分占的位数越多,则能表示的 数值范围越大。 类型 比特数 有效数字 各种实型数据 类型 比特数 有效数字 数值范围 float 32 6~7 10-38 ~ 1038 double 64 15~16 10-308 ~ 10308 long double 128 18~19 10-4932 ~ 104932
2.2.3 字符类型 字符类型:一个字符型数据只占用一个字节的空间。 字符类型的双重属性:整数属性和字符属性。 整数属性: 2.2.3 字符类型 字符类型:一个字符型数据只占用一个字节的空间。 字符类型的双重属性:整数属性和字符属性。 整数属性: 字符类型即单字节的整数类型。 char unsigned char 8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 1 =-128 = 0 …… …… = 0 1 1 1 1 1 1 1 = 127 …… …… 1 1 1 1 1 1 1 = 127 1 1 1 1 1 1 1 1 = 255
字符型数据的值即为相应字符的 Ascii 码。 字符属性: 字符型数据的值即为相应字符的 Ascii 码。 Ascii 值 字符 48 ‘0’ 49 ‘1’ 50 ‘2’ 51 ‘3’ 52 ‘4’ 53 ‘5’ 54 ‘6’ 55 ‘7’ 56 ‘8’ 57 ‘9’ Ascii 值 字符 65 ‘A’ 66 ‘B’ 67 ‘C’ 68 ‘D’ 69 ‘E’ 70 ‘F’ 71 ‘G’ 72 ‘H’ 73 ‘I’ 74 ‘J’ Ascii 值 字符 97 ‘a’ 98 ‘b’ 99 ‘c’ 100 ‘d’ 101 ‘e’ 102 ‘f’ 103 ‘g’ 104 ‘h’ 105 ‘i’ 106 ‘j’
2.3 常量 常量:在程序运行过程中,其值不能被改变的量。不同的 数据类型,有不同的常量。 2.3 常量 常量:在程序运行过程中,其值不能被改变的量。不同的 数据类型,有不同的常量。 根据常量的表示方法,可分为两类: (1) 字面常量:从其字面形式即可判别,如12、4.6、‘a’等。 (2) 符号常量:用一个标识符来代表一个常量。 #define PRICE 30 main( ) { int num, total; num = 10; total = num * PRICE ; printf(“total = %d”, total); } 符号常量
2.3.1 整型常量 三种表示形式: 十进制形式,如123,– 456,0; 2.3.1 整型常量 三种表示形式: 十进制形式,如123,– 456,0; 八进制形式,以0开头的数是八进制数。 0123 = 00123 = 000123 ≠ 123 0123 = (123)8 = 1×82+2×81+3 = 83 十六进制形式,以0x开头的数是十六进制数; 0x123 = (123)16 = 1×162+2×161+3 = 291 二进制形式?
2.3.2 实型常量 两种表示形式: 十进制小数形式,它由数字和小数点组成(注意 必须有小数点)。如 .123, 123., 123.0, 0.0; 指数形式,如321.54e6 = 321.54×106 整数 . 小数 e 指数 或者 E 须为整数 须有数字 错误的指数形式:E3、2.5E3.5、.E3、2.5E
2.3.3 字符常量 定义:用单引号括起来的一个字符,如‘a’, ‘A’, ‘#’。 字符常量的整数属性与字符属性: 2.3.3 字符常量 定义:用单引号括起来的一个字符,如‘a’, ‘A’, ‘#’。 字符常量的整数属性与字符属性: main( ) { int num; char ch1, ch2, ch3; ch1 = ‘A’; // ch1 = 65 ch2 = ‘Z’; // ch2 = 90 num = ch2 – ch1 + 1; // num = 26 ch3 = ch1 + 1; // ch3 = ‘B’ } ('F' − 'A')+'d' = 'i'
转义字符表 如: '\x41' Why 转义字符? 符号 ASCII值 含义 \a 007 响铃 \b 008 退格 \n 010 换行 \r 013 回车 \t 009 水平Tab键 \v 011 竖直Tab键 \’ 039 单引号 ‘ \” 034 双引号 “ \\ 092 反斜杆 \ \ooo - 八进制表示的字符 \xhhh 十六进制表示的字符 如: '\x41'
符号 ASCII值 八进制表示 十六进制表示 24 ‘\30’ ‘\x18’ 25 ‘\31’ ‘\x19’ 26 ‘\32’ ‘\x1A’ 27 ‘\33’ ‘\x1B’ printf("%c %c %c %c\n", '\30', '\31', '\32', '\33'); printf("%c %c %c %c\n", '\x18', '\x19', '\x1A', '\x1B'); 输出结果:
字符串常量 定义:用一对双引号括起来的一串字符序列,如: “How do you do.”, “China”, “a”, “$123.45”。 存储方式:字符串中的每一个字符各占用一个字节,最后增加一个空操作字符‘\0’结尾。 H o w d y u do . \0 C h i n a \0 a \0 ‘a’ = ≠ a $ 1 2 3 . 4 5 \0
通过 b 可以找到相应的存储空间地址XXXX, 2.4 变量 2.4.1 基本概念 变量:其值可变的量。 b 变量名 ————变量值 存储空间地址XXXX 30 通过 b 可以找到相应的存储空间地址XXXX, 从而对该变量的值进行访问和修改。
2.4.2 变量的命名 变量的命名规则: 仅包含字母、数字和下划线(‘_’); 第一个字符必须为字母或下划线; 2.4.2 变量的命名 变量的命名规则: 仅包含字母、数字和下划线(‘_’); 第一个字符必须为字母或下划线; 不能使用C语言保留的“关键字”来作为变量名, 如int, float等; 变量名是大小写有关的,例如:sum和SUM是两 个不同的变量名。
合法的变量名: 是否合法? sum, average, _total, Class, Stu_name, LI float&variable, Main, M.John, _int 12a, if, a>b, average_samples_div_count ╳ √ ╳ √ ╳ ╳ ╳ √
2.4.3 变量的定义 变量的使用原则:“先定义,后使用”。 定义方法: 数据类型 变量列表; 变量列表:变量1, 变量2, …, 变量N 2.4.3 变量的定义 变量的使用原则:“先定义,后使用”。 定义方法: 数据类型 变量列表; 变量列表:变量1, 变量2, …, 变量N int lower, upper, step; // 定义三个整型变量 char c, line[1000]; // 定义一个字符变量和一个 // 字符数组 int lower; int upper; int step; char c; char line[1000];
为什么要先定义? 每一个变量被指定为一确定类型,在编译时就能 为其分配相应的存储空间。例如,字符型变量需 要1个字节,int型变量需要4个字节;float型变量 需要4个字节; 指定每一个变量属于某种类型,这就便于在编译时,据此检查该变量所进行的运算是否合法。例如,整型变量a和b可以进行求余操作:a % b,若将a、b指定为实型变量,则不允许该操作,在编译时会给出有关的“出错信息”。
void main( ) { int a; double b; char c; a = 10; b = 3 void main( ) { int a; double b; char c; a = 10; b = 3.14; c = 127; c++; } . . . 0x0012FF80 a 127 3.14 10 0x0012FF7C b 0x0012FF74 c 0x0012FF70 . 内存 源程序
2.4.4 变量的初始化 C 语言允许在定义变量的同时进行初始化,例如: 2.4.4 变量的初始化 C 语言允许在定义变量的同时进行初始化,例如: char esc = ‘\\’; // 定义一个字符变量,初始值为‘\’ int i = 0; // 定义一个整型变量,初始值为0 float eps = 1.0e-5; // 定义并初始化一个实数变量 int a = 3, b, c = 3; // 只初始化 a 和 c
2.5 运算符和表达式 C 语言的运算符范围很宽,把除了控制语句和输入输出以外 的几乎所有的基本操作都作为运算符处理。 2.5 运算符和表达式 C 语言的运算符范围很宽,把除了控制语句和输入输出以外 的几乎所有的基本操作都作为运算符处理。 1. 算术运算符 + - * / % 2. 关系运算符 > < == >= <= != 3. 逻辑运算符 ! && || 4. 位运算符 << >> ~ | ^ & 5. 赋值运算符 = 及其扩展赋值运算符 6. 条件运算符 ? : 7. 逗号运算符 , 8. 指针运算符 * & 9. 求字节运算符 sizeof 10. 强制类型转换运算符 (类型) 11. 分量运算符 . -> 12. 下标运算符 [ ] 13. 其他 如函数调用运算符 ( )
什么是表达式? 什么是表达式:有“值”的式子; 简单的表达式:一个变量就是一个表达式(如:变量r),一个常量也是一个表达式(如: 3.14); 通常的表达式:由一些变量、常量和运算符组合在一起所形成的一个式子,例如: 3.14 * r * r 表达式中还可以包含函数调用,例如: a + 2*sqrt(a*b) + b 即
2.5.1 算术运算符和算术表达式 1. 基本的算术运算符 012 % 3 = 1 +:加法运算符,或正值运算符。如 3 + 5、+3 2.5.1 算术运算符和算术表达式 1. 基本的算术运算符 +:加法运算符,或正值运算符。如 3 + 5、+3 -:减法运算符,或负值运算符。如 5 – 2、 –3 * :乘法运算符。如 3 * 5; / :除法运算符。如 5 / 3。两个整数相除,结果 为整数,小数部分被舍去; %:模运算符,或称求余运算符,%两侧均为 整型数据,如 7 % 4 = 3。 012 % 3 = 1
299 / 100 2 6 / 4 1 5 / 6 0 ————————— 299 % 100 99 6 % 4 2 5 % 6 5
volume = (4.0 / 3.0) * pi * r * r * r; 整数 VS 实数(1) // 计算一个球体的体积 int r; // 半径 double volume; // 体积 double pi = 3.1415926; volume = (4/3) * pi * r * r * r; 危险代码 4/3 等于1 volume = (4.0 / 3.0) * pi * r * r * r; double类型
整数 VS 实数(2) if (x == 5) ? 有些数据只有整数才有意义,例如,他家有三口 人,不能说“他家有2.86个人”; 用实数来表示一个整数的时候,可能是一种不精确的表示,例如,在数学上,3 * 15 * (1/3) = 15; 但3.0 * 15.0 * (1.0 / 3.0) 不一定等于15.0。 float a = 1575.6; int c; c = a * 10 - 15750; // c 等于? if (x == 5) ? c = 5 (5.999756)
2. 算术表达式与运算符的优先级和结合性 算术表达式:用算术运算符和括号将运算对象(也称 操作数)连接起来的、符合C语法规则的式子。运算 对象包括常量、变量、函数调用等。 运算符的优先级和结合性:在表达式求值时,先按 运算符的优先级的高低次序执行,如先乘除后加减。 如果在一个运算对象两侧的运算符的优先级别相等, 则按规定的“结合方向”处理。算术运算符的结合方向 为“从左到右”,即先左后右,也称为“左结合性”。
算术运算符的优先级规则 先计算括号( )当中的内容,如果有多重括号,先 从最里面开始; 然后处理负值运算符“-”; 然后处理“乘法类”运算符,包括:*、/ 和 %; 最后处理“加法类”运算符,包括+和-。
算术运算符的结合性 15 / 4 * 2 = ? 算术运算符是左结合,如a / b * c = (a / b) * c, a + b – c + d = ((a + b) – c) + d。 nA * 4 + nB * 3 + nC * 2 3 1 12 2 15 17
3. 自增和自减运算符 ++、 – –:作用是使变量的值加 1 或减 1。 ++i、 – –i:在使用 i 之前,先使 i 的值加 1 或减 1; i++、i– –:在使用 i 之后,使 i 的值加 1 或减 1; 若 i = 3 (1) j = ++i; // i 的值先加1,再赋给 j,故j=4 (2) j = i++; //先把 i 的值3赋给 j,使得 j = 3, // 然后 i 加1变成4; 记忆方法: 运算符在前:先运算,后使用; 运算符在后:先使用,后运算。
4. 算术表达式中的函数调用 C语言提供了一些数学函数,可以象通常的常量 和变量那样,用于算术表达式当中。例如: sqrt (2.0),2.1 * sin(theta / 1.5) + 17.0 这些数学函数被组织在库文件中,在使用之前,要把相应的头文件 math.h包含进来。 #include <math.h> main( ) { root = sqrt(2.0); …… } 包含了各种数学 函数:sqrt, sin, cos, 等等。
C语言提供了几百个数学函数,放在函数库中,这里只介绍其中最常用的13个函数。 1、求绝对值函数 (1) 函数原型为 int abs(int x) 自变量为整数,函数值也为整数。 例 abs(-4)=4 (2) 函数原型为 long labs(longx)自变量为长整数,函数值也为长整数。 例 labs(-41576)=41576 (3) 函数原型为 double fabs (double x)自变量为双精度实数,函数值也为双精度实数。 例 fabs (-3.14159)=3.14159
2、正弦函数 函数原型为 double sin (double x) 自变量和函数均为双精度实数。其中x为弧度值。 例 sin (3.1415926535/2)=1 3、反正弦函数 函数原型为 double asin (double x) 自变量和函数均为双精度实数。 例 asin (0.32696)=0.333085 这里的 asin(x) 就是数学中的 arcsin(x)
4、余弦函数 函数原型为 double cos (double x) 自变量和函数均为双精度实数。 例 cos (3.1415926535/2)=4.48966e-011 注意cos(π/2 )的只由于π是近似值算出的余弦值不为0,但十分接近0,该值是 4.48966×10‾¹¹ 5、反余弦函数 函数原型为double acos (double x)自变量和函数均为双精度实数。 例 acos (0.32696)=1.23771 这里的acos(x)就是数学中的arccos(x)
6 、正切函数 函数原型为 double tan(double x) 自变量和函数均为双精度实数。 例 tan (3.1415926535/4)=1 7、反正切函数 函数原型为 double atan(double x) 自变量和函数均为双精度实数。 例 atan (-862.42)=-1.56964 这里的 atan(x) 就是数学中的 arctan(x)
8、计算 函数 函数原型为 double exp(double x) 自变量和函数均为双精度实数。 例 exp(1)=2.71828 exp(2.302585093)=10 9、 计算 函数 函数原型为double log(doublex),自变量和函数均为双精度实数。 例 log(2.71828)=0.999999
10、计算 函数 函数原型为 double log10(doublex),自变量和函数均为双精度实数。 例 log10(10)=1 11、 计算 函数 函数原型为 double pow(double x, double y),自变量x,y和函数 均为双精度实数。 例 pow(2.0, 3.0)=8
12、计算不大于自变量x的整数值函数 函数原型为 double floor (double x) 自变量和函数均为双精度实数。 例 floor(2.8)=2 floor(-2.8)=-3 13、计算 函数 函数原型为 double sqrt(double x) 自变量和函数均为双精度实数。 例 sqrt(42.25)=6.5
2.5.2 赋值运算符和赋值表达式 赋值运算符 “=”:把一个数据赋给一个变量。a = 3 2.5.2 赋值运算符和赋值表达式 赋值运算符 “=”:把一个数据赋给一个变量。a = 3 复合的赋值运算符:在赋值运算符“=”之前加上其他 的运算符,可以构成复合的赋值运算符。 +=、 –=、*=、/=、%=、<<=、>>=、&=、^=、|= 变量 Op= 表达式 变量 = 变量 Op 表达式 a += 3 等价于 a = a + 3 x *= y + 8 等价于 x = x * (y + 8) x %= 3 等价于 x = x % 3 本质:一种缩写,原因有二:一是为了简化程序,使程序 精炼;二是为了提高编译的效率。
赋值表达式:由赋值运算符将一个变量和一个表达式 连接起来的式子。 赋值表达式:由赋值运算符将一个变量和一个表达式 连接起来的式子。 形式:<变量> <赋值运算符> <表达式>,如 a = b + 5; 赋值表达式的求解过程:将赋值运算符右侧的“表达式” 的值赋给左侧的变量,而赋值表达式的值即为该值。 赋值表达式的双重功能:(1) 给变量赋值,(2) 把该值 作为表达式的结果提供给外界。 a = ( b = 4) + (c = 6); // 做了三件事情,分别给b、c、a赋值; 结果为4 结果为6 printf(“%d”, a = b); 等价于 a = b; printf(“%d”, a);
有程序如下: int a = 1; int b = 0; int c = 0; int d = (++a) * (c = 1); 则a, b, c, d的值为: 2,0,1,2
2.6 类型转换 什么是类型转换? 把一种类型的数据转换成另外一种类型。 类型转换的三种情形: 运算转换(系统自动进行); 2.6 类型转换 什么是类型转换? 把一种类型的数据转换成另外一种类型。 类型转换的三种情形: 运算转换(系统自动进行); 赋值转换(系统自动进行); 强制转换(程序员指定)。
2.6.1 运算转换 运算转换:如果运算符带有不同数据类型的运算对象, 那么需要根据一些转换规则,把它们先转 换成一种共同的数据类型,然后再运算。 2 * 3.14 = ? 当一个int型数据和一个double型数据混合运算时, 编译器会自动地把int型数据先转换为double类型。 2 * 3 * 3.14 (2 * 3) * 3.14 6 * 3.14 6.0 * 3.14 18.84 2 / 3 * 3.14 (2 / 3) * 3.14 0 * 3.14 0.0 * 3.14 0.0 尽量避免使用混合的数据类型,改为:2.0 / 3.0 * 3.14
转换规则 基本原则:把“窄一点”的运算对象转换成一个“宽一点”的运算对象(避免精度的损失); 首先,如果有一个运算对象为 long double,那么把另外一个也转换成 long double; 否则,如果有一个运算对象为 double,那么把另外一个也转换成 double; 否则,如果有一个运算对象为 float,那么把另外一个也转换成 float; 否则,把 char 类型和 short 类型转换成 int; 然后,如果有一个运算对象为 long,则把另一个也转成 long。
2.6.2 赋值转换 赋值转换:如果赋值运算符两侧的类型不一致,但 都是数值型或字符型,则在赋值时要进行类型转换。 2.6.2 赋值转换 赋值转换:如果赋值运算符两侧的类型不一致,但 都是数值型或字符型,则在赋值时要进行类型转换。 int total, count, value; double avg; total = 25; count = 10; avg = total / count; /* avg is ? */ value = total * 2.5; /* value is ? */ 自动转换为 double类型。 2.0 62 自动转换为int 类型,即舍去 小数部分。
转换规则(1) 将实型数据(包括单、双精度)赋值给整型变量时,舍弃实数的小数部分。如 i 为整型变量,执行“ i = 3.56”的结果是使 i 的值变成 3,在内存中 以整数形式存储; 将整型数据赋值给单、双精度浮点变量时,数值不变,但以浮点数形式存储到变量中。如 f 为实数变量,f = 23,先把整数 23 转换成实数23.000,再存储在 f 当中; 将一个 double 型数据赋值给 float 变量时,截取其前面的7 位有效数字,存放到 float变量的存储单元中。反之,将一个float型数据赋值给double 变量时,数值不变,有效位扩展到16位;
转换规则(2) 将字符型数据赋值给整型变量时,由于字符只占 1 个字节,而整型变量为 4 个字节,因此把字符数据放到整型变量的低 8 位当中; 将一个 int、short、long 型数据赋值给一个 char 型变量时,只将其低 8 位原封不动地送到 char 型变量; 将带符号的 short 型数据赋值给 int 或 long 型变量时,要进行符号扩展,先将 short 型数据的 16 位送到 int 或 long 型变量的低 16 位,然后根据它的正负来决定在 int 或 long 型变量的高 16 位是补 0 还是补 1;
转换规则(3) 将 unsigned short 型数据赋值给 int 或 long 型变量时,不存在符号扩展问题,只需将高位补 0 即可。如果将一个unsigned类型数据赋值给一个占字节数相同的整型变量,那么将unsigned型变量的内容原样送到目标变量中; 将非 unsigned 型数据赋值给长度相同的unsigned型变量,也是原样赋值(把原有的符号位也作为数值一起传送)。 总之,不同类型的整型数据之间的赋值本质上就 是按存储单元的存储形式直接传送。
2.6.3 强制转换 强制转换:程序员使用强制类型转换运算符将一个 表达式的值转换成所需的数据类型。 2.6.3 强制转换 强制转换:程序员使用强制类型转换运算符将一个 表达式的值转换成所需的数据类型。 用法: (类型) 表达式,如: (int) 3.6,(int) (x + y) 说明:(1)转换的目标是紧邻运算符的表达式, (int) 3.6 + 1.5 ≠ (int) (3.6 + 1.5) (2)在强制类型转换时,并不改变原来变量的类型, 而是生成一个目标类型的中间变量。 main ( ) { float x = 3.6; int i; i = (int)x; // i = 3, x = 3.6 }
考察下列程序,计算 num4 的值: num4的值为: 10.0 int num1, num2; double num3, num4; num4 = num1/num2 + (int)(num3*(num3/2)); num4的值为: 10.0
- END -