- C++语言定义了几种基本内置类型(如字符,整型,浮点数等),同时为程序员提供了自定义数据类型机制
2.1 基本内置类型
. C++定义了一套包含算术类型和空类型在内的数据类型。其中,空类型不对应具体的值(例如打印相关内容)
2.1.1算术类型
算数类型分为两类:整型和浮点型
注: 算术类型的尺寸在不同的机器上存在差别
布尔类型(bool)的取值是真(1)或假(0)
带符号类型和无符号类型
- 除布尔型和拓展字符型外,其他整型可划分为带符号和不带符号(仅表示大于等于0的数)
带符号:int,short,long
不带符号:unsigned long,unsigned int(简写为unsigned) - 字符型:signed char,unsigned char,char(不同的编译器,会把char归为不同类)。其中char属于unsigned char或signed char。
- 表示数值范围:
unsigned char:表示0到255区间内任意值
signed char:理论上可表示-127——127区间内任意值
编写程序时如何选择类型
- 确定数值不为负时,用unsigned int
- 使用int进行整数运算
- 在算数表达式中,不要用char或bool
- 执行浮点数运算选用double类型
2.1.2 类型转换
类型的定义包含了数据与能参与的运算其中一种支持的运算将一种给定的类型转换为另一种相关类型
语句 | 含义 |
---|---|
bool b=42; | b为真(b=0) |
int i=b; | i=0 |
i=3.14; | i=3(i为int类型) |
double pi=i; | pi=3.0 |
unsigned char c=-1 | 假设char占1字节,c=255 |
signed char c2=256; | 假设char占1字节,c2未定义 |
- 当我们把整数值赋给浮点类型时,小数点部分数记为0。如果整数所占空间超过了浮点型的容量,浮点数将损失精度。
对负数取模:
从数学角度解读:
取模运算时,对于负数,应该加上被除数的整数倍,使结果大于或等于0之后,再进行运算.
也就是:(-1)%256 = (-1+256)%256=255%256=255
- 程序中两种不同算数类型的运算
例:
//i为非0,程序继续执行,i值变为0
如果i的值为0,视为faulse;i的值不为0,是为true.
- 含有无符号类型表达式
- u+i实际上是将i换为无符号数,即赋一个负值再求模,结果等于负数加无符号数的模
求模公式:unsigned(z)=2 z所占的位数−(−z)
注:
2.1.3字面值常量
形如42的值被称为字面值常量(光凭字面就能看出数值大小),其形 式和值决定了它的数据类型
整型和浮点型字面值
-
以0开头的通常为八进制
-
以0x或0X开头的通常表示十六进制
如表示20:
十进制: 20
八进制 : 024
十六进制:0x14 -
默认情况下,十进制带符号,八进制和十六进制可能带符号也可能不带
-
十进制字面值的类型是能容纳下该数的int,long,long long中尺寸最小的那个(即int型)
-
八进制和十六进制中,字面值的类型是能容纳下该数的int,unsigned int,long,unsigned long,long long和unsigned long long中尺寸最小的
-
short没有相应的字面值
-
严格来说,十进制字面值不会为负,符号是强行添加的,仅仅是对字面值取负
-
浮点型字面值表现为以科学计数法表示的小数,指数部分用e或E标识
字符和字符串字面值
用单引号‘ ’括起来的单个字符成为char型字面值
由“ ”括起来的0个或多个字符则构成字符串型字面值
-
字符串的面值实际上是由字符数组构成的
-
字符串实际长度比字面长度加1(含‘\0’)
-
字符串的分行书写:
转义序列
不能直接使用的两类字符:
1.不可打印的字符(如退格等其他控制符)
2.有特殊含义的字符(如单引号,双引号,问号,反斜线)。通常情况下用转义序列解决
功能 | 符号 |
---|---|
换行符 | \n |
纵向制表符 | \v |
反斜线 | \\ |
回车符 | \r |
横向制表符 | \t |
退格符 | \b |
问号 | \? |
进纸符 | \f |
报警符 | \a |
双引号 | \" |
单引号 | \’ |
- 同样可以使用八进制或十六进制数运用到转义序列,如:
- 注:‘ \ '后只能识别三位八进制数
如:\1234表示八进制123对应的字符,和字符4
而\x后则需用到后面所跟的所有数字
指定字面值的类型
通过添加指定的前缀或后缀,改变默认类型
- 以U为后缀可能为:unsigned int,unsigned long,unsigned long long
- 以L为后缀可能为:long,long long,unsigned long long
- 以LL为后缀可能为:long long,unsigned long long
- 以UL为后缀可能为:unsigned long,unsigned long long
布尔字面值和指针字面值
- 布尔字面值:true或faulse
- 指针字面值;nullptr
2.2变量
数据类型决定变量的空间大小,布局方式和变量能参与的运算。变量和对象通常来说可以互换使用
2.2.1变量定义
变量定义的基本格式:类型说明符+变量名+(;) 其中变量名间以逗号分隔
- std::string book表示存在于std::string库中的名为book的字符序列,类似于从iosream库中的std::cin和std::cout。该语句含义为将字面值“0-201-78345-X”拷贝给book。
什么是对象?
对象指一块能存储数据并具有某种类型的内存空间
初始值
当对象在创建是获得了一个特定的值,我们就称这个对象被初始化了
注 :初始化和赋值尽管用的都是“=”,但它们是两个不同的操作。初始化是创建一个初值,而赋值是擦除当前变量的值,以一个新值来替代。
列表初始化
初始化的几条语句:
int units-sold={0};
称为列表初始化,无论是初始化还是赋新值,都可使用。
这种初始化有一个重要的特点:如初始值有丢失信息的风险,编译器会报错
由于double类型值强制赋予int类型,小数点后部分会自动省去,会丢失信息,故编译器会报错
默认初始化
- 若定义变量时未初始变量值,变量就会被默认初始化。此时变量的初始值因类型而定,同时也会对变量的位置有影响。
- 绝大多数类未强制规定初始化,如string类型未指定初值,则生成一个空字符串:
- 还有一种情况是,定义在函数内部的内置类型变量将不被初始化,即此变量未定义,从而不能对变量进行拷贝或其他形式访问
- 有些类要求每个对象都显示初始化,如创建了一个变量而未对其初始化,将引发错误
-
2.2.2变量声明和定义的关系
- 如果将程序分为多个文件,则需要在多个文件中共享代码。例如一个文件的代码需要另一个文件中定义的量。例如std::cin和std::cout定义于标准库,同时可以被我们写程序所使用
- 为支持分离式编译,C++将声明和定义区分开来,声明使得名字为程序所知,定义负责创建与名字关联的实体
任何包含了显示初始化的声明即为定义
注:如试图在函数体内部由extern关键字标记变量,将引发错误
变量有且只能定义一次,但可被多次声明
2.2.3标识符
标识符由字母,数字和下划线组成,必须以字母或下划线开头
- 用户自定义标识符不能连续出现两个下划线
- 下划线后不能紧接大写字母
- 定义在函数体外的标识符不能以下划线开头
变量命名规范
- 标识符应体现实际含义
- 变量名一般用小写
- 用户自定义类名一般以大写开头,如Sales_item
- 如果由多个单词构成,单词间应有区分,如student_loan而不是studentloan
2.2.4名字的作用域
作用域:C++语句中,大多作用域以花括号分隔
- 定义名字:main,sum,va1
- 命名空间名字:std
- 命名空间提供名字:cout,cin
名字main定义于花括号外,具有全局作用。而sum定义于花括号内,出了花括号就无法访问sum了,因此,sum拥有块作用域
建议当你第一次使用变量时再定义
嵌套的作用域
被包含的作用域称为内作用域
包含别的作用域的作用域称为外作用域
如果函数用到局部变量,不宜与全局变量重名
2.3复合类型
一条声明语句由一个基本数据类型和一个紧跟其后的声明符列表组成。每个声明符命名了一个变量,并且指定了数据的基本类型
2.3.1 引用
引用为对象起了一个新名字。通过将声明符写成&d的形式来定义引用类型,其中d为声明变量名
定义引用时,程序将两者的初始值“绑”在一起。一旦初始完成后,引用将和它的初始值一直绑在一起,并且无法令其绑定到另一个对象,因此,引用必须初始化
引用即别名
引用的定义
允许一条语句中有定义个引用
引用只能绑在对象身上,不能与字面值或某个表达式的计算结果绑在一起
2.3.2 指针
指针是指向另一种类型的复合类型。与引用类似,实现了与其他对象的间接访问。与指针不同点:
1.允许对指针赋值和拷贝
2.指针无须定义和赋初值
定义指针类型声明符:* d,d为变量名,定义指针变量前必须加 *:
其中,&ival未引用对象,因此不能定义指向引用的指针,应看成指针指向ival的地址
定义指针的几种形式:
定义指针时,二者的类型必须匹配
指针值
1.指向一个对象
2.指向紧邻对象所占空间的下一个位置(无法访问)
3.空指针(无指向)(无法访问)
4.无效指针
利用指针访问对象
可用解引符“*”来访问该对象
对指针使用解引符,等于访问它的所指对象
空指针
空指针不指向任何对象,以下为生成空指针的方法:
- 可使用nullptr来初始化指针(最佳方案)
- 还可使用包含在头文件cstdlib中的NULL的预处理变量处理,它的值就是0
- 预处理变量不属于std空间,因此引用时不须加上std::
不能通过指针指向数值为0的变量来实现空指针
建议:初始化所有指针
赋值和指针
指针可重新赋值,而引用不行
其他指针操作
指针在条件语句中的运行:
- 对于两个相同类型的指针,可用(==)和(!=)来比较,比较结果为布尔类型(比较二者的地址)
- 上述操作均需要指针合法
void * 指针
void *是一种特殊的指针类型,可存放任意对象的地址
不能直接操作void*指针所指对象,因为不清楚对象的类型
2.3.3 理解复合类型的声明
一条定义语句可定义不同类型的变量
int* p1,p2;中“*”仅修饰了p1,对p2无影响
声明两个整型指针方法:
指向指针的指针
通过的个数区分指针的级别。即 “ **”表示指针的指针,***表示指针的指针的指针
- 解引用指针得到一个int型整数,因此,解引用一个指针的指针,得到指针。为了访问最初值,应对指针进行两次解引用:
指向指针的应用
因为引用本身并不是对象,因此,不能定义指向引用的指针。由于指针是对象,所以存在对指针的引用
2.4 const限定符
有时候我们希望定义一种值不变的变量,就可运用关键字const对变量类型加以限定,如想对const变量的值进行更改,将引发错误
- const对象一旦创建,就不能更改,且必须对const对象进行初始化
初始化和const
- 可以用const类型去初始化另一个对象
默认状态下,const对象仅在文件内有效
- 执行过程:运行过程中,编译器会找到代码中所有的const变量,并用相应的值去替换
- 必须在每一个用到const变量的文件夹中分别进行定义,const对象设定为仅在文件内有效
- 当多个文件夹中的程序想共用一个const变量时,需在const前加extern
如上述语句,表示在file_1.cc中定义const类型bufSize,在file_1.h中
2.4.1 const的引用
对const的引用又称为对常量的引用。
对常量的引用不能修改它所绑定的对象
除const变量外,其他的引用均需遵守引用对象与被引用对象的类型相同
初始化和对const的引用
const变量引用的类型不一定需要const类型,而其他类型变量的引用必需是相同类型
常量的引用相当于将const类型引用绑定了一个temp,temp的值等于被绑定对象。
当引用非常量时,此方法不成立
2.4.2指针和const
- 与引用一样,指针可以指向常量或非常量
- 指向常量的指针不能改变指向对象的值
- 只能用指向常量的指针来存放常量对象的地址
- 但允许一个指向常量的指针指向一个非常量对象
const指针
- 常量指针必须初始化
- 常量指针的值(地址)不能改变
- 常量指针指向常量的话,不能通过指针改变指向的常量
- 常量指针指向非常量,则可以通过指针改变指向的非常量的值
2.4.3 顶层const
- 顶层const:表示指针本身是一个常量(值不能改变)。可表示任意类型的对象是常量(算数类型,类,指针等)。
- 底层const:表示指针所指对象为是一个常量。与指针或引用等复合类型的基本类型部分有关。
指针既可以是顶层const也可以是底层const
- 拷入和拷出的对象必须拥有相同的底层const资格
2.4.4 constexpr 和常量表达式
- 常量表达式:指值不会改变,且在编译过程就能得到计算结果的表达式
- 对象是否是常量表达式由它的数据类型和初始值共同决定(语句中的对象全是常量)
constexpr 变量
- 将变量声明为constexpr类型来验证变量值是否为常量表达式
- 新标准允许一种特殊的constexpr函数来初始化constexpr变量
字面值类型
- 字面值类型:算数类型,引用和指针
- 非字面值类型:自定义类型Sales_item,IO库,string库类型等
- 一个constexpr类型指针的初始值必须是nullptr或0
指针和constexpr
constexpr把其所定义的对象置为了顶层const
2.5 处理类型
2.5.1 类型别名
1)typedef:
2) using
指针,常量和类型别名
- 下列语句中的pstring实际上是类型char*的别名
2.5.2 auto类型说明符
- auto类型说明符:编译器通过初始值来推算变量的类型
使用auto声明多个变量时,一条语句中的变量应该相同
复合类型,常量和auto
使用auto时,编译器会以引用对象作为auto类型
auto一般会忽略掉顶层const的性质,而底层const性质会被保留
如需推断auto类型为顶层const,需提前指出const:
设置一个auto类型引用时,初始值中的顶层常量属性仍旧保存
2.5.3 decltype类型指示符
decltype类型:选择并返回操作类型(不返回值)
如果f被调用,就返回f的类型
decltype可返回顶层const类型
decltype和引用
- r是一个引用,但r+0是int类型
- 如果表达式内容是解引用操作将得到引用类型
2.6 自定义数据结构
2.6.1 定义Sales_data类型
- 数据结构的定义:
格式:
1)struct+类名和类体
2){
//作用域(添加数据成员)
}
3)“;”前可添加变量名
4) 以“;”结尾
自定义Sales_data类型,并声明变量:
类数据成员
- 创建一个新对象时,未被初始的成员会被默认初始化
2.6.2 使用Sales_data类型
添加两个Sales_data对象
- 自定义的Sales_data类需要手动输入,输出以实现相应功能
需要大致的思路:
Sales_data对象输入数据
表示类中的成员的方法:
类名+“."+成员名
输出两个Sales_data对象的和
2.6.3 编写自己的头文件
- 类一般在函数体外
- 类通常被定义在头文件中
- 头文件通常包含只能被定义一次的实体
- 头文件也经常用到其他头文件的功能
预处理器概述
- 预处理器:可以部分改变我们所写的程序。例如#include,当处理器看到#include,会用想应头文件代替#include所指代的文件
- 头文件保护符:
-#define把一个名字设为预处理变量
#ifdef当且仅当变量已定义时为真
#ifndef当且仅当变量未定义时为真
#endif执行操作停止