认识C++指针

简介: 认识C++指针

前言:


 期待已久的指针篇来啦,这篇全都是有关指针的知识,喜欢指针的读者可以大饱眼福^ ^。关于指针的声明和初始化等部分,我在共用、结构里讲过了,需要的读者可以点进去看一下再返回来。


 链接:《共、枚、指1》


1.指针未初始化的危险性

 我们知道指针是存放地址的变量。当我们创建了一个指针的时候,但并没有初始化,在后面我们还对这个未初始化的指针解引用了,就会对内存产生破坏,导致程序挂掉。



  创建指针变量的时候,计算机不会为指针指向的内存单元编号开辟空间。



 当我们用p指针里的地址去找到相应的内存空间并对它进行操作,可不敢这样做,那样是犯法的。故事性理解:就好像你知道了一个旅馆的一个房间地址,但你知道并不代表你拥有这个房间,你来到这个旅馆,没跟馆主说,你径直的冲到这个房间门口,强行打开房间门,里面有小明躺在床上睡觉,你一脚把它踹开,自己躺在床上睡觉,还赖皮的不走了,馆主表示无奈,并报了警。


2.指针与十六进制数字

 我们知道,地址是用十六进制表示的,但数字的十六进制和地址的十六进制的意思是截然不同的,虽然计算机通常把地址当做整数来处理。但我们知道数字是描述数值的,指针描述的是位置。



 计算机把地址当做整数来处理的意思是:指针变量存储的值(也就是地址),就是在按照存储整型的方式存到内存中的,取出来的时候也是按整型取的。


 但我们要记得的是:十六进制数值,和十六进制地址不是一回事就OK。


3.使用new分配内存空间

 使用new运算符在运行阶段开辟堆区空间,开辟的空间大小根据我们给new后面加的数据类型,而且我们需要使用同类型的指针来接收new返回来的地址。

例子:

 指针的大小是固定不变的,不管指针指向什么类型的地址,指针的大小只与平台位数有关。是32位平台就是4字节,64位平台就是8字节。


 这里面pn、pd是用来接收new开辟出来的内存空间的地址,这块内存空间没有名称,只能用指针去访问。而普通变量,内存空间有名称。比如int a = 10;&a是a变量的内存地址,用a这个名称来标记这块空间,就可以直接用a来访问里面的值。


4.使用delete释放内存

 在用new申请内存的时候,当我们不需要用了的时候,必须将这块申请的内存交还给操作系统,否则内存将会越来越少,直到没有多余的内存空间让程序运行起来。

 在C++中,我们使用delete来释放使用new开辟的空间,格式是:delete 指针;(接收new开辟空间返回地址的那个指针),本质上是释放掉new开辟的空间,因为这个接收的指针就是new开辟的地址,所以这么写也是没问题的。


 特别注意的是new和delete是要配套使用的。


delete使用注意事项

delete只能用来释放用new开辟的空间

不能对同一块new开辟的空间释放两次

对空指针使用delete是没事的,相当于delete什么都没做


 我们这里是用delete释放pd,pd指向的那块地址也是new开辟的,因为ps将值赋给了pd,其实delete释放ps也可以,但不要都释放,不然就对同一块new空间释放两次了。


5.使用new来创建动态数组

 静态联编:通过声明创建数组,则在程序被编译时将为它分配内存空间,不管程序最终使不使用数组,它都存在,并占用空间。在编译时给数组分配内存被称为静态离联编。


 而在运行阶段需要数组,则创建数组,不需要就不创建。还能在程序运行时确定数组的长度。这被称为动态联编。这种数组叫做动态数组。  


 使用静态联编,必须在编写程序时指定数组的长度。使用动态联编时,程序可以在运行时确定数组的长度。

  当我们使用new时带上方括号以及数目的时候,也就是在跟new传达开辟数组空间的信号,相应的释放掉数组空间也要给delete加上方括号。用new没有用方括号,delete也不用带。


 补充:对于ANSIC和ISO标准来说,new和delete的格式不匹配的结果是未知的。


总之就是:使用new来创建动态数组的时候,要用对应的格式和new匹配起来,需要补充的一点是这种情况new int[1];,new[]只为一个实体分配内存,用不带[]的delete来释放。看下面代码:

 pd是指向一个double(数组第一个元素)的指针,我们需要负责把数组的元素确定好,因为编译器不能对pd是指向10个double元素中的第一个进行确定。什么意思呢?也就是说当我们忽略数组的元素个数的时候,编译器不知道你这个首元素地址是10个元素大的数组的首元素地址,还是20个元素大的数组的首元素地址,所以不能省略数组元素的个数。


 知道动态数组怎么创建和怎么释放后,我们来讨论如何使用动态数组。


6.使用动态数组

 数组表示法:将指针当做数组名使用即可。



 数组名是一般情况下是首元素地址,数组名的值是不能变的。而指针是变量,可以通过加减来改变所指向的地址。例如parr = parr + 1;,使得parr原本指向第一个元素变成了指向第二个元素。



 parr[0]就是第一个元素。parr[0]是第二个元素是因为parr+1了,指向了第二个元素。把指针当做数组名来使用,并不是就不能用指针的方式来访问数组了,我们知道parr是数组第一个元素的地址,用解引用可以访问到该地址的值,所以*parr和parr[0]是等价的。我们来讲完指针的运算,就会更清晰了。


7.指针运算

 在C和C++中数组和指针基本是等价的。等价的原因不只是因为C和C++内部都使用指针来处理数组,也在于指针算术。


 将一个整数加1,其值将增加1,但指针增加1,它的值增加的大小取决于指针的类型。



 i的值增加1,这我们都理解。指针的值从E8变到EC(十六进制 C是12),增加了4,恰好是一个整型的大小,所以指针的加减运算是让指针变量跳过n个指针类型,值也相应加减n个指针类型的大小。例子:double df = 3.14;double* pd = &df;pd+1;pd的值将增加8。


 加油加油,再看一个就结束啦!坚持。

 在这里面,我们将wages(数组名)给double类型的指针初始化,可行的原因正是因为数组名是首元素的地址,数组的元素是double类型的,double类型的地址给double指针初始化那再合理不过了。


 pd在加1前,打印的值是A8,加一后是B0,增加了8。因为pd跳过了一个double类型(占8个字节)。有些读者可能就要问了,为什么让值增加8呢?其实是因为,每个地址编号对应的内存单元占一个字节。第一个元素占8个字节也就是8个编号,而指针指向头个内存单元。A8是第一个元素的头个字节,还有A9,AA一直到AF,算上A8是8个字节。然后指针加一指向下一个元素,而数组又是连续存储元素的,紧接着指针就指向了B0,这就是为什么会加8的原因。


 来到20、21行,这里就是指针访问和数组表示的关系。我们已经知道数组是首元素地址了,stacks[0]就相当于*(stacks+0),stacks加0还是首地址,解引用就是首个元素。


 也就是假设:在这里我们用指针ps来接收stacks,就可以用这些操作,*ps就是首元素,*(ps+1)是指针指向首元素的下一个元素后解引用得到第二个元素,ps[0]是*(ps+0),ps[1]是*(ps+1)。


 数组名的意思:在最后sizeof(wages),wages不是数组首元素地址的意思,而是整个数组的意思。wages每个元素8字节,数组有三个元素,就是24字节。


数组名

&arr

整个数组的地址

arr

数组首元素地址

sizeof(arr)

整个数组的大小

 一般情况下数组名都是首元素地址,有且仅有两种情况是值整个数组,单纯&arr和单纯sizeof(arr),加单纯的意思就是说只能是这种形式下的数组名才指整个数组。sizeof(arr+1),这里的arr就是一般情况了。


 到这里指针的大部分基本知识就讲完啦,还有指针与字符串、指针与类型的组合等,我们下节再讲。最重要的是对使用要熟练使用指针解决问题。


 希望读者读完有所收获,如果本篇博客有内容上的错误或排版、内容分布不合理,请评论跟博主讲!


 求点赞,求点赞,求点赞!你的点赞是我更新的动力^ ^。

相关文章
|
1天前
|
存储 安全 编译器
C++进阶之路:探索访问限定符、封装与this指针的奥秘(类与对象_上篇)
C++进阶之路:探索访问限定符、封装与this指针的奥秘(类与对象_上篇)
4 0
|
1天前
|
存储 安全 编译器
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
7 2
|
4天前
|
C++ 存储 Java
C++ 引用和指针:内存地址、创建方法及应用解析
'markdown'C++ 中的引用是现有变量的别名,用 `&` 创建。例如:`string &meal = food;`。指针通过 `&` 获取变量内存地址,用 `*` 创建。指针变量存储地址,如 `string *ptr = &food;`。引用不可为空且不可变,指针可为空且可变,适用于动态内存和复杂数据结构。两者在函数参数传递和效率提升方面各有优势。 ```
|
6天前
|
存储 设计模式 安全
C++中的函数指针技术详解
C++中的函数指针技术详解
12 0
|
6天前
|
C++
在C和C++中,指针的算术操作
在C和C++中,指针的算术操作
|
8天前
|
安全 Linux 编译器
从C语言到C++_36(智能指针RAII)auto_ptr+unique_ptr+shared_ptr+weak_ptr(下)
从C语言到C++_36(智能指针RAII)auto_ptr+unique_ptr+shared_ptr+weak_ptr
20 3
|
8天前
|
安全 编译器 C语言
从C语言到C++_36(智能指针RAII)auto_ptr+unique_ptr+shared_ptr+weak_ptr(中)
从C语言到C++_36(智能指针RAII)auto_ptr+unique_ptr+shared_ptr+weak_ptr
15 1
|
8天前
|
安全 编译器 C语言
从C语言到C++_36(智能指针RAII)auto_ptr+unique_ptr+shared_ptr+weak_ptr(上)
从C语言到C++_36(智能指针RAII)auto_ptr+unique_ptr+shared_ptr+weak_ptr
15 0
|
9天前
|
C语言 容器
从C语言到C++_17(list的模拟实现)list不是原生指针的迭代器(下 )
从C语言到C++_17(list的模拟实现)list不是原生指针的迭代器
5 1
|
9天前
|
C语言 计算机视觉
从C语言到C++_17(list的模拟实现)list不是原生指针的迭代器(中)
从C语言到C++_17(list的模拟实现)list不是原生指针的迭代器
11 1