C++ Primer 阅读笔记(一)

简介: C++ Primer 阅读笔记(一)

2.3 复合类型

2.3.1 引用

2.3.2 指针

1 指针

指向是指向另一种类型的复合类型


与引用类似


都实现了对其它对象的间接访问


与引用不同


指针本身是对象,允许对其赋值和拷贝


在其生命周期内,可以指向几个不同的对象


不需要在定义时赋初值,在块作用域若未初始化,则拥有一个不确定的值


2 获取对象的地址

指针存放某个对象的地址,获取对象地址,需用取地址符(&)


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

除了两种例外情况(后面总结),其它所有的类型都要和它所指向的对象严格匹配,若指针指向了一个其它类型的对象,则该对象操作将发生错误【编译不通过】

3 指针值

指针的值 (即地址) 应属下列四种状态之一


1、指向一个对象


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


3、空指针,指针没有指向任何对象


4、无效指针,即上述情况之外的指针


拷贝或访问无效指针将引发错误,且编译器不会检查。这与使用未初始化的变量一样。第2,3种的指针有效,但没有指向任何对象,所以访问此类指针对象的行为不允许。


指向紧邻对象所占空间的下一位置如何理解?


例:声明一个 int32 数组,数组在初始化时会自动分配地址以及元素个数。假定起始地址为001E,int* p=arr,即指向该数组的指针,指向其首部001E。


按照位计算,4字节容量的int就是占有32位,即紧邻对象位置001E+32=003E再下一个位置就是003E+32=005E。一般不用按照占位运算,而是通过指针加减法,让编译器根据指针所指向的数据的大小进行移动


实际上就是通过指针算术运算,改变其指向的内存空间地址


4 利用指针访问对象

如果指针指向一个对象,则可用一个解引用符(*)来访问对象


对指针解引用会得到指向的对象,对解引用结果赋值就是给指向对象赋值


5 空指针

空指针不指向任何对象,在使用指针前需检查是否为空


几个生成空指针的方法


int *pl = nullptr;     // 等价于 int *pl =0;
int *p2 =0;           //直接将p2初始化为字面常量0 需include cstdlib
int *p3 = NULL;        // 等价于 int *p3 =0;

nullptr 初始化指针是得到空指针最直接的方法,也是C++11新标准引入的办法。它是一种特殊的字面值,可以被转换成任意的指针类型,也可以通过字面值 0 来生成空指针


NULL是预处理变量,在头文件cstdlib中定义,它的值就是0。预处理变量不属于命名空间std,由预处理器管理,它会将预处理变量转换为实际值,因此用NULL初始 化指针和用0初始化指针是一样的


【建议】


在新标准下的C++程序最好使用nullptr,把 int (0) 变量直接赋给指针是错误操作


建议初始化所有指针,并且尽量定义对象后再定义指向它的指针。若不清楚指针指向何处,则初始化为nullptr或者0, 便于程序检测


6 赋值和指针

指针和引用都可以对其它对象的间接访问,而最重要的区别是,引用本身并非一个对象,无法绑定到另外对象上


指针没有这样的限制,给指针赋值就是令它存放一个新对象


确定改变指针值,还是所指对象值,只需:赋值改变的永远是等号左边的对象


7 其它指针操作

只要指针拥有一个合法值,就能用在表达式中,和采用算术值作为条件


int ival =1024;
int *pi =0;                // pi合法,是一个空指针
int *pi2 = &ival;           // pi2是一个合法的指针,存放着ival的地址 
if (pi)                     // pi的值是0,条件的值是false
if (pi2)                    // pi2指向ival,值不是0,条件的值是true

任何非0指针对应的条件值都是true


对于两个类型相同的合法指针,可以用相等操作符(=)或不相等操作符(!=)来


比较,比较结果是布尔类型


如果两个指针存放地址值相同,则它们相等;反之不相等。两个指针相等有两种可能:它们都为空、 或都指向了同一地址


8 void*指针

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


一个 void* 指针存放着—个地址,但并不了解该地址中的对象类型


以 void 的视角看,内存空间仅仅是内存空间,无法访问内存空间中的对象


2.3.2 节练习

【练习 2.18】
编写代码分别更改指针的值以及指针所指对象的值
int a =1 , b =1;
int *ptr = &a;
*ptr =2;  // 更改指针所指对象的值
*ptr = &b; // 更改指针的值
【练习 2.19】
说明指针和引用的主要区别
指针和引用类似都提供间接操作对象的方式,主要区别在于指针是对象而引用不是,因为指针不必初始化赋值,引用必须;指针可以拷贝赋值,引用不呢;指针生命周期内可以指向多个对象,引用只能和初始化的对象绑定;
【练习2.20】
请叙述下面这段代码的作用
int i =42;     // 用int字面值常量初始化变量i
int *pl = &i;     // 指针pl指向i,或把i的内存地址赋值给指针pl
*pl = *pl * *pl;    // 为变量i赋值,值为 42*42
【练习2.21】
请解释下述定义。在这些定义中有非法的吗?如果有,为什么?
int i =0;
(a) double* dp = &i; // 非法,除了两种特殊情况,指针类型和对象类型需一致  
(b) int *ip = i;     // 非法,无法用整型的字面值常量给指针赋值
(c) int *p = &i;     // 合法
【练习2.22】
假设p是一个int型指针,请说明下述代码的含义
if (p) // 判断指针的值是否为0,即判断指针是否为空指针
if (*p) // 判断指针指向的对象是否为0,判断指针指向对象值是否为0
【练习2.23】
给定指针p,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断思路;如果不能,也请说明原因
可以:用*p将其值输出,若编译器报错,证明p指向一个无效对象,要么p=0,要么p未进行初始化,此时可以用if(p == NULL)进行判断即可
不可以:无法检测其实有效指针,且无法判断其指向的地址是否合法;
【练习2.24】在下面这段代码中为什么p合法而lp非法?
int i =42;       
void *p = &i; 
long *lp = &i;
void* 指针为任意类型指针,可转换成任意指针类型;而lp为long类型指针,指针指向对象的类型必须和其类型一致

2.3.3 理解复合类型的声明

1 定义多个变量

变量定义 包括一个基本类型 + 声明符,在同一条定义语句中,基本类型只有一个,但声明符却不同。即,一 条定义语句可定义出不同类型变量


int i =1024, *p = &i, &r = i;

基本数据类型是int而非int* , * 仅修饰了 p , 对声明语句中其它变量,不产生作用


int* p;            //合法但是容易产生误导

涉及指针或引用的声明,一般有两种写法


1、把修饰符和变量标识符写在一起


这种形式着重强调变量具有的复合类型


int *pl, *p2; // pl和p2都是指向int的指针


2、把修饰符和类型名写在一起,并且每条语 句只定义一个变量


这种形式着重强调声明定义了一种复合类型


int* pl;  // pl是指向int的指针
int* p2;  // p2是指向int的指针

2 指向指针的指针

一般声明符中修饰符个数没有限制。当多个修饰符连写时,需按照其逻辑关系解释


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


通过*的个数可以区分指针的级別。即,**表示指向指针的指针,***表示指向指针的指针的指针,以此类推


通过*的个数可以区分指针的级別。即,**表示指向指针的指针,***表示指向指针的指针的指针,以此类推
int ival =1024;
int *pi = &ival; // pi 指向一个 int 型的数
int **ppi = π // ppi指向一个 int 型的指针

3 指向指针的引用

引用不是对象,故不存在指向引用的指针,但指针是对象,所以存在指针的引用


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

理解 r 类型,办法是从右向左阅读 r 定义


离变量名最近的符号决定变量类型,因此r是一个引用。声明符其余部分用以确定 r 引用的类型。* 说明 r 引用的是一个指针。基本数据类型部分指出 r 引用的是一个 int 指针


2.3.3节练习

【练习2.25】
说明下列变量的类型和值
(a) int* ip, i, &r = i;  // 指针(不确定) int整型(0) 引用(0)
(b) int i, *ip =0;    // int整型(0) 空指针(0)
(c) int* ip, ip2;    // 指针(不确定) int整型(0)
相关文章
|
5月前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
|
5月前
|
编译器 数据安全/隐私保护 C++
c++primer plus 6 读书笔记 第十三章 类继承
c++primer plus 6 读书笔记 第十三章 类继承
|
3月前
|
C++ 容器
【C/C++笔记】迭代器
【C/C++笔记】迭代器
25 1
|
3月前
|
存储 安全 程序员
【C/C++笔记】迭代器范围
【C/C++笔记】迭代器范围
66 0
|
4月前
|
C++ Windows
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
在Windows上使用Visual Studio 2022进行FFmpeg和SDL2集成开发,首先安装FFmpeg至E:\msys64\usr\local\ffmpeg,然后新建C++控制台项目。在项目属性中,添加FFmpeg和SDL2的头文件及库文件目录。接着配置链接器的附加依赖项,包括多个FFmpeg及SDL2的lib文件。在代码中引入FFmpeg的`av_log`函数输出"Hello World",编译并运行,若看到"Hello World",即表示集成成功。详细步骤可参考《FFmpeg开发实战:从零基础到短视频上线》。
157 0
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
|
5月前
|
C++
c++primer plus 6 读书笔记 第十四章 C++中的代码重用
c++primer plus 6 读书笔记 第十四章 C++中的代码重用
|
5月前
|
SQL 人工智能 算法
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
|
5月前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
|
5月前
|
存储 小程序 程序员
Essential C++ 第1章 C++编程基础 (笔记)
Essential C++ 第1章 C++编程基础 (笔记)
|
5月前
|
算法 C语言 C++
面向考试编程C++笔记
面向考试编程C++笔记