C++ primer学习笔记之变量和基本类型

这里是学习C++ primer做的笔记

基本内置类型

C++定义了一套算数类型和空类型在内的基本数据类型

算术类型

算术类型分为两大类:整型(包括字符和布尔类型)和浮点型。分别如下:

  • bool(布尔类型):取值是真(true)或者假(false)
  • char(字符):一个char空间应该确保可以存放机器基本字符集中任意字符对应的数字值,即一个char的大小和一个机器字节一样。
  • wchat_t(宽字符),char16_t(Unicode字符),char32_t(Unicode字符):wchat_t类型用于确保可以存放机器最大扩展字符集中的任意一个字符,类型char16_t和char32_t则为Unicode字符集服务。
  • short(短整型,16),int(整形,16),long(长整形,32),long long(长长整形,64)
  • float(单精度浮点数),double(双精度浮点数),long double(扩展精度浮点数):float的有效位是7,double的有效位是16,long double 常常被用于有特殊浮点需求的硬件。

带符号类型和无符号类型

  • 除去布尔类型和扩展的字符型外,其他整形可以划分为带符号和无符号的两种,带符号的可以表示正数,负数和0,无符号类型仅能表示大于等于0的值。
  • 类型int,short,long和long long都是带符号的,通过在这些类型名前添加unsigned就可以得到无符号类型,例如 unsigned long,类型unsigned int可以缩写为unsigned。
  • 字符型和其它的不一样,被分为三种:char,signed char和unsigned char。类型char和signed char并不一样,虽然有三种,但是表现形式却只有两种:带符号的和无符号的。类型char实际上会表现为上述两种形式中的一种,具体由编译器决定。
  • 无符号的类型中用全部的比特来存储值,比如8比特的unsigned char可以表示0至255区间内的值,对于带符号的,比如signed char理论上可以表示-127纸127区间内的值,但是现在的计算机实际的表示范围是-128至127.

变量

变量提供一个具名的,可供程序操作的存储空间,C++中的每一个变量都有其数据类型,数据类型决定这变量所占内存空间的大小和布局方式,该空间能存储的值的范围,以及变量能参与的运算【这加重的话没看懂额???】。

初始化

C++语言中,初始化和赋值是两个完全不同的操作,初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来代替。
这里给出4种初始化的方法,其中{}表示的是列表初始化。

1
2
3
4
int units=0;
int units={0};
int units{0};
int units(0);

这里需要注意一点的是,如果我们使用列表初始化且初始化存在丢失信息的风险,则编译器会报错。比如下面的:

1
2
3
long double ld=3.1415926536
int a{ld};//错误,存在丢失信息的风险
int a(ld);//正确,只是丢失了部分值

这里如果定义变量时没有指定初值,则变量被默认初始化,定义在任何函数体之外的内置类型(就是前面讲到的已经实现的类型,如int,long等)变量被初始化为0,定义在函数体内部的内置类型变量将不被初始化,一个未被初始化的内置类型变量的值是未定义的,如果试图拷贝或以其他形式访问将引发错误。

变量声明和定义

声明使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明,而定义负责创建于名字关联的实体。
如果想声明一个变量而非定义它,就在变量名前添加关键词extern,而且不要显示初始化变量。在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。

复合类型

这里讲到两种:引用和指针

引用

为对象起了另外一个名字,并非对象,而是为一个已经存在的对象所起的另外一个名字,引用必须初始化。

1
2
int ival=1024;
int &refVal=ival;

引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起

1
2
3
int & ref=10;//错误
double dval=3.14;
int &refVal=dval;//错误,引用类型的初始值必须是int类型对象

指针

利用指针访问对象

1
2
3
int val=10;
int *p=&val;
cout<< *p;

空指针,使用nullptr或者是NULL

1
2
3
int *p1=nullptr;//等价于int *p1=0;
int *p2=0;
int *p3=NULL;//要先使用#include cstdlib

指针和引用都能提供对其他对象的间接访问,但是一旦定义了引用,就无法令其再绑定到另外的对象了,之后每次使用这个引用都是访问它最初始绑定的那个对象。指针和它存放的地址之间就没有这种限制,和其他任何变量一种,给指针赋值就是令它存放一个新的地址。

1
2
3
4
int i=42;
int *pi=0;
pi=&i;//指向i地址
pi=0;//不指向任何对象

理解符合类型

定义多个变量的时候,一般的写法是*,&和变量名一起,如果和类型一起容易误导:

1
int* p1,p2;//定义了一个指向int的指针,和一个int型变量

指向指针的指针:声明符中的修饰符的个数并没有限制,多个修饰符连接到一起,只要符合逻辑即可,比如下面:

1
2
3
int ival=1024;
int *pi=&ival;//指向整形变量的指针
int **ppi=&pi;//指向(指向整形变量的)指针的指针

指向指针的引用:引用本身不是一个对象,因此不能定义指向引用的指针,但指针是对象,所以存在对指针的引用:

1
2
3
4
int i=42;
int *p;
int *&r=p;//这句话这样看,首先r是一个指针,中间的&表示对p的引用,一般从右往左读,以便分析含义。
r=&i;//即将p指向i

const 限定符

const的引用

前面提到引用类型必须与其所引用对象的类型一致,但是有两个例外,第一个例外是在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。

1
2
3
4
5
int i=42;
const int &r1=i;//允许const int&绑定到一个普通int对象上
const int &r2=42;//正确:r2是一个常量引用
const int &r3=r1*2;//正确:r3是一个常量引用
int &r4=r1*2;//错误:r4是一个普通的非常量引用

指针与const

指向常量的指针

1
2
3
4
const double pi=3.14;//
double *ptr=&pi;//错误,ptr是普通指针,需要一个常量指针
const double *cptr=&pi;
*cptr=42;//错误,不能给常量赋值

常量指针:指针本身为常量,必须初始化,,一旦初始化后,值就不再改变了,但是指向的对象是可变的,指向常量的常量指针都不能变。

1
2
3
int errNum=0;
int *const curErr=&errNum;
errNum=20;

这里用顶层const指针本身是常量,用底层const表示指针所指的对象是一个常量。

1
2
3
4
int i=0;
int *const p1=&i;//顶层const,p1不能改变(指针本身不能改变)
const int ci=42;//顶层const
const int *p2=&i;//底层const,p2可以改变

constexpr

常量表达式是指值不会改变并且编译过程就能得到计算结果的表达式,由数据类型和初始值共同决定,比如下面几种情况:

1
2
3
4
const int max_files=20;//max_files是常量表达式
const int limit=max_files+1;//limit是常量表达式
int staff_size=27;//staff_size不是常量表达式
const int sz=get_size();//sz不是常量表达式

处理类型

一是一些类型难于拼写,二是有的时候根本搞不清需要什么类型,程序员不得不回过头从程序的上下文中寻求帮助。

类型别名

有两种方法定义,一是使用typedef,一是使用using

1
2
typedef double wages;//wages是double的同义词
using SI=Sales_item;//SI是Sales_item的同义词

指针,常量和类型别名

1
2
3
typedef char *pstring;//pstring是char*的同义词
const pstring cstr=0;//定义cstr是指向char的常量指针
const pstring *ps;//ps是一个指针,它的对象是指向char的常量指针

注意下面两行语句的区别:

1
2
3
4
const pstring cstr=0;//定义cstr是指向char的常量指针
const char *cstr=0;//定义cstr是指向常量char的指针
//上面两行是不同的,加上括号应该就等价了
const (char *)cstr=0;

auto

编程时常常把表达式的值赋给变量,这就要求在声明变量的时候清楚知道表达式的类型,然而要做到这一点并不容易,有时甚至根本做不到,为了解决这个问题,C++11引入auto类型,用它让编辑器替我们分析表达式所属的类型。auto让编译器通过初始值来推算变量的类型。

decltype

有时会遇到这种情况,希望从表达式的类型推断出要定义的变量类型,但不想用该表达式的值初始化变量。C++引入第二种类型说明符decltype,其作用是选择并返回操作数的数据类型。

1
2
3
const int ci=0,&cj=ci;//
decltype(cj) y=x;//
decltype(cj) z;//错误,z是引用,必须初始化

如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。
decltype((variable))的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用。

如果觉得有帮助,给我打赏吧!