IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++ primer plus学习笔记(2.5)——数据类型(指针部分) -> 正文阅读

[C++知识库]c++ primer plus学习笔记(2.5)——数据类型(指针部分)

本来准备在第二章讲完数据类型的,后来发现实在太多了。虽然c++使用指针的程度不如c,但是我还是想把“c语言的灵魂”详细地讲讲。

目录

1 指针是什么?

1.1 取地址运算符 &

?1.2?解引用运算符 *

2 进一步理解指针?

2.1 硬币模型?(形象)

2.2 内存空间模型(本质)

3 使用指针

3.1 声明语法

3.2 空指针

3.3?手动管理内存:new 和 delete

3.4 只读指针,const指针

4 数组和指针

4.1 数组名的意义?

4.2 指针算术

4.3 数组表示法与指针表示法

5 二维数组、二维指针



回忆之前说的,一个变量包括三个基本属性:存储的信息的类型、内容、存储位置(地址)。信息的类型、内容分别在声明、赋值语句处标明。而信息的存储位置似乎没什么用。这里引出一个问题,:我们为什么需要知道变量的存储位置?知道后有什么用处?读者可以带着这个问题阅读本章。

1 指针是什么?

指针是一种特殊的数据类型,要与已有的数据类型组合才有意义;其值存储该种(与其组合的)数据类型的地址。int型指针便可存储某个int型变量的地址。声明格式如下:

type * name;? ? ? ? //在type后加上 * ,以表明这是一个这种类型的指针

1.1 取地址运算符 &

声明任意一种变量后,我们便可在变量左侧使用 & 来获得该变量的地址,如:

    int a = 10;
    int *p = &a;          //p是int型的指针,初始化为变量a的地址
    cout << p;            //输出a的地址

?1.2?解引用运算符 *

?声明任意一种指针后,我们便可在指针左侧使用 *?来获得该地址上的值,如:

    int a = 10;
    int *p = &a;          //p是int型的指针,初始化为变量a的地址
    cout << *p;           //输出a的值,10

2 进一步理解指针?

2.1 硬币模型?(形象)

    int a = 10;
    int *p = &a;          

如上述代码,有以下等价关系:

a == *p;

&a == p;

好比一枚硬币,其正面是值,背面是地址。对硬币正面使用 & 运算符,相当于使用硬币背面;对硬币背面使用 * 运算符,相当于使用硬币正面。换而言之,& 和 * 是将硬币翻面的运算。

2.2 内存空间模型(本质)

?声明语句构建了部分内存和变量名称之间的所属关系,int a 即表明分配了4字节的内存,可以使用变量名a进行追踪。而指针的值是1000,这表明将直接使用内存地址进行追踪,而不是使用变量名a。

3 使用指针

3.1 声明语法

已知同类型变量可在同一行声明,但指针略有不同,如:

int *a, b;    //a是int指针,b是int

上述代码告诉我们声明时,要在每个指针前面写上 * 。也可理解为对该变量解引用后才是该种数据类型(意即该变量是指针)。

3.2 空指针

未初始化的指针指向的位置是未知的。使用未初始化的指针就是使用未知内存,可能会导致系统崩溃,十分危险。如果确实在声明时没有初始化的目标,c++提供了空指针nullptr(c风格是NULL)以供解决风险。

nullptr:本质上就是0,对指向nullptr的指针解引用会直接终止程序,如:

    int *p = nullptr; //或 int *p = NULL;
    cout << p;        //输出0
    cout << *p;       //程序终止
    cout << "here";   //不执行

3.3?手动管理内存:new 和 delete

直接声明的变后,c++将自动管理相应的内存。但是,我们也可通过new和delete运算符来手动管理内存。new 运算符将在堆内存(详见内存空间章节)中分配相应大小的内存,并返回该内存首地址的指针。delete将指针指向的堆内存释放。声明语法如下:

type *point_name = new type;

delete?point_name;

    int *p = new int;
    *p = 10;
    cout << *p;
    delete p;            //一定要记得释放

同时,也能使用new和delete声明数组:

type *point_name = new type [size];

delete [] point_name;? ? ? ? //注意这个方括号

    int *p = new int[10];
    p[1] = 10;
    cout << p[1];
    delete[] p;

?由此可见,可以直接将指针名视为数组名(因为在c++内部数组与指针基本等价,详见后文)。

使用new和delete有以下规则:

  • delete只能释放堆内存,即不使用delete释放不是new分配的内存
  • delete只能释放一次(多次释放将报错)
  • 使用new[],则用delete[];使用new,则用delete
  • delete 指向nullptr的指针不会有问题

3.4 只读指针,const指针

?注意指针的声明:type?* name,name前共有三个位置可以放const,区别如下:

    const int *a = new int;
    int const *b = new int;
    int *const c = new int;
    *a = 10;    //报错
    *b = 10;    //报错
    *c = 10;
    delete a;   //记得释放
    delete b;
    delete c;

const的位置只有在 * 前后的区别,

* 前表示指向的内容不可被修改, * 后表示指向的地址不可被修改。

前者(例中的a,b)可以指向多个地址,但是不能修改它们,是只读指针

后者(例中的c)只能指向初始化时安排的地址,即这个指针的值不能改变,是const指针

ps:实际上,我们经常把只读指针说成const指针,因为const指针用处不大。

4 数组和指针

4.1 数组名的意义?

?你认为下述代码会输出什么??

    int a[10] = {1, 2, 3};
    cout << a;

回忆数组的定义——在内存空间中连续放置的多个变量的整体,这个整体就是这一段连续内存的首地址。所以前文使用new分配的数组能使用int指针指向,且指针名和普通声明的数组名一样。

    int *p = new int[10];
    p[0] = 10;
    cout << p << '\n';
    cout << *p << '\n';    //输出10
    int a[10] = {20};
    cout << a << '\n';
    cout << *a << '\n';    //输出20

数组的首地址,便是第一个元素的地址,对数组名解引用便能得其值。

4.2 指针算术

指针也是变量,也有相应的运算符,p代表指针,n代表常数,具体如下:

*p 获得指针指向的值

&p 获得指针的地址

p + n?获得向后偏移n个单位(根据p指向的类型)的地址

p?- n?获得向前偏移n个单位(根据p指向的类型)的地址

p1 - p2 获得两个指针在内存空间中的距离

p++ 等同于p = p + 1? ? ? ? //p--,--p,++p同理

#include <iostream>
#include <iomanip>
using namespace std;
#define show(x) cout << #x ":\r\t" << setw(2) << x << endl;

int main()
{
    int *a = new int, *b = new int, x, *c = &x;
    show(a);
    show(b);
    show(c);
    show(a + 1); //int是4字节,所以增加4(1个4字节)
    show(a - 2); //减少8(2个4字节)
    show(b - a); //堆内存之间的变量离得很近
    show(a - b);
    show(a - c); //栈内存离堆内存很远
    delete a;
    delete b;
}

我的电脑上的输出:

a: ? ? ?0xe91600
b: ? ? ?0xe91620
c: ? ? ?0x61fdf4
a + 1: ?0xe91604
a - 2: ?0xe915f8
b - a: ? 8
a - b: ?-8
a - c: ?2213379

4.3 数组表示法与指针表示法

根据指针算术,这两句等价:?

a[i];

*(a + i);? ? ? ? // *a 等同于?*(a+0)?

因此,很多情况下数组和指针是有共性的,?你甚至可以想象:

int a[10];
int * const a = new int[10];

这两者在使用上是等价的,区别是内存管理手段不同。?

而区别之一是:

  • 数组名是const指针
  • 对数组名使用 sizeof 运算符,得到整个数组大小;指针则是自身长度
  • 对数组名使用 &,得到指向数组的指针;指针则是升维

请注意下述代码:?

#include <iostream>
#include <iomanip>
using namespace std;
#define show(x) cout << #x ":\r\t\t" << setw(2) << x << endl;

int main()
{
    short a[10] = {22}, *p = a;
    show(sizeof(&p)); //输出8,short二维指针的大小
    show(sizeof(p));  //输出8,short指针的大小
    show(sizeof(*p)); //输出2,short的大小

    show(sizeof(&a)); //输出8,指针的大小,指向short数组
    show(sizeof(a));  //输出20,short数组的大小,指向第一个short
    show(sizeof(*a)); //输出2,第一个元素的大小

    show(a);
    show(&a + 1); //输出+20,单位是数组长
    show(a + 1);  //输出+2,单位是short
    show(*a + 1); //输出23,第一个元素增加
}

参考输出:
sizeof(&p): ? ? ?8
sizeof(p): ? ? ? 8
sizeof(*p): ? ? ?2
sizeof(&a): ? ? ?8
sizeof(a): ? ? ?20
sizeof(*a): ? ? ?2
a: ? ? ? ? ? ? ?0x61fdf0
&a + 1: ? ? ? ? 0x61fe04
a + 1: ? ? ? ? ?0x61fdf2
*a + 1: ? ? ? ? 23

再次可以看到,

  • 数组名就是指向第一个元素的指针;
  • 取地址是指向本数组的指针;
  • 解引用是第一个元素。
  • 指针加法的单位就是指针指向的大小。

声明一个5维指针z保证赋值出错,看vscode编辑器的报错可以帮助大家理解:

?

5 多维数组、多维指针

本节十分复杂,无意深入者可略过。

5.1 多维指针

首先,维数意味着什么?指针可以指向指针,把基本的数据类型定义为0维,每一级指针便增加一级维数。如:

int a0;    //0维
int *a1;   //1维指针
int **a2;  //2维指针
int ***a3; //3维指针

?使用时,每用一个 & ,维数上升1;每用一个 * ,维数下降1。维数降至0时,便是原数据。如:

?

5.2 * [] 组合

?分析下列代码:

    int a[10][5];    //a是个二维数组,10行5列
    int *b[10];      //b是个指针数组,包含10个int*
    int **c;         //c是个指针,指向int*
    int(*d)[10];     //d是个指针,指向一个大小为10的int数组

ps:因为 [] 的 优先级比 * 高,所以产生了b和d的区别

?b的声明的重要意义之一是管理多个不等长的数组

?

5.3 二维数组

对于二维数组a,有以下等价表示:

a[y][x];

*(*(a + y) + x);

*(a[y] + x);? ? ? ? //不好的表达

有实用价值的语句是:

    int a[10][5]; //a是个二维数组,10行5列
    &a;           //int (*)[10][5]
    a;            //int (*)[5]
    a[0];         //int * ,指向第一行
    a[0][0];      //int,第一行第一列

?因为 * 放在操作数左边, [] 放在操作数右边,所以单一地使用其中一种不容易发生混淆。?值得注意的是c++检查指针更为严格,指向多维数组的指针和该数据的同维指针在c++中是不同的,不能相互赋值。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-25 11:28:13  更:2021-07-25 11:30:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 2:35:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码