C++入门笔记(二)变量和基本类型

变量和基本类型 

一、基本内置类型

1.除去布尔类型和扩展的字符型外,其他整型可以分为带符号的和无符号的。

2.与其他整型不同,字符型被分为了三种:char、signed char 和 unsigned char。

其中类型char会表现为带符号和无符号其中的一种,具体由编译器决定。

3.执行浮点数运算一般选用double,这是因为float通常精度不够而且单精度浮点数

和双精度浮点数的计算代价相差无几。事实上,某些机器上双精度更快。long double

一般没必要,它带来的运行时消耗不容忽视。

4.切勿混用带符号类型和无符号类型:如果表达式里既有带符号类型又有无符号类型,当

带符号类型取值为负时会出现异常结果,这是因为带符号数会自动转换为无符号数。

5.十进制字面值是带符号数,八进制和十六进制字面既可能是带符号的也可能是无符号的。

十进制字面值的类型是int、long 和 long、long 中尺寸最小的那个。类型short没有对应

的字面值。

6.字符串字面值的类型实际上是由常量字符构成的数组。编译器在每个字符串的结尾处添加

一个空字符(''),字符串字面值的实际长度要比它的内容多1。如果两个字符串字面值紧

邻且仅由空格、缩进和换行符进行分隔,则它们实际上是一个整体。

7.指定字面值的类型:

    1)L'a'        //wchar_t

    2)u8"hello"    //utf-8字符串字面值

    3)88ULL       //无符号整型字面值,类型是unsinged long long 

    ......

8.nullptr是指针字面值

二、变量

1.变量提供一个具名的、可供程序操作的存储空间。C++中的每个变量都有其数据类型,

数据类型决定着变量所占内存空间大小和布局方式以及变量能参与的运算。对C++程序员

来说,“变量”和“对象”一般可以互换使用。

2.很多程序员对于使用等号=来初始化变量的方式倍感困惑,这种方式让人认为初始化是

赋值的一种。事实上在C++语言中初始化和赋值是两个完全不同的操作:初始化---创建

变量时赋予其一个初始值,赋值---把对象的当前值擦除,而以一个新值来代替。

3.C++11标准中,可以用花括号初始化变量:

int a{0};
double b{1.22}

这种初始化形式被称为列表初始化。

4.变量的声明和定义:

为了允许把程序拆分成多个逻辑部分来编写,C++语言支持分离式编译机制,该机制允许将程序

分割为若干个文件,每个文件可以被独立编译。如果将程序分割为多个文件,则需要有在文件间

共享代码的方法。为了支持分离式编译,C++将声明和定义区分开来。

声明:使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。

定义:负责创建与名字关联的实体,申请存储空间,也可能为变量赋初始值。

extern int i;               //声明i而非定义i
int j;                      //声明并定义j
extern int k = 19;          //赋值了,定义

在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。

变量能且只能被定义一次,但可以被多次声明。

如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的

定义必须出现且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。

三、复合类型

复合类型:基于其他类型定义的类型。

1. 引用

引用(reference):为对象起另外一个名字,引用类型引用(refers to)另外一种类型。

通过将声明写为&name形式来定义引用类型,例如:

int a = 24;
int &refA = a;           //指向a,即a的另外一个名字
int &refB;               //引用必须被初始化

无法另引用重新绑定到另外一个对象,因此引用必须初始化。

定义了一个引用后,对其进行的所有操作都是在与之绑定的对象上进行的。

因为引用本身不是一个对象,所以不能定义引用的引用。

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

2. 指针

指针(pointer)是"指向"另外一种类型的复合类型。指针与引用的不同点:

1)指针本身就是一个对象,允许对指针的赋值和拷贝,而且在指针的声明周期内它可以先后指向几个不同的对象;

2)指针无需在定义时赋初始值;

定义指针的方法:

int *ip1, *ip2;                   
double dp, *dp2;               

指针存放某个对象的地址,要想获取该地址,需要使用取地址符。

int ival = 43;
int *p = &ival;        //p存放变量ival的地址,或者说p是指向变量ival的指针

因为引用不是对象,没有实际地址,所以不能定义指向引用的指针。

除了两种例外情况,其他所有指针的类型都要和它所指向的对象严格匹配:

double dval;
double *pd = &dval;          //正确:初始值是double型对象的指针
double *pd2 = pd;            //正确:初始值是指向double对象的指针

int *pi = pd;                //错误:类型不匹配

指针值:

指针的值应属下列4种状态之一:

1.指向一个对象;

2.指向紧邻对象所占空间的下一个位置;

3.空指针,意味着没有指向任何对象;

4.无效指针,上述情况的其他值。

试图拷贝或以其他方式访问无效指针都将引发错误。编译器并不负责检查此类错误。

尽管第2种和第3种形式的指针是有效的,但其使用同样受到限制。显然这些指针没有

指向任何具体对象,所有试图访问此类指针(假定的)对象的行为不被允许。

使用解引符来访问指针指向的对象:

int ival = 42;
int *p = &ival;
cout << *p;                   //输出42
*p = 9;
cout << *p;                   //输出0

空指针不指向任何对象,在试图使用一个空指针之前代码可以首先检查它是否为空。生成空指针的方法:

int *p1 = nullptr;
int *p2 = 0;

nullptr是一种特殊类型的字面值,它可以被转换为其他的指针类型。

int zero = 0;
int *p = zero;             //错误

应该尽量初始化所有指针。有时候想要搞清楚赋值语句到底是改变了指针的值还是改变了指针所指对象的值不太容易,

最好的办法就是记住赋值永远改变的是等号左侧的对象。

p = &val;             //p的值被改变,现在指向了ival
*p = 8;               //ival的值被改变,指针p并没有变

void*是一种特殊的指针类型,可用于存放任意对象的地址。

double obj = 3.14, *pd = &obj;
void *pv = &obj;
pv = pd;

利用void*指针能做的事比较有限:拿它和别的指针做比较、作为函数的输入和输出,或者赋值

给另外一个void*指针。不能直接操作viod*指针所指的对象,因为我们并不知道这个对象到底是什么类型。

易误解的声明方式:

int* p1, p2;                    //p1是指向int的指针,p2 是int

指向指针的指针 

指针是内存中的对象,像其他对象一样也有自己的地址,因此允许把指针的地址再放到另一个指针中。

int val = 1024;
int *pi = &val;
int **ppi = &pi;                  //ppi指向一个int型的指针
cout << **p << endl;

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

int i = 42;
int *p;              
int *&r = p;                  //r是一个对指针p的引用
r = &i;                       //r引用了一个指针,因此给r赋值&i就是令p指向i
*r = 0;                       //将i的值改为0

要理解r的类型到底是什么,最简单的方法是从右向左阅读r的定义。离变量名最近的符号(此例中是&r的&)

对变量有最直接的影响,因此r是一个引用。声明符的其余部分用以确定r引用的类型是什么,此例中的*说明r是一个指针。

四、const限定符

使用关键字const对变量的类型加以限定,使其成为一个常量:

const int a = 99;
a = 100;              //引发错误

const对象必须初始化。默认情况下,const对象被设定为仅在文件内有效。

对于变量不管是声明还是定义都添加extern关键字,这样只需要定义一次就可以了,其他文件都可见。

extern const int a = 99;

const的引用: 

可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用。

与普通的引用不同,对常量的引用不能被用作修改它所绑定的对象:

const int ci = 1024;const int &r1 = ci;          //正确,引用及其对应的对象都是常量
r1 = 22;                     //错误
int &r2 = ci;                //错误:试图让一个非常量引用指向一个常量对象

指针和const 

指向常量的指针,不能用于改变所指对象的值。

const double pi = 3.14;                        //常量,不能改变其值
double *ptr = &pi;                            //错误
const double *cptr = &pi;                     //正确
*cptr = 3.1415;                               //错误:不能给*cptr

例外:

double dval = 222.22;
cptr = &dval;                    //正确:但是不能通过cptr改变dval的值

const 指针: 

常量指针必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能改变了。

int errNumb = 0;
int *const curErr = &errNumb;   //curErr将一直指向errNumb
const double pi = 3.14;
const double *const pip = &pi;   //pip是一个指向常量对象的常量指针

指针本身是一个常量并不意味着不能通过指针修改其所指对象的值。

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

constexpr 和常量表达式

常量表达式是指值不会改变并且在编译阶段就能得到计算结果的表达式。

显然,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。

const int sz = get_size();   //常量sz的具体值要到运行时才能获取到,所以不是常量表达式。

将变量声明为 constexpr 类型以便由编译器来验证变量的值是否是一个常量表达式。

constexpr int mf = 20;
constexpr int sx = size();       //只有当size是一个constexpr函数时,才是一条正确的声明语句

函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量。

 五、处理类型

1.类型别名

类型别名是一个名字,他是某种类型的同义词。定义类型别名的两种方法:

1)使用typedef关键字

typedef double wages;
typedef wages base, *p;            //base是double的同义词,p是double*的同义词

2)使用别名声明:

using SI = Sale_item;           //SI是Sales_item的同义词
typedef char *pstring;           //pstring 实际上是char*的别名
const pstring cstr = 0;          //cstr是指向char的常量指针
const pstring *ps;               //ps是一个指针,它的对象是指向char的常量指针

2. auto类型说明符

auto类型说明符能让编译器帮助我们分析表达式的类型。

auto定义的变量必须有初始值

auto  a = b + c;
auto i = 0, *p = &i;            //正确:i 是整数、p是整型指针
auto sz = 0, pi = 3.14;       //错误,类型不一致

编译器推断出来的类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其符合初始化规则。

int i = 0, &r = i;
auto a = r;                   //a是一个整数(r是i的别名,而i是整数)

auto会忽略掉顶层const,同时底层const会保留下来,比如当初始值是一个指向常量的指针时:

int i = 2;
const int ci = i,  &cr = ci;
auto b = ci;          //b是整数(ci的顶层const特性被忽略掉了)
auto c = cr;          //c是整数(cr是ci的别名,ci本身是一个顶层const)
auto d = &i;         //d是一个整型指针(整数地址就是指向整数的指针)
auto e = &ci;       //e是一个指向整数常量的指针

 auto类型是一个顶层const:

const auto f = ci;             //ci的推演类型是int, f 是 const int

将引用类型设为auto,此时原来的初始化规则任然适用:

auto &g = ci;                     //g是一个整型常量引用,绑定到ci
auto &h = 42;                   //错误:不能为非常量引用绑定字面值
const auto &j = 42;           //正确:可以为常量引用绑定字面值

六、自定义数据结构

定义Sales_data类型:

struct Sales_data {
    std::string bookNo;   //数据成员
    unsigned units_sold = 0;
    double revenue = 0.0;
};
原文地址:https://www.cnblogs.com/Shadowplay/p/10524260.html