C++中变量以及基本类型
内置类型包括:字符,整型,浮点型等;同时C+还支持更加复杂的数据类型,首先来看看基本的内置类型;
C++定义的内置类型
- 算数类型
- 整形:
-
bool
:布尔类型,用于表示真和假; -
char
:字符型,最小尺寸为8
位; -
wchar_t
:表示宽字符,最小尺寸为16
位; -
char16_t
:表示最小尺寸为16
位,Unicode
字符; -
char32_t
:表示最下尺寸为32
位,Unicode
字符; -
short
:表示短整型,最小长度为16
位; -
int
:表示整型,最小尺寸为16
位; -
long
:表示长整型,最小尺寸为32
位; -
long long
:长整型,最小尺寸为64
位; -
float
:单精度浮点数,6
位有效数字; -
double
:双精度浮点型,10
为有效数字; -
long double
:扩展精度浮点型,10
位有效数字;
-
-
C++
中对于长度的规定:一个int
至少和一个short
一样大,一个long
至少
和一个int
一样大,一个long long
至少和一个long
一样大,其中long long
类型是C++11
标准中定
义的; - 整型按照不同的标准还可以划分成为带符号整型(用于表示正数,负数,0),无符号类型(只能用于表示正数),
默认的都是带符号的,可以通过unsigned
来表示无符号类型; - 类型转换
类型所能表示的范围决定了转换的过程
非布尔类型的算数值赋值给布尔类型时,初始值为
0
,赋值结果为false
,否则结果为true
;如果讲上面的过程反过来进行赋值,初始值为
false
结果为0
,初始值为true
结果为1
;如果将一个浮点数据赋值给整数类型时,结果仅仅保留浮点数据的整数部分;
整数赋值为浮点数据类型时,小数部分为0,,如果整数的类型超过了浮点类型,精度就会有所损失;
如果将一个大范围的数,赋值给一个小范围的类型,赋值后的结果是进行取余操作;
- 整形:
#include
using namespace std;
int main(){
bool bi = 10;
bool by = 0;
cout << "one is "<< bi << " Next one is " << by << endl;
int i ;
i = 3.14;
cout << "The i(int) is "<< i << endl;
double dx ;
dx = 1;
cout << "The dx(double) is " << dx << endl;
unsigned char cx = -1;
cout << "Unsigned char(cx) is " << (int)cx << endl;
unsigned char cy = 256;
cout << "Unsigned char(cy) is " << (int)cy << endl;
}
字面值常量
每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型;
- 整型和浮点型
字面值包括十进制,八进制,十六进制等形式,用0
开始代表八进制,使用0x
或者0X
开头代表十六进制;对
于整型字面值常量的具体数据类型由它的值和符号来决定; - 字符和字符串型
使用''包含的单个字符称为char
字面值,使用""包含的0个或者多个字符称为字符串字面值;字符串字面值
通常表示的是由常量字符构成的数组,编译器会在每个字符串的结尾使用\0
,字符'A'
表示的是单个字符
但是"A"
表示的是两个字符'A'和\0
;对于编译器来说,如果存在两个字符串字面值之间仅仅由空格,缩进,
和换行符分离,name本质上就是一个整体; - 常用的转移序列
- 程序员不能够直接使用的字符包含以下两种:
- 1 不可打印字符,比如退格符号,换行符号;
- 2 在C++语言中由特殊定义的符号:单引号,双引号,问号,反斜线等,这就需要使用转义序列;
- 程序员不能够直接使用的字符包含以下两种:
符号 | 含义 |
---|---|
\n | 换行符,可以用于结束本行输入,进入下一行,也用来刷新缓存 |
\t | 横向制表符号,每次缩进有限个单位 |
\v | 表示纵向指标符号,垂直制表符号 |
\b | 表示退格符号,每次回退一格 |
" | 用于打印"" |
\ | 用于转义\ |
? | 用于转义? |
' | 用于转义'' |
\r | 表示回车符号 |
\f | 表示进纸符号 |
以上字符都是被当做一个字符来进行使用的; |
- 指定字面值类型
通过添加前缀或者后缀的方法可以用来改变整形,浮点型和字符型字面值的默认类型;
前缀 | 含义 | 类型 |
---|---|---|
u | Unicode 16字符 | char16_t |
U | Unicode 32字符 | char32_t |
L | 宽字符 | wchar_t |
u8 | UTF-8(仅仅用于字符串字面值常量) | char |
-
- 整数型字面值
后缀 | 最小匹配类型 |
---|---|
u or U | unsigned |
l or L | long |
ll or LL | long long |
- *浮点型字符类型
后缀 | 类型 |
---|---|
f or F | float |
l or L | long double |
- 布尔字面值
true
和false
都是布尔类型的字面值;
变量
变量提供一个具名的,可供程序操作的存储空间,数据类型决定着变量所占内存空间的大小和布局方式该空间能
存储值的范围,以及变量能够参与的运算;
-
定义变量
int sum1,sum2;
:首先是类型名,然后是变量列表,最后是;
结尾;- 对象:对象表示的是一块能够存储数据并且具有某种类型的内存空间;
- 对象可以表示类中;
- 通常已经命名的对象成为变量;
- 对象还用于表示能够被程序修改的数据,值通常用来表示只读的数据;
- 还是将对象理解为某种数据类型的内存空间;
- 对象:对象表示的是一块能够存储数据并且具有某种类型的内存空间;
-
初始化和赋值操作
- 初始化:对象在创建时获得的某个特定的值;在变量的初始化过程中,在一条语句中可以用一个已经初始化
的变量去为另一个要定义的变量进行初始化操作; - 赋值操作:表示的是将对象的当前值擦除,用一个新的值来进行代替;
初始化的四种方式
int i1 = 0; int i2 = {0}; int i3(0); int i4{0};
- 默认初始化操作:如果定义变量时,没有指定初始值,变量会执行默认初始化,默认值由变量类型来决定;如果
内置类型的变量未被初始化,定义于任何函数值外的变量会被初始化为0,定义域函数体内部的内置类型的变
量将不会被初始化,如果访问此类未被初始化的内置类型数据会发生错误;
在类里面可以各自决定其初始化对象的方式,并且是否允许不初始化就定义对象,也有类本身决定; - 当时用列表数初始化时,初始数值存在丢失的风险,编译器回进行报错;
- 初始化:对象在创建时获得的某个特定的值;在变量的初始化过程中,在一条语句中可以用一个已经初始化
-
声明和定义的关系:
- 在
C++
语言中为了支持分离式编译,通常将声明和函数的定义分开; - 声明使得名字为人所知,声明使用
extern
比如extern int i
,表示的含义是仅仅是声明了这个变量,但是不能够显式的初始化这个变量; - 定义负责创建和名字关联的实体;
- 变量只能够被定义一次,但是可以被声明很多次;
- 如果
extern
包含初始值就变成了定义;
总结: - 也就是当变量的定义前面有关键字
extern
时,并且没有进行变量的初始化赋值,就是变量的声明; -
C++
语言是一种静态类型的语言,也就是说,在编译阶段段进行类型检查;
- 在
-
标识符
组成:C++
语言的标识符,必须由字母.数字和下划线组成,并且必须由字母或者下划线开头,标识付没有长度限制;注意:
* 用户自定义的标识符不能够出现两个连续的下划线;
* 同时也不能够使用下划线紧跟大写字母开头;- 变量的命名规范
- 标识符需要体现特定的意义
- 变量名称一般使用小写;
- 用户自定义的类名一般使用大写字母开头;
- 如果标识符有多个单词组成,单词之间应该又明显的区分;
C++
关键字一共有:5*13+8=73
关键字;
- 变量的命名规范
-
作用域
作用域表示的是大部分是使用{}
进行分割的,并且作用域是可以进行嵌套的,当某个作用域中定义了变量,那么在其嵌套的的作用
域中都是可以访问该变量的;- 一个建议就是局部变量尽量不要和全局变量冲突;
-
复合类型
- 引用
引用可以分为左值引用和右值引用;- 左值引用:引用就是个对象起了另外一个名字.并且引用必须被初始化,且不能够修改引用的值,也就是说引用无法绑定一个
新的对象;对于引用的操作等价于对于引用对象的操作;
- 左值引用:引用就是个对象起了另外一个名字.并且引用必须被初始化,且不能够修改引用的值,也就是说引用无法绑定一个
- 指针
和引用的不同:
* 指针本身就是一个对象,允许对于指针进行赋值和拷贝,在指针的生命周期内,可以指向几个不同对象;
* 指针不需要在定义时,就进行赋值;
指针存放的是某个单元的地址,引用只是一个别名,并没有地址,所以不能够定义指向引用的指针
如果这样定义int ival = 1024; int *p = &ival;
但是这样其实也是可行,书里面说的不能够定义指向引用的指针,上面的代码都是没有任何错误的的;int inval = 1024; int &rival = ival; int *p = rival;
指针是一个地址,获取地址使用&
运算符; - 当存在指针时,指针的四种指向:
- 指针指向一个对象;
- 指向紧邻对象所占空间的的下一个位置;
- 空指针,也就是指向
NULL
; - 无效指针,也就是指向其他情况;
- 解引用
如果需要利用指针来访问对象,就需要使用解引用符号*
;
需要说明的是借用操作仅仅适合于那些真正指向某个对象的有效地址的指针; - 空指针的初始化
int *pival1 = nullptr; int *pival2 = 0; int *pival3 = NULL; //这里需要#include
nullptr
:是字面值,这个是C++11
引用的新标准,是一种特殊的字面值,可以被转换成任意的指针类型;
建议使用这种方式进行控制真的初始化;书里面给出的一点建议:初始化所有定义的指针;-
void *
指针
void *
类型的指针可以用于存放任意对象的地址,所以不确定是什么类型的时候,建议声明成这种类型; - 指针与引用的区别
- 引用本身不是一个对象,但是指针是一个对象,所以引用是可以指向指针的;
int i = 42; int *p; int *&r = p;
- 引用
-
const
限定符- 当定义某一个值,并且希望这个值不被改变时.可以使用
const
限定符; - 需要注意的是
const
定义时,就需要进行初始化,如果执行了默认初始化,之后值就不能够进行更改; - 如果利用一个对象来初始化另一个对象,那么两个对象是不是
const
并没有太多的要求; - 在默认情况下,
const
对象仅仅在文件内部有效;如果多个文件之间需要共享const
文件,那么需要使用关键字extern
;
- 当定义某一个值,并且希望这个值不被改变时.可以使用
-
const
与引用- 当引用和
const
进行绑定时,对常量的引用不能够用来修改所绑定的对象; - 当对象是一个
const
常量时,那么引用也必须是一个const
常量;
const int ci = 1024; const int &rci = ci; int &r2 = ci; //因为`ci`是一个`const`常量,所以引用必须是`const`常量,所以这里会出错;
-
const
引用通常称为"常量引用"; - 引用的类型必须和所引用对象的类型一致,但是例外的情况有两种:
- 初始化常量引用时,允许用任意表达式作为初始值;
int i = 42; const int &r1 = i; const int &r2 = 42; int &r4 = r1; //这个操作就是错误的,`r4`是一个非常量的引用;
- 对
const
的引用可能引用一个非const
的值;-
const int& i2 = i
,其中i
不一定是一个常量值;因为const
引用并不要求i
为常量,但是反过来,当i
为常量,对i
的引用必须是const
常量引用;
-
- 当引用和
-
const
和指针- 当
pi
是一个常量时,pi
的指针也必须是一个常量
const double pi = 3.14; const double* cptr = π
- 当
-
一点小总结:
- 指向常量的指针或者引用,只不过是指针或者常量以为自己指向了常量,而不去修改常量的值,但是实际上,这些值往往
是可以改变; - 指针允许定义为常量,但是常量指针必须进行初始化;
int *const ptr=&tr
;
- 指向常量的指针或者引用,只不过是指针或者常量以为自己指向了常量,而不去修改常量的值,但是实际上,这些值往往
-
顶层
const
与底层const
- 这里涉及两个问题,首先:指针是常量还是指针所指向的对象是个常量,顶层
const
表示的是指针本身是一个常量,尔底层const
用于表示指针所指向的对象是一个常量; - 另一方面来说顶层
const
用于表示任意的对象是常量,但是底层的const
用于表示指针和引用某些部分是常量; - 指针类型可以表示顶层也可以表示底层;
-
const int *const pintq = &intq
:对于这个来说最左边的const
表示底层,右边的const
表示顶层; - 用于声明引用的都是底层
const
; - 这些区别在进行对象拷贝是区别明显;
- 这里涉及两个问题,首先:指针是常量还是指针所指向的对象是个常量,顶层
-
常量表达式
- 用于表示在程序的便以阶段就得到结果的表达式,常见的包括
const
初始化的对象,字面值常量; - 所以
const int size = get_size()
,这个就不是常量表达式;size
的值只有等到程序运行时才可以的得到; -
C++11
允许将一个可能是常量表达式生命为constexpr
,让编译器进行检查;
- 用于表示在程序的便以阶段就得到结果的表达式,常见的包括
-
字面值常量
- 算数类型,引用和指针都属于字面值类型,其中指针被定义为
constexpr
时,初始值必须是nullptr
或者是0,或者是某个固定
地址里面的对象; - 函数体里面的变量一般存放于非固定的位置,所以不能够使用
constexpr
,但是所有函数体外的变量则例外; - 如果再
consexpr
声明时,定义了一个指针,那么该限定符仅对指针有效和指针所指向的对象无关;
- 算数类型,引用和指针都属于字面值类型,其中指针被定义为
-
类型处理
两种办法:* `typedef oldname newname` * `using newname = oldname`; * 定以后的别名和原来的名称完全等价,但是在理解替换后的别名时,是不能够替换回去的,这样产生的理解是错误的; * 例如:`typedef char* pstring; const pstring cstr = 0; const pstring *ps;` * 对于替换之后的解释,首先应该解释变量本身的类型,`cstr`是一个没有类型的,其次解释`pstring`的含义,是一个常量指 针,所以`cstr`是一个指向`char`的常量指针;对于`ps`,首先是一个指针类型,其次它的对象是一个指向`char`的常量指针; * 如果进行替换就会影响`const`和`*`的相对位置,从而理解含义就会发生变换;
-
auto
类型说明符-
auto
通过初始值来推断变量的类型.所以使用auto
时,必须包含初始值,如果连续进行赋值操作,那么类型必须是匹配的; - 对于引用来说,编译器使用应用对象的类型作为
auto
的类型; -
auto
会忽略顶层的const
,但是会保留底层const
;但是可以通过添加const
来保留顶层const
;这是对于指针来说; - 当设置为一个引用的
auto
时,初始值中顶层的const
仍然有效; - 在一条语句中定义多个变量时,符号
&
和*
不属于基本数据类型;
-
-
decltype
类型指示符需要通过表达式的类型来定义自己需要的类型,但是有不对表达式进行变量的初始化;
decltype(f()) sum = x;
编译器可以通过分析返回值f
的类型得到sum
的类型,而不需要对x
进行类型的判定;decltype
,如果使用的表达式是一个变量,那么decltype
返回该变量的类型(包括顶层的const
和引用);引用一直是引用对象的同义词,但是在
decltype
是一个例外;const int& cj = 0;decltype(cj) = ci;
在这里cj
是一个const int&
的引用,但是和引用本身的对象没有关系,只和类
型有关;如果
decltype(*p)
,如果进行解引用操作,那么得到的就是一个引用类型,并且引用类型必须有初始值;int i; decltype((i));decltype(i);
,前者得到是int&
类型,后者得到的是int
类型;(())
:表示的一定是引用;类里面的数据成员,在定义对象之后,每个对象都有数据的一份拷贝,数据之间是不会相互影响的;