2.1 数据类型
数据的特征在程序设计中体现为数据类型,它包括数据结构和可施于数据的操作(运算)。
一种数据类型(data type)可看作由两个集合构成:值集和操作(运算)集
- 值集:描述了该数据类型可包含哪些数据以及这些数据的结构(组织形式)
- 操作集:描述了对值集中的数据能实施哪些运算
数据类型分为简单数据类型和复合数据类型
- 简单数据类型:构成类型值集的数据是不可再分解的简单数据(整型、实数型)
- 复合数据类型:构成类型值集的数据是由其他类型的数据按照一定的方式组合而成的(向量、矩阵)
程序设计语言分为静态类型语言和动态类型语言
- 静态类型语言(statically typed language)
- 程序运行前必须为每个数据指定类型
- 采用编译执行,运行效率高
- 动态类型语言(dynamically typed language)
- 不要求在静态程序中明确指出数据的类型,而是在程序运行中数据被用到时才确定它们的类型
- 采用解释执行,使用灵活,运行效率不高
C++是静态类型语言,数据类型分为
- 基本数据类型(fundamental data type):语言预先定义好的数据类型,又称标准类型(standard type)或内置类型(build-in type)
- 整数类型(integer type)
- 用于描述通常的整数,用2的补码表示,分为
- int:在字长16位计算机上,占2字节;在32位计算机上,为4字节
- short int 或 short:占2字节(16位),范围-32768~32767
- long int 或 long:占4字节(32位),范围-2147483648~2147483647
- 无符号整数类型,表示范围大于带符号数整数,提高程序的可靠性以及表示一些由二进制位构成的非数值数据
- unsigned int 或 unsigned
- unsigned short int 或 unsigned short
- unsigned long int 或 unsigned long
- 用于描述通常的整数,用2的补码表示,分为
- 实数类型(又称浮点型 (float point type))
- float:占4字节(32位),表示范围-3.402823466*1038~3.402823466*1038
- double:占8字节(64位),表示范围-1.7976931348623158*10308~1.7976931348623158*10308
- long double
- 字符类型(character type)
- char
- 用于描述文字类型数据中的单个字符
- 对于字符类型的数据,计算机存储的是他们的编码
- ASCII(American Standard Code for Information Interchange,美国标准信息交换码)
- 采用单字节(8位)编码的字符集,包含了英文的基本字符及其编码,最多能表示256个字符
- Unicode国际通用大字符集,采用2~4个字节编码,用wchar_t描述这个字符集的字符
- 编码是整数表示,C++把char类型的数据当作比short int所代表的数值范围更小的整数类型来看待,可以参加算术运算
- char可以作为signed char或unsigned char参与算术运算
- 逻辑类型(又称布尔类型,boolean type)
- 由于描述“真”和“假”这样的逻辑值
- bool类型的值由两个:true(1)和false(0),使用一个字节(8位)存储
- 空值类型(void type)
- 值集为空的类型,用void表示
- 整数类型(integer type)
C++中,把各种int、char、wchar_t以及bool统称为整型(integral type);把整型和实数型统称为算术型(arithmetic type)
- 构造数据类型
- 枚举类型
- 数组类型
- 结构类型
- 联合类型
- 指针类型
- 引用类型
- 抽象数据类型
- 类
- 派生类
2.2 数据的表现形式
待处理的数据通常以常量和变量形式出现。
2.2.1 常量
常量是指程序执行过程中不变(或不能改变)的数据。
C++把常量分为几种类型:整数类型、实数类型、字符类型、逻辑类型、字符串类型、指针类型
常量以两种形式存在:字面常量和符号常量
- 字面常量
字面常量(又称直接量,literal)是指在程序中直接写出常量值的常量。
字面常量包括:
- 整数类型字面常量
- 可用十进制、八进制和十六进制(以0x或0X打头)形式来书写
- 默认为int
- 在整数类型字面常量后面加L,表示long int类型的字面常量
- 在整数类型字面常量后面加U,表示unsigned int类型的字面常量
- 在整数类型字面常量后面加LU,表示无符号长整数类型字面常量
- 实数类型字面常量
- 采用十进制形式书写
- 小数表示法
- 由整数部分、小数点和小数部分组成
- 科学记数法
- 在小数或整数后加一个指数部分,指数部分由E(或e)和一个整数构成,表示基数为10的指数,如4.5678E2,-5.7e-3等
- 小数表示法
- 默认为double
- 在实数型字面常量后面加F,表示float类型
- 在实数型字面常量后面加L,表示long double类型
- 采用十进制形式书写
- 字符类型字面常量
- 一般由一对单引号和一个字符来表示
- 也可以用一对单引号括起来的一个字符编码来表示,这时必须用转移序列(以反斜杠(\)开头的一串字符)来书写编码,编码可以采用八进制或十六进制
- 八进制:'\ddd',ddd为三位八进制数
- 十六进制:'\xhh',hh为两位十六进制数
字母A的三种表示:'A'、'\101'、'\x41'
- 字符类型字面常量还可以用一些特殊的转移序列符号来表示
符号 | 含义 | 符号 | 含义 |
\a | 响铃 | \v | 纵向制表 |
\b | 退格 | \' | 单引号 |
\f | 换页 | \" | 双引号 |
\n | 换行 | \\ | 反斜杠 |
\r | 回车 | \0 | 字符串结束符 |
\t | 横向制表 |
|
|
反斜杠应该写成'\\'
单引号应该写成'\''
双引号应该写成'\"'或""
- 在内存中占一个字节
- 字符串字面常量
- 由双引号括起来的字符序列构成
- 当字符串中包含"时,应写为:\"
- 符号常量
符号常量(symbolic constant,又称命名常量)是指有名字的常量。
在程序中可以给常量取一个名字并指定一个类型,然后在程序中通过常量的名字来使用这些常量。
符号常量的定义格式:
const <类型名> <常量名>=<值>; const double PI=3.14; 能对常量的使用进行类型检查,C++通常采用这种形式
或
#define <常量名><值>; #define PI 3.14; C语言中使用,采用编译预处理命令——宏定义来实现
符号常量的优势:
- 增加程序的易读性
- 提高程序对常量使用的一致性
- 增强程序的易维护性
2.2.2 变量
在程序中,可变的数据用变量(variable)表示,一个变量往往包含了名字、类型、值以及内存空间和地址等特征。
1、变量的名字
用标识符来表示,不能是C++的关键字
2、变量的类型
每个变量都有类型,类型决定了该变量的取值范围,运算方式,所需内存空间大小。
3、变量的值
每个变量都有一个值,这个值就是变量所表示的数据。变量的值是所属类型值集中的值。
4、变量的内存空间及地址
在程序运行时,程序中的每个变量都有一块内存空间,从而他们都有一个内存地址,从本质上讲,变量实际上是内存空间的一个抽象。
在低级语言程序中,用内存地址来访问内存;在高级语言中,用变量来访问内存。
C++中,每个变量都必须定义(definition)。定义变量时需指出变量的名字和类型,并且提供一个初值。
定义变量的格式:
<类型名> <变量名> = <初值>;
- 类型名:包括基本数据类型、构造数据类型和抽象数据类型
- 变量名:标识符,不能为C++中的关键字
- 初值:一般是一个常量,也可以是相应类型的一个表达式
2.2.3 变量值的输入
- 用C++标准库中定义的输入对象cin和抽取操作符“>>”来实现
- 用C标准库中的函数scanf来实现
void getInput(){
inti;
doubled;
strings;
//从键盘中读取一个整数给变量i
cin>>i;
//从键盘中读取一个浮点数给变量d
cin>>d;
//从键盘中读取字符串给变量s
cin>>s;
cout<<"i:"<<i<<",d:"<<d<<",s:"<<s<<endl;
}
从键盘输入数据时:
- 需要用空白符(空格符、制表符、回车符)作为输入数据之间的分隔符
2.3 操作符
操作符(operator)用于描述对数据的基本操作,又称运算符。
- 这些数据称为操作数(operand)
- 通常情况,操作符不会改变操作数的值,运算结果保存在临时的存储单元(内存或寄存器)内
- 带有副作用(side effect)的操作符(如赋值=、自增++、自减--等操作符)在得到运算结果的同时,也会改变操作数的值
- 根据操作符的功能,分为算术操作符、关系操作符、逻辑操作符、位操作符、赋值操作符
- 根据操作数个数,分为单目(一个操作数)、双目(两个操作数)、三目(三个操作数)
2.3.2 算术操作符
算数操作符(artithmatic operator)用于实现数值运算,操作数类型为算术型。
- +、-、*、/、%
- /:表示整除,结果为整数部分、小数部分直接舍去
- %:计算两个操作数相除的余数,操作数的类型应为整型
(a/b)*b+a%b = a
- 取负(-)、取正(+)
- 自增(++)、自减(--)
- 前置的“++”表示“先加后用”、后置的”++”表示先用后加
- “++”和“--”是两个带副作用的操作符,改变了操作数的值
算术操作可能出现溢出(overflow)的问题,可以通过显式类型转换来解决
2.3.3 关系与逻辑操作符
- 关系操作符(>(大于)、<(小于)、>=(大于等于)、<=(小于等于)、==(相等)、!=(不等))
- 结果为bool类型:true(1) or false(0)
- 操作数类型通常是算术型
- 对浮点数进行“==”和“!=”比较运算,采用判断两者的差的绝对值是否小于某个很小的数来实现
- 由于某些十进制小数(如0.1)无法用二进制准确表示,在计算机内部表示的是它们的近似值。下面的比较运算结果为0(false)。
float d1=0.1, d2=0.2;
cout<<((d1+d2)==0.3)<<endl;
- 浮点数的比较运算
//判断d1==d2
cout<<"d1==d2:"<<(d1==d2)<<endl;
cout<<"fabs(d1-d2)<1e-6:"<<(fabs(d1-d2)<1e-6)<<endl<<endl;
//判断d1!=d2
cout<<"d1!=d2:"<<(d1!=d2)<<endl;
cout<<"fabs(d1-d2)>1e-6:"<<(fabs(d1-d2)>1e-6)<<endl<<endl;
运行结果:
0
d1==d2:0
fabs(d1-d2)<1e-6:0
d1!=d2:1
fabs(d1-d2)>1e-6:1
注:fabs在标准库cmath中
关于浮点数比较大小的说明:
- 除了能用2的指数幂乘以整数表示的浮点数能够被精确的表示外,其余的浮点数都是近似表示的。
- 在c++中,两个浮点数相差在DBL_EPSILON(2.22045e-16)之内的数都认为是相等的
- 如何判断两个浮点数是否相等?
例:判断 float a和float b是否相等?
- 使用绝对误差的方式:abs(a-b)<DBL_EPSILON
- 但当两个数的数量级很大时,例如a=1000000000000.00001,b=1000000000000.0000,两数的相对误差为(1000000000000.00001-1000000000000.0000)/1000000000000.0000=1e-14<DBL_EPSILON,这两数也是相等的,可是如果考虑绝对误差1000000000000.00001-1000000000000.0000=1e-5>DBL_EPSILON,使用相对误差可以回避掉数字数量级较大的问题,不过,它对于数量级较小的问题解决起来效果不佳。
- 若a=0,b=0.0000 0000 0000 0000 1,两者绝对值小于DBL_EPSILON,考虑绝对误差相等;单考虑相对误差时,浮点数/0=∞,出现异常!此时应先判断a与b的大小(abs(a)与abs(b)),在进行相减、除法运算。
- 在某些特殊情况下, 相对误差也不能代表全部,比如在判断空间三点是否共线的时候,使用判断点到另外两个点形成的线段的距离的方法的时候,只用相对误差是不够的,因为线段距离可能很短,也可能很长,点到线段的距离,以及线段的长度做综合比较的时候,需要相对误差和绝对误差结合的方式才可以。
//c++中的abs函数已经被重载,因此可以适用int,longint,float等各种类型。
//如果是c请调用fabs用于浮点数的绝对值。
bool Selection::cmpFloat(float a,float b){
if(a==b) return true;
if(abs(a-b)<DBL_EPSILON) return true;
if(abs(a>b)){
return abs((a-b)/a>DBL_EPSILON)!=0;
}else{
return abs((a-b)/b>DBL_EPSILON)!=0;
}
}
- 逻辑操作符
C++提供了三个逻辑操作符:&&(与)、||(或)、!(非)
- 逻辑“与”(&&)
- 只有当两个子条件均为true时,操作符“&&”的运算结果才为true,否则为false。
- 逻辑“或”(||)
- 只要两个子条件中有一个为true,操作符“||”的运算结果就为true,否则为false。
- 逻辑“非”(!)
- 单目操作符
- 实现与操作数所表达的条件相反的条件,操作结果为true或false
- 短路求值
- 对与逻辑“与”操作和逻辑“或”操作,如果第一个操作数已能确定运算结果了,则不再计算第二个操作数的值
- 短路求值一方面提高逻辑运算效率,另一方面为逻辑运算中其他运算提供一个“卫士”(guard)
2.3.4 位操作符
对一些由纯二进制位(C++中用整型(无符号整型)表示)构成的非数值数据进行处理,这些二进制位对应着设备的状态以及图像的位图信息等。
1、逻辑位操作
C++中的逻辑位操作符:~(按位“取反”),&(按位“与”),|(按位“或”),^(按位“异或”)
按位“取反”(~)
- 单目操作符
- 把操作数的每一个二进制位分别取反
- ~0x6F3A = 0x90C5
- ~0110 1111 0011 1010 == 1001 0000 1100 0101
按位“与”(&)
- 把两个操作数的二进制位分别按位进行“与”运算
- 0&0 = 0
- 0&1 = 0
- 1&0 = 0
- 1&1 = 1
- 0x3F80 & 0x7AE5 = 0x3A80
- 0011 1111 1000 0000 & 0111 1010 1110 0101 == 0011 1010 1000 0000
按位"或"(|)
- 把两个操作数的二进制位分别按位进行“或”运算
- 0|0 = 0
- 0|1 = 1
- 1|0 = 1
- 1|1 = 1
- 0x3F80 | 0x7AE5 = 0x7FE5
- 0011 1111 1000 0000 | 0111 1010 1110 0101 == 0111 1111 1110 0101
按位"异或"(^)
- 把两个操作数的二进制位分别按位进行“异或”运算
- 两位相同,结果为0;两位不同,结果为1
- 0^0 = 0
- 0^1 = 1
- 1^0 = 1
- 1^1 = 0
- 一个数与另一个数进行两次按位“异或”操作的结果与该数相同
- (x^a)^a = x
- 用于在程序中对所管理的资源或设备的状态进行识别与设置
- 对屏幕上显示的图形数据用按位“异或”操作实现图形的快速移动
2、移位操作
移位操作是双目运算符。
基本功能:把第一个操作数按二进制位依次左移或右移由第二个操作数指定的位数。移位操作包括:<<(左移)和>>(右移)
- 左移操作
左移时高位舍弃,低位补0
- 0x3F61<<2==0xFD84
- 0011 1111 0110 0001 << 2 == 1111 1101 1000 0100
- 右移操作
无符号数:高位补0;
带符号数:高位补符
- unsigned char 0xA6 >> 2 == 0x29
- 1010 0110 >>2 = 0010 1001
- signed char 0xA6 >> 2 == 0xE9
- 1010 0110 >>2 == 1110 1001
移位操作可以实现对图形/图像数据的处理,和特殊的乘法/除法运算
2.3.5 赋值操作符
程序中变量的值除了通过输入/输出改变还可以通过赋值操作(assignment)来实现
1、简单赋值操作符
- “=”为简单赋值操作符,它具有明确的内存地址,程序中能显式的访问该地址所指的内存单元,并且该内存单元中的内容可以被修改。
- 含义:用右边操作数的值来改变左边操作数的值,结果为改变值之后的左操作数
2、复合赋值操作符
- +=、-=、/=、%=、&=、|=、^=、<<=、>>=
- 以a#=b为例,按照a = a # (b)来解释
赋值操作符在得到一个结果的同时,还改变第一个(左边)操作数的值,因此C++的赋值操作是一个带副作用的操作。
赋值操作涉及CPU与内存之间的数据传输,限制了计算机的计算效率。
2.3.6 其他操作符
条件操作符(?:)
- 三目运算符
- d1?d2:d3
- d1为ture,结果为d2,否则为d3
- d1?d2:d3
逗号操作符(,)
用逗号把若干个运算连接起来构成一个复合运算,表示更清晰
sizeof操作符
计算某类型的数据所占用的内存大小(字节数),如size of(int)==4
2.3.7 操作数的类型转换
C++的类型转换有两种方式:显示转换和隐式转换。
两者都不会改变被转换数本身,转换的结果保存在临时的存储单元中。
1、隐式类型转换
- 算术操作的类型转换
常规算术转换规则(usual arithmetic conversions):
- 浮点数之间进行隐式转换时:long double、double、float
- 整数与浮点数运算:将整数转为浮点数
- 带符号数与无符号数运算:转为无符号数
整型提升(integral promotion):
- char、signed char、unsigned char、short int、unsigned short int类型,如果int能够表示他们的值,转为int,否则转为unsigned int
- bool型转为int,true为1;false为0
- wchar_t和枚举类型转换为下列按次排序第一能表示所有值的类型:int、unsigned int、long int、unsigned long int
关系操作的类型转换
表述多个逻辑条件时,不能按照数学上习惯的表达方式
- 如:a==b==c
- 含义:把a==b的结果(bool类型)与c进行“等于”比较
- 正确表达方式:(a==b)&&(b==c)
逻辑操作的类型转换
若算术型操作数参加逻辑运算,操作前会进行逻辑转换,转换规则:“零”转换成“false”,“非零”转换成true
- int i && int j
- 若i和j都不等于0,结果为true,否则为false
- 提高易读性:(i!=0)&&(j!=0)
位操作的类型转换
对于逻辑位操作,编译程序将会按常规算术转换规则对操作数进行类型转换,运算结果的类型与转换后的操作数类型相同
对于移位操作,编译程序将会按整型提升规则对操作数进行类型转换,运算结果的类型与转换后的操作数类型相同
赋值操作符的类型转换
当赋值操作的两个操作数类型不同时,编译程序将按赋值转换规则进行隐式类型转换,即,“把右边操作数转换成左边操作数类型”
- 将实数转换成整数时,小数部分将舍去,并且不进行“四舍五入”
条件操作符的类型转换
第一个操作数可以是算术型,此时编译程序将对其进行逻辑转移:“零”转为false;非“零”转为true。
第二、三个操作数可以是任意类型。
2、显式类型转换
显式类型转换(explicit type conversion)是指程序中明确给出转换,又称强制类型转换
- 大范围强制转换到小范围,有时会丢失数据的精度或产生错误
(<类型名>)<操作数>
inti=-10;
unsignedintj=3;
cout<<"i+j:"<<i+j<<endl;
cout<<"i+j:"<<i+(int)j<<endl;
cout<<"i+j:"<<(double)i+(double)j<<endl;
2.4 表达式
2.4.1 表达式的构成与分类
2.4.1.1 表达式的构成
- 表达式(expression)由操作符、操作数以及圆括号所组成的运算式,它构成了程序的基本运算单位
- 单独的常量或变量构成了表达式的特例,称为基本表达式
- 表达式中连续出现两个操作符时,有时需要使用空格把它们分开,避免歧义
2.4.1.2 表达式的分类
- 根据表达式结果的类型分为:
- 算术表达式:结果为算术型
- 关系/逻辑表达式:结果为bool型
- 地址表达式:结果为指针类型(即内存地址)
- 常量表达式:编译时刻能够确定的值或常量
- 根据结果的有效时间分为:
- 左值表达式(lvalue expression):结果有明确的内存地址
- x、++x、--x、x=<表达式>都是左值表达式,结果保存在变量x中
- 右值表达式(rvalue expression):结果存放在临时存储单元
- x++、x--,结果保存在临时的存储单元
- 字面常量和#define定义的符号常量,没有明确的内存地址
- 左值表达式(lvalue expression):结果有明确的内存地址
2.4.2 操作符的优先级和结合性
- 单目、双目、三目、赋值依次降低
- 算术、移位、关系、逻辑位、逻辑依次降低
左结合:从左到右计算
右结合:从右到左计算
优先级 | 操作符 | 含义 | 综合性 |
1 | :: ++,-- ( ) [ ] -> . | 域解析符 自增(后置),自减(后置) 函数调用 数组访问(或下标操作) 成员选择(对象指针->成员) 成员选择(对象.成员) | 左结合 |
2 | ++,-- ! ~ - + & * sizeof new,delete (type) | 自增(前置),自减(前置) 逻辑非 按位取反 取负 取正 取地址 间接访问 计算数据所占内存字节数 动态分配和释放 强制类型转换 | 右结合 |
3 | .* ->* | 间接成员选择(对象.*成员指针) 间接成员选择(对象指针->*成员指针) | 左结合 |
4 | *,/,% | 乘法,除法,取余数 | 左结合 |
5 | +,- | 加法,减法 | 左结合 |
6 | <<,>> | 左移,右移 | 左结合 |
7 | <,<=,>,>= | 小于,小于或等于,大于,大于或等于 | 左结合 |
8 | ==,!= | 相等,不等 | 左结合 |
9 | & | 按位与 | 左结合 |
10 | ^ | 按位异或 | 左结合 |
11 | | | 按位或 | 左结合 |
12 | && | 逻辑与 | 左结合 |
13 | || | 逻辑或 | 左结合 |
14 | ?: | 条件操作 | 右结合 |
15 | =,*=,/=,+=,-=,>>=,<<=,&=,~=,|= | 赋值 | 右结合 |
16 | , | 逗号操作 | 左结合 |
2.4.3 表达式中的类型转换
计算表达式时,编译程序会根据优先级和结合性,基于单个操作符依次进行转换,不是全部转换成最大范围的类型再计算
2.4.5 带副作用操作符的表达式计算
- C++中带副作用的操作符:--,++,各种赋值操作符
2.4.6 表达式结果的输出
- C语言提供printf
- C++中,使用<iostream>库中的cout
2.5 小结
- 一种数据类型是由一个值集和一个操作集构成
- 程序设计语言分为静态类型语言和动态类型语言
- 静态类型语言:要求在静态的程序(运行前的程序)中必须为每个数据指定类型
- 静态类型语言的好处是便于编译程序自动进行类型的一致性检查,以保证数据操作的合法性以及生成高效的可执行代码
- 动态类型数据:不要求在静态程序中明确指出数据的类型,而是在程序运行中数据被用到时才确定它们的类型
- 静态类型语言:要求在静态的程序(运行前的程序)中必须为每个数据指定类型
C++是静态类型语言
- C++把数据类型分为:基本数据类型、构造数据类型和抽象数据类型
- 基本数据类型:语言预先定义好的数据类型,包括整型、算术型
- 整型:整数类型、字符类型、逻辑类型
- 算术型:整型和实数型统称为算术型
- 基本数据类型:语言预先定义好的数据类型,包括整型、算术型
- 程序中数据的表现方式有两种:常量和变量。
- 常量:在程序执行中不变(或不能被改变)的数据。分为:字面常量和符号常量
- 采用符号常量有利于增加程序的易读性、提高程序对常量使用的一致性以及增强程序的易维护性
- 变量:可变的数据用变量和表示。变量拥有名字、类型、值、内存空间与地址
- 程序中使用的每个变量要有定义
- 常量:在程序执行中不变(或不能被改变)的数据。分为:字面常量和符号常量
- 操作符用于描述对数据的运算,分为算术操作符、关系操作符、逻辑操作符、位操作符、赋值操作符、条件操作符、sizeof等
- 赋值“=”,自增/自减“++/--”是有副作用的操作符
- 在操作符运算前有时会对操作数进行类型转换。类型转换分为:显式(强制类型)转换和隐式转换
- 程序中对数据操作的具体实施是通过表达式来描述的。表达式由:操作数、操作符以及圆括号组成的运算式
- 根据表达式的结果类型可以把表达式分为:算术表达式、关系/逻辑表达式、地址表达式等
- 如果一个表达式中的操作数为常量或在编译中能够确定它的值,称之为常量表达式
- 在表达式中,操作符具有优先级和结合性。
- 操作符的优先级规定了相邻的两个操作符中优先级高的先运算
- 操作符的结合型规定相邻的两个具有相同优先级的操作符先计算哪一个
- 在进行表达式计算时,有时需要进行类型转换
- 转换原则:根据操作符的优先级基于单个操作符依次进行转换
- 在进行程序设计时,应尽量避免把带副作用的操作符用在复杂的表达式中
课后答案(仅供参考)
1、表达式中类型转换规则是什么?计算下面的表达式时如何进行操作数类型转换?
(1)3/5*12.3
(2)'a'+10*5.2
(3)12U+3.0F*24L
隐式类型转换
- 算术操作的类型转换
常规算术转换规则(usual arithmetic conversions):
- 浮点数之间进行隐式转换时:long double、double、float
- 整数与浮点数运算:将整数转为浮点数
- 带符号数与无符号数运算:转为无符号数
整型提升(integral promotion):
- char、signed char、unsigned char、short int、unsigned short int类型,如果int能够表示他们的值,转为int,否则转为unsigned int
- bool型转为int,true为1;false为0
- wchar_t和枚举类型转换为下列按次排序第一能表示所有值的类型:int、unsigned int、long int、unsigned long int
- 关系操作的类型转换
表述多个逻辑条件时,不能按照数学上习惯的表达方式
- 如:a==b==c
- 含义:把a==b的结果(bool类型)与c进行“等于”比较
- 正确表达方式:(a==b)&&(b==c)
- 逻辑操作的类型转换
若算术型操作数参加逻辑运算,操作前会进行逻辑转换,转换规则:“零”转换成“false”,“非零”转换成true
- int i && int j
- 若i和j都不等于0,结果为true,否则为false
- 提高易读性:(i!=0)&&(j!=0)
- 位操作的类型转换
对于逻辑位操作,编译程序将会按常规算术转换规则对操作数进行类型转换,运算结果的类型与转换后的操作数类型相同
对于移位操作,编译程序将会按整型提升规则对操作数进行类型转换,运算结果的类型与转换后的操作数类型相同
- 赋值操作符的类型转换
当赋值操作的两个操作数类型不同时,编译程序将按赋值转换规则进行隐式类型转换,即,“把右边操作数转换成左边操作数类型”
- 将实数转换成整数时,小数部分将舍去,并且不进行“四舍五入”
- 条件操作符的类型转换
第一个操作数可以是算术型,此时编译程序将对其进行逻辑转移:“零”转为false;非“零”转为true。
第二、三个操作数可以是任意类型。
void c3::print(){
cout<<"3/5*12.3="<<3/5*12.3<<",typeid:"<<typeid(3/5*12.3).name()<<endl;
cout<<"'a'+10*5.2="<<'a'+10*5.2<<",typeid:"<<typeid('a'+10*5.2).name()<<endl;
cout<<"12U+3.0F*24L="<<12U+3.0F*24L<<",typeid:"<<typeid(12U+3.0F*24L).name()<<endl;
}
输出结果:
3/5*12.3=0,typeid:d
'a'+10*5.2=149,typeid:d
12U+3.0F*24L=84,typeid:f
2、判断一个数是否为奇数/偶数?
bool c3::isOdd(intx){
cout<<x<<"&1="<<(x&1)<<",typeid:"<<typeid(x&1).name()<<endl;
return(x&1)==1;
}
bool c3::isEven(intx){
cout<<x<<"&0="<<(x&0)<<",typeid:"<<typeid(x&0).name()<<endl;
return(x&0)==1;
}
3、不引进第三方变量,交换两个整型变量的值
void c3::che(inta,intb){
//不借助中间变量,实现a,b数值互换
a=a+b;
b=a-b;
a=a-b;
cout<<"a:"<<a<<",b:"<<b<<endl;
a=a^b;
b=a^b;((a^b)^b=a)
a=a^b;((a^b)^a=b)
cout<<"a:"<<a<<",b:"<<b<<endl;
}
4、举例说明int转float丢失精度
int和float都是32位表示,占4字节
- int范围:231-1~-231
- float范围:1位S,8位E,23(+1)位尾数
所以如果int类型的值在224以内,float是可以精确表示的,但是当超过这个数的时候就不一定能精确表示了。
voidc3::intToFloat(){
inti=pow(2,31)-1;
cout<<"inti="<<i<<",typeid:"<<typeid(i).name()<<",to_string:"<<to_string(i)<<",hex:"<<hex<<i<<",sizeof(i)="<<sizeof(i)<<endl;
cout<<"floati="<<(float)i<<",typeid:"<<typeid((float)i).name()<<",to_string:"<<to_string((float)i)<<",hex:"<<hex<<(float)i<<",sizeof((float)i)="<<sizeof((float)i)<<endl;
cout<<"doublei="<<(double)i<<",typeid:"<<typeid((double)i).name()<<",to_string:"<<to_string((double)i)<<",hex:"<<hex<<(double)i<<",sizeof((double)i)="<<sizeof((double)i)<<endl;
}
输出结果:
int i=2147483647,typeid:i,to_string:2147483647,hex:7fffffff,sizeof(i)=4
float i=2.14748e+09,typeid:f,to_string:2147483648.000000,hex:2.14748e+09,sizeof((float)i)=4
double i=2.14748e+09,typeid:d,to_string:2147483647.000000,hex:2.14748e+09,sizeof((double)i)=8
2^31-1(int)原码:0111 1111 1111 1111 1111 1111 1111 1111(0x7fff ffff),补码:0x7fff ffff,机器数:0x7fff ffff,真值:2147483647
2^31-1(float)原码:0x7fffffff,机器数:
- (1.11 1111 1111 1111 1111 1111 1111 1111)2×230
- S:0
- f:111 1111 1111 1111 1111 1111 (111 1111)
- E:30+127=(157)10=(1001 1101)2
2^31-1对应的IEEE 754 float:0 1001 0001 111 1111 1111 1111 1111 1111(0x58ffffff),转换为真值:2^31-2^7=2147483520,不能准确表示
故表示为最近的值:0 1001 1110 000 0000 0000 0000 0000 0000(ox4f000000) ,转换为真值:2^31=2147483648
5、计算(x+1)*(++x)+(x++)的值(假设x初始值为1)
cout<<"(i+1)*(++i)+(i++):"<<(i+1)*(++i)+(i++)<<endl;
运行结果:
(i+1)*(++i)+(i++):6
https://github.com/zzq1996/ProgrameDesign
参考:《程序设计教程:用C++语言编程》 陈家骏,郑滔