基本数据类型包含了算术类型(arithmetic type)
和空类型(void)
算数类型,包含了字符、整型数、布尔值和浮点数
空类型,不对应具体的值
算术类型分为两类:整型(integral type,包含字符和布尔)和浮点型
bool
取值true或false
char
大小和机器字节一样
其他字符类型用于扩展字符集,wchar_t
用于确保可存放机器最大扩展字符集中的任意字符,char16_t
和char32_t
为Unicode字符集服务。
short ≤ int ≤ long ≤ long long
。其中,数据类型 long long 是在 C++11 中新定义的
浮点型可表示单精度、双精度、扩展精度。
float
表示单精度浮点数,格式为1位符号,8位指数,23位小数
double
表示双精度浮点数,格式为1位符号,11位指数,52位小数。
long double
提供的精度在一般情况下是没有必要的,况且它带来的运算时消耗也不容忽视。
带符号类型和无符号类型
带符号(signed)
无符号(unsigned)
字符型
char
和signed char 并不一样;有无符号由编译器决定
signed char
8位,表示0~255
unsigned char
理论表示-127127,大多是现代计算机将实际的表示范围定位-128127
自动转换过程
非bool → bool
,值为0则返回false,否则返回true
bool → 非bool
,值为false则返回0,值为true则返回1
浮点 → 整型
,只保留整数部分
整型 → 浮点
,小数部分记为0,若整数所占空间大于浮点型容量,则损失精度
给unsigned赋超出范围的值
,结果为该值对unsigned表示数值总数取模后的余数,如-1赋值unsigned char得到255
给signed赋超出范围的值
,结果为undefined
含有无符号类型的表达式
算术表达式中既有signed又有unsigned时,signed会转成unsigned
。因此,切勿混用unsigned和signed
一个形如42的值被称作字面值常量。字面值常量的形式和值决定了它的数据类型。
整型和浮点型的字面值
默认十进制字面值是signed,八进制和十六进制字面值可能是signed或unsigned
20/* 十进制 /、024/ 八进制 /、0x14/ 二进制 */
十进制字面值的类型是int、long、long long中的最小者,八进制和十六进制字面值的类型是int、long、long long、unsigned int、unsigned long、unsigned long long中的最小者。因此short没有对应的字面值。
浮点型字面值表现为小数或以科学计数法表示的指数,默认浮点字面值是double类型
3.14159 、3.14159E0、0.、0e0、.001
字符和字符串字面值
'a' //char型字面值
"Hello World!" //字符串字
字符串型字面值的类型实际是常量字符构成的数组。编译器在字符串结尾处添加空字符'\0'
,因此字符串的长度比内容多1
分行书写字符串
std::cout << "a really, really long string literal "
"that spans two lines" << std::endl;
转义序列
在C++中有特殊含义的字符(如单双引号、问号、反斜线等)出现在字符串中时要转义,转义序列均以反斜线作为开始。
泛型转义字符
形式是\x后紧跟1个或多个十六进制数,或\后紧跟1、2、3个八进制数,其中数字部分表示的是字符对应的数值。如果超过3个,八进制只看前3个,十六进制看所有数字
std::cout<< "Hi \x4dO\115!\n"; //输出Hi MOM!,换行 \115 \x4d代表字符M
指定字面值的类型
布尔字面值 true 和 false
指针字面值 nullptr
基本形式
类型说明符(type specifier)后紧跟一个或多个变量名组成的列表,变量名以逗号分隔,最后分号结束
int sum=0, value, units_sold=0;
Sales_item item;
std::string book("0-201-78345-X");
对象(object)
:指一块能存储数据并具有某种类型的内存空间。
初始值
初始化
:创建变量时赋予一个初始值,有别于赋值赋值
:把对象的当前值擦除,而以一个新值来代替列表初始化
C++11新标准:用花括号来初始化变量
如果使用列表初始化且初始值存在丢失信息的风险,则编译器报错
int a=0;
int a={0};
int a{0};
int a(0);
long double ld=3.141592653;
int a{ld}, b={ld}; //错误,转换未执行,因为存在丢失信息的危险
int c(ld), d=ld; //正确:转换执行,且确实丢失了部分值
默认初始化
定义变量时未指定初值,则是默认初始化,此时变量被赋予“默认值”。
默认值由变量类型决定,同时定义变量的位置也会对此有影响
内置类型的默认值由定义的位置决定,定义函数之外的变量被初始化为0
;
定义于函数之内的变量不被初始化
,如果试图拷贝或以其他形式访问此类值将引发错误。
各个类各自决定其初始化对象的方式。绝大多数类无须显示初始化就提供默认值;一些类则要求显示初始化,否则引发错误。
分离式编译(sparate compilation)
声明(declaration)
extern
,且不要显式初始化。可以给extern显式初始化,一旦这样做,就成了定义。在函数体内部初始化extern标记的变量,将引发错误定义(definition)
负责创建与名字相关联的实体
申请存储空间,也可能赋予初值
extern int i; //声明
int j; //声明并定义
extern double pi-3.1416 //定义
定义
由字母、数字、下划线组成,且必须以字母或下划线开头。长度没有限制,但大小写敏感。
C++为标准库保留了命
用户自定义标识符要求
变量命名规范
一下规范能提高程序可读性
标识符体现实际含义
变量名一般用小写字母
自定义类名一般大写字母开头
表示符由多个单词组成时,单词间应有明显区分,如student_loan或studentLoan,不要使用studentloan
作用域(scope)
是程序的一部分,以花括号
分隔
同一个名字在不同作用域中可能指向不同实体,名字的有效区域
始于声明语句,结束于声明语句所在作用域的末端。
全局作用域(global scope)
是定义在所有函数之外的名字的作用域,块作用域( block scope)
是花括号内部的作用域。
嵌套的作用域
作用域能彼此包含,包括外层作用域
和内层作用域
。
用域操作符::
手动指定作用域,左侧是作用域的名称,全局作用域的名称为空。
复合类型
是指基于其他类型而定义的类型,包括引用
和指针
等。
声明
:一条声明语句由一个基本数据类型
和紧随其后的一个声名符
列表组成,每个声名符命名了一个变量并指定该变量为与基本数据类型有关的某种类型。
我们使用术语引用
时,通常是指左值引用
(C++11中新增右值引用
)
引用
为对象起了另外一个名字。将声名符写为&d
来定义变量d
为引用。
初始化变量和定义引用的区别:
一旦引用被绑定到一个初始值对象后,便无法令引用重新绑定到另外的对象,因此引用必须被初始化
int ival = 1024;
int &refVal = ival;
int &refVal2; //报错:引用必须被初始化
引用即别名
引用并非对象,他只是一个已存的对象的别名,所以不能定义引用的引用
定义引用之后,对其进行的所有操作都是在与之绑定的对象上进行的
引用的定义
允许一条语句中定义多个引用,其中每个引用标识符都必须以符号&
开头
引用只能绑定在对象上
,不能绑定到字面值或表达式的计算结果
引用类型要与其被绑定的对象类型严格匹配
指针是“指向”另一种类型的复合类型。
指针与引用的区别:
定义指针类型的方法将声名符写成*d
的形式,其中d时变量名。
获取对象的地址
指针存放某个对象的地址,获取该地址需使用取地址符&
int ival=42;
int *p=&ival; //p存放ival的地址。或者说p时指向ival的指针
指针的类型要和它所指向的对象严格匹配,除了两种例外情况
double dval;
double *pd=&dval; //正确,指向double型对象的指针
double *pd2=pd; //正确,指向double型对象的指针
int *pi=pd; //错误,指针pi和pd的类型不匹配
pi=&dval; //错误,试图把double型对象的地址赋给int型指针
指针值
指针的值(地址)应属于下列4中状态之一:
利用指针访问对象
如果指针指向一个对象,则允许使用解引用符*
来访问该对象
对解引用的结果赋值,实际上也就是给指针所指的对象赋值
int ival=42;
int p=&ival;
cout<<p;//输出42
p=0;
cout<<p;//输出0
空指针
空指针不指向任何对象,使用指针前可以先检查它是否为空。
生成空指针的方法
//C++11引入
int *p1=nullptr; //等价于int *p1=0;
int *p2=0;
//#include cstdlib
//预处理变量NULL,由预处理器负责管理,不属于命名空间std,可直接使用,无需std::
//预处理其会自动将预处理变量替换为实际值
int *p3=NULL //等价于int *p3=0;
尽量使用nullptr,尽量避免使用NULL
不能把int变量直接赋给指针,即使int变量值刚好等于0也不行
建议:初始化所有指针
尽量在定义对象之后再定义指针。如果实在不清楚指针指向何处,就初始化为nullptr
赋值和指针
给指针赋值就是存放一个新的地址,从而指向一个新的对象
int i=42;
int *pi=0; //pi初始化空指针
int *pi2=&i; //pi2初始化,指向i
int *pi3; // pi3如果定义块内,值无法确定
pi3=pi2; //pi3和pi2指向i
pi2=0; //pi2为空指针
pi=&ival; //pi指向ival
*pi=0; //ival值改变,pi依然指向ival
其他指针操作
void* 指针
同一条声明语句中,基本数据类型只有一个,但声名符形式可以不同,即可定义多种不同的复合类型
定义多个变量
int* p; //合法但容易产生误导
int* p1,p2; //p1是指向int的指针,p2是int
int *p1,*p2; //都是指向int的指针
指向指针的指针
名符中的修饰符(&和*)的数量没有限制
指针本身是对象,也有自己的地址,允许指针的地址再存到另一个的指针当中
int ival=1024;
int *pi=&ival; //pi指向int型的数
int **ppi=π //ppi指向int型的指针
指向指针的引用
int i=42;
int *p; //指针可不用初始化
int *&r=p; //绑定到指针p的引用。
//r是一个引用,引用的对象是指针,该指针是int型,该引用r被绑定到对象p。
r=&i; //给r赋值就是给指针p赋值
*r=0; //解引用r就是解引用指针p
初始化和const
默认状态下,const对象仅在文件内有效
在编译期将const变量都替换为对应的值
当多个文件出现了同名的const变量时,等同于在不同文件中分别定义了独立的变量。
如果要在文件间共享const对象,就必须在声明和定义中都用extern,即将所有该对象都声明为文件外可见。
//file_1.cc 定义并初始化一个常量,其他文件可访问
extern const int bufSize=fcn();
//file_1.h
extern const int bufSize;//与.h中的bufSize是同一个
对常量的引用:将引用绑定到const对象上。对常量的引用不可修改其绑定的对象。
const int ci = 1024;
const int &r1=ci; //正确,引用及其绑定对象都是常量
r1=42; //错误r1是对常量的引用
int &r2=ci; //错误,试图让非常量引用指向一个常量引用
初始化和对const的引用
初始化普通引用,其类型必须严格匹配其所引用的对象类型。
初始化常量引用时允许任意表达式作为初始值
,只要该表达式的结果能转换成引用的类型即可。
(补充
:意味着可以绑定非常量的对象、字面值和一般表达式 详解)
这两者差异的原因在于::类型不匹配时,编译器定义一个临时量
用于类型转换。如果允许非常量引用的类型不匹配,就会产生临时量
,修改引用时修改的是临时量
而不是想要绑定的对象。常量引用不可修改,因此不存在这个问题。如下例:
//手写的代码
double dval=3.14;
const int &ri=dval;
//编译器转化的代码
double dval=3.14;
const int temp=dval; //定义与引用类型匹配的中间量,类型转换
const int &ri=temp; //将常量引用绑定到中间量
可将常量引用绑定到非常量对象。此时对象仍可修改,只是不能被这个引用修改。
int i = 42;
int &r1 = i;
const int& r2 = i;
std::cout << r2 << std::endl; //输出42
r1 = 0;
std::cout << r2 << std::endl; //输出0
i = 10;
std::cout << r2 << std::endl; //输出10
指向常量的指针
不能用于改变其所指对象的值
要想存放常量对象的地址,只能使用指向常量的指针
没有规定其所指的对象必须时常量
const double pi=3.14;
double *ptr=π //错误,ptr是普通指针
const double *cptr=π //正确,cptr可指向双精度常量
cptr=42; //错误,不能给cptr赋值
double dval=3.14;
cptr=&dval; //正确,但不能通过cptr改变dval的值
const 指针 —— 常量指针
指针本身是常量*const
,即存放的地址不可改,但其所指对象的值可以修改
常量指针必须初始化
//定义“常量指针”
int errNumb=0;
int *const curErr=&errNumb; //常量指针。curErr是const,是指针,指向int对象
//定义“指向常量的指针”和“指向常量的常量指针”
const double pi=3.14;
const double *cptr=π //指向常量的指针。cptr是指针,指向const double对象
const double *const pip=π //指向常量对象的常量指针。pip是const,是指针,指向const double对象
顶层const
底层const
引用的const都是底层const。
指针既可以是顶层const也可以是底层const,也可以同时是两种const
拷贝时,顶层const没有影响。
拷贝时,拷入拷出的对象必须有相同的底层const资格,或者必须能类型转换。一般来说非常量可以转成常量,反之不行
int i=0;
int *const p1=&i; //指针不可变,顶层const
const int ci=42; //基本类型的const都是顶层const
const int *p2=&ci; //指针可变,指向对象不可变,底层const
const int *const p3=p2; //既是顶层const又是底层const
const int &r=ci; //引用的const都是底层const
//顶层const不受影响
i=ci; //正确,ci是顶层const,可拷贝给顶层非const的i
p2=p3; //正确,底层指向对象的类型严格匹配,可将顶层const的p3拷贝给顶层非const的p2
//底层const的限制
int *p=p3; //错误,p3是底层const,而p底层非const。不可将底层const拷贝给底层非const
。
p2=p3; //正确,p2和p3底层指向对象的类型严格匹配,都是底层const。
p2=&i; //正确,i是int,p2是底层const,可将底层非const拷贝给底层const
(类型转换)。
int &r=ci; //错误,普通int& 不能绑定到int常量上。
const int &r2=i; //正确,const int&可以绑定到普通int上。
常量表达式
指不会改变,且在编译期就能得到结果的表达式(两个条件缺一不可)。用常量表达式初始化的const对象也是常量表达式。
一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定
const int max_files=20; //是常量表达式
const int limit =max_files+1;//是常量表达式
int staff_size=27; //不是,因为类型不是const int
const int sz =get_size(); //不是,因为get_size()运行时才确定
constexpr 变量
C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式
constexpr int mf=20; //是常量表达式
constexpr int limit=mf+1;//是常量表达式
constexpr int sz=size();//只有当函数size()是constexpr函数时,才是初始化constexpr变量
字面值类型
算术类型、引用和指针属于字面值类型
自定义类Sales_item、IO库、string类型不属于字面值类型
指针和constexpr
constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关
constexpr指针既可以指向常量也可以指向非常量
const int *p=nullptr; //p是一个指向整型常量的指针
constexpr int *q=nullptr;//q是一个指向整数的常量指针
类型别名是一个名字,它是某种类型的同义词。类型别名和类型的名字等价
定义类型别名:
typedef
using
typedef double wages; //wages是double的同义词
typedef wages base, *p; //base是double的同义词,p是double *的同义词
using SI=Sales_item; //SI是Sales_item的同义词
指针、常量和类型别名
typedef char *pstring; //pstring类型是指向char的指针
const pstring cstr=0; //cstr是const pstring类型,即是常量指针,指向char,
//此处易误解成“const char *cstr=0”,即指向const char的指针
const pstring *ps; //ps是指针,指向的对象是“指向char的常量指针”
类型说明符auto
:让编译器通过初始值来推算变量的类型。因此auto类型的变量必须初始化
auto可以在一条语句中声明多个变量,但所有变量的初始基本数据类型都必须一样。
auto i=0, *p=&i; //正确,i是整型,p是指向整型的指针
auto sz=0, pi=3.14; //错误,sz和pi类型不一样
复合类型、常量和auto
编译器推出的auto有时候和初始值不完全一样,编译器会适当改变结果类型十七更符合初始化规则
auto &
,原来初始化规则仍然保留const auto
int i=0, &r=i;
auto a=r; //a是int型
const int ci=i, &cr=ci;
auto b=ci; //ci是顶层const,忽略。b是int型
auto c=cr; //cr是ci的引用,忽略。ci是顶层const,忽略。c是int型
auto d=&i; //i是int,取地址得到指向int的指针。d是指向int的指针
auto e=&ci; //ci是const int,取地址得到指向const int的指针,是底层const不可忽略。e是指向const int的指针
const auto f=ci; //f是const int
auto &g=ci; //手动指定g的类型为引用。引用的对象是ci,即g是const int。
auto &h=42; //错,h是非常量引用。不能为非常量引用绑定字面值,只能绑定对象
const auto &j=42 //j是引用,绑定到const auto类型,即j是常量引用。可为常量引用绑定字面值
auto k=ci, &l=i; //k是整型,l是整型引用
auto &m=ci, *p=&ci; //m是整型常量的引用,p是指向整型常量的指针
auto &n=i, *p2=&ci; //错误,i是int型,&c是const int型
decltype
:返回操作数的类型。
auto必须初始化,而decltype只需推导类型,不需初始化。C++11新标准引入
const int ci=0, &cj=ci;
decltype(ci) x=0; //x的类型是const int
decltype(cj) y=x; //y的类型是const int&,y绑定到x
decltype(cj) z; //错误,z是引用,必须初始化
decltype 和引用
如果decltype使用的表达式是一个变量,则返回该变量的类型
如果decltype使用的表达式不是一个变量,则返回表达式结果对应的类型
如果decltype内表达式的内容可作为赋值语句的左值,则decltype得到引用类型。
如果decltype内表达式的内容是解引用操作,则decltype得到引用类型。(解引用指针得到的是所指对象的引用
,而不是值)
如果decltype内表达式的内容是变量加上一层或多层括号,编译器把它当成一个表达式。变量是可作为左值的特殊表达式,故此时得到引用类型。
int i=42, p=&i, &r=i; decltype(r) a; //错,r的推导结果是引用,必须初始化 decltype(r+0) b; //对,r+0的结果是int,故b是未初始化的int decltype(p) c; //错,解引用得到的结果是引用,c是绑定到int的引用,必须初始化
//decltype内表达式的内容是变量加上一层或多层括号,结果是引用
decltype((i)) d; //错,d是绑定到int的引用,必须初始化
decltype(i) e; //对,e是未初始化的int
auto 和 decltype 的区别主要有三方面:
类以关键字struct
开始,紧跟类名
和类体
(类体可为空)
类内部定义的名字必须唯一
类体后面可以紧跟变量名,故结束的花括号后必须写一个fen'ha
struct Sales_data{
std::string bookNo;
unsigned units_sold=0;
double revenue=0.0;
};
struct Sales_data{/……/} accum,trans,*salesptr;
struct Sales_data{/……/};
Sales_data accum,trans,*salesptr;
类数据成员
预处理功能:头文件保护符
防止重复包含的发生
#ifndef SALES_DATA_H
#define SALES_DATA_H
#endif
手机扫一扫
移动阅读更方便
你可能感兴趣的文章