第二章 变量与基本类型 章节重点

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34536551/article/details/82863481

 


指针和引用


●  指针本身就是一个对象,允许对指针复制和拷贝, 而且在指针的生命周期内它可以先后指向几个不同的对象。

● 指针无需在定义时赋初值。和其他内置类型一样,在块作用域内定义的指针如果没有初始化,也将拥有一个不确定的值。

● 指针是一个实体,而引用只是一个已经存在的对象所起的别名。

●  引用使用时无须解引用(*),指针需要解引用。

● 引用只能在定义时初始化一次,之后不能改变指向其他变量(从一而终),指针变量可以指向其他不同的对象。

●  引用必须指向有效的变量,指针可以为空,引用不能为空。

  sizeof 指针对象和引用对象的意义不一样, sizeof 引用得到的是所指向变量(对象)的大小。

而sizeof 指针本身的大小,即所指向的变量或对象的地址大小。

●  指针和引用自增自减的意义不一样。

● 从内存分配上看,程序需要为指针变量分配内存区域,而引用不需要分配内存区域。

●  相对而言,引用比指针更加安全。

●  使用指针比引用灵活,但是其风险也很大,使用指针时一定要检查指针是否为空(NULL), 且内存空间回收后,指针是否置零,以免野指针的发生,发生内存泄漏。


相同点:


●   两者都是地址的概念,指针指向一块连续的内存空间,其内容为所指内存的地址,引用是某块内存的别名。

●  指针可以 指向数组的地址来代替数组使用,而引用不可以代替数组引用只能指向数组中的某一个元素。


注意:返回的引用不能指向超出作用域范围的对象,这些对象在函数结束后、将不存在,这是不合法的,不要返回局部变量的引用。

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


注意: 一般情况下:其他所有引用的类型都要和与之绑定的对象严格匹配。而且,引用只能绑定在对象上, 而不能与字面值或某个表达式的计算结果绑定在一起。

int  &refVal4=10;  //错误:引用类型的初始值必须是一个对象
double dval=3.14;
int &refVal5=dval;  //错误:此处引用类型的初始值必须是int型对象

const 引用

但是有两个例外:

●  在初始化常量引用时允许用任意表达式作为初始值, 只要该表达式的结果能转换成引用的类型即可。 尤其, 允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式:

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

普通引用不能用任意表达式作为初始值。

● 注意 :如果需要将一个常量赋给一个引用,必须赋给一个常量引用, 非常量引用是无法这样做得。

● 注意 : 常量引用仅对引用可参与的操作做出了限定, 对于引用的对象本身是不是一个常量未作限定。 因为对象也可能是个非常量, 所以允许通过其他途径改变它的值

int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0;
r2 = 0;  //错误: r2是一个常量引用

注意:  一般情况下:其他所有指针的类型都要和它所指向的对象严格匹配:

double dval = 9;
double *pd = &dval;
double *pd2 = pd;

int *pi = pd; //错误:指针pi的类型和pd的类型不匹配
pi = &dval; //错误:试图把double型对象的地址赋给int 型 指针

因为在声明语句中指针的类型实际上被用于指定它所指向对象的类型,所以二者必须匹配。 如果指针指向了一个其他类型的对象,对该对象的操作将发生错误。

● 指针的值(即地址)应属于下列4种状态之一:

(1) 指向一个对象

(2)指向紧邻对象所占空间的下一个位置。

(3) 空指针,意味着指针没有指向任何对象

(4) 无效指针, 也就是上述情况之外的其他值——野指针

● 注意: 试图拷贝或以其他方式访问无效指针的值都将引发错误。 编译器并不负责检查此类错误, 这一点和试图使用未经初始化的变量是一样的。 访问无效指针的后果无法预计, 因此程序员必须清楚任意给定的指针是否有效。

尽管第2种和第3种形式的指针是有效的, 但其使用同样受到限制。 显然这些指针没有指向任何具体对象, 所以试图访问此类指针(假定的)对象的行为不被允许。 如果这样做了, 后果也无法预计。

● 注意: 解引用操作仅适用于那些确实指向了某个对象的有效指针。

● 空指针: 不指向任何对象, 在试图使用一个指针之前的代码可以首先检查它是否为空, nullptr是一种特殊类型的字面值,它可以被转换成任意其他的指针类型,

● NULL——预处理变量,来给指针赋值, 这个变量在头文件cstdlib中定义的,它的值就是0。

● 预处理变量不属于命名空间std,它由预处理器负责管理, 因此我们可以直接使用预处理变量而无须在前面加std::

当用到一个预处理变量时, 预处理器会自动地将它替换为实际值, 因此用NULL初始化指针和用0初始化指针是一样的。

注意: 把int 变量直接赋给指针是错误的操作, 即使int变量的值恰好等于0也不行。

int *p1 = nullptr;
int zero = 0;
p1 = zero;  //错误, 不能把int变量直接赋给指针

● 注意只要指针拥有一个合法值,就能将它用在条件表达式中。 和采用算术值作为条件遵循的规则类似, 如果指针的值是0, 条件取false.   任何非0指针对应的条件值都是true.

 对于两个类型相同的合法指针,可以用“==”和 “!=” 来比较它们,比较的结果是布尔类型。 如果两个指针存放的地址值相同,则它们相等;反之亦然。

这里两个指针存放的地址值相同(两个指针相等)有三种可能:它们都为空、都指向同一个对象,或者都指向了同一个对象的下一个地址。  需要注意的是:  一个指针指向某对象,同时另一个指针指向另外对象的下一个地址,此时也有可能出现这两个指针值相同的情况,即指针相等。


指针和const


● 与引用一样, 也可以令指针指向常量或非常量, 指向常量的指针不能用于改变其所指对象的值。 注意 : 要想存放常量对象的地址, 只能使用指向常量的指针

const double pi = 3.14;
double *prt = π  //错误, ptr是一个普通 指针
const double *cptr = π  //正确,可以指向一个双精度 常量
*cptr = 42;  //错误, 不能给*cptr 赋值

● 指针的类型必须与其所指对象的类型一致, 但是有两个例外:

 允许令一个指向常量的指针指向一个非常量对象:

const double pi = 3.14;
const double *cptr = π  //正确,可以指向一个双精度 常量

double dval=3.14;  // dval是一个双精度浮点数, 它的值可以改变
cptr=&dval; // 正确:  但是不能通过 cptr改变dval的值

注意: 和常量引用一样, 指向常量的指针也没有规定其所指的对象必须是一个常量。 所谓指向常量的指针仅仅要求不能通过该指针改变对象的值, 而没有规定那个对象的值不能通过其他途径改变。


指向指针的指针


● 指针也有自己的地址, 允许把指针的地址再存放到另一个指针当中, 通过* 的个数可以区分指针的级别

● 解引用int型指针会得到一个int型的数,同样, 解引用指向指针的指针会得到一个指针。 此时为了访问最原始的那个对象的, 需要对指针的指针做两次解引用

int ival = 1024;
int *pi = &ival; //pi 指向一个int型的数
int **ppi = π // ppi指向一个int型的指针

cout << "分别输入它们的值为:" << ival << " " << *pi << " " << " " << **ppi << endl;

指向指针的引用


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

int i = 42;
int *p;  //p是一个int型指针
int *&r = p; r是一个对指针p的引用

r = &i; //r引用了一个指针,因此r赋值 &i 就是 令p指向i
*r = 0; //解引用r 得到i, 也就是p指向的对象,将i的值改为0

cout << i << endl;
cout << *p << endl;
cout << *r << endl;

要理解r的类型到底是什么,最简单的方法是从右向左阅读r的定义。 离变量名最近的符号(此列中是&r的符号&)对变量的类型有最直接的影响, 因此r是一个引用。 声明符的其余部分用以确定r引用的类型是什么, 此例中的符号 * 说明 r引用的是一个指针。  最后, 声明的基本数据类型部分指出r引用的是一个int 指针。


const 限定符


● const常量(对象)必须定义的同时初始化, 不可分开初始化, 它的初始值可以是任意复杂的表达式

● const跟非const类型的对象相比, 大部分的操作都可以完成,也不是所有的操作都适合, 主要的限制就是 只能在const 类型的对象上执行不改变其内容的操作, 可以参与算术运算, 也可以转换成一个布尔值。

如果利用一个对象去初始化另外一个对象, 则它们是不是const 都无光紧要

int i = 42;
const int ci = i;  //正确
int j = ci;  //正确

 ci的常量特征仅仅在执行改变ci的操作时才会发挥作用。 当用ci去初始化j时,根本无须在意ci是不是一个常量。 拷贝一个对象的值并不会改变它, 一旦拷贝完成, 新的对象就和原来的对象没什么关系了。


● 注意: 默认情况下, const 对象被设定为仅在当前文件内有效。 当多个文件中出现了同名的const变量时, 其实就等同于在不同文件中分别定义了独立的变量

如果想在多个文件之间共享const对象,解决的办法是: 对于const变量不管是声明还是定义都添加extern 关键字, 这样只需定义一次就可以了,

在别的文件中用extern 做限定, 作用是指明 该常量并非本文件所独有, 它的定义将在别处出现。


const指针


● 注意: 常量指针必须初始化, 而且一旦初始化,则它的值(也就是存放在指针中的那个地址)就不能在改变了,即指针不能指向别的对象了.    隐含的意思是: 即不变的是指针本身的值而非指向的那个值。

● 注意: int *const cp; //错误, 常量指针必须初始化,因为其值不能改变

● 注意: 指针本身是一个常量并不意味着不能通过指针修改其所指对象的值, 能否这样做完全依赖于所指对象的类型。

猜你喜欢

转载自blog.csdn.net/qq_34536551/article/details/82863481