C++—14、C++ 中的指针最基础的原理
这里的指针是指原始指针,并非智能指针。计算机跟内存打交道,内存是计算机的一切,编程中最重要的东西可能就是内存。当你编写一个应用程序并启动它时,整个程序被载入内存,在你写的代码中,所有指令告诉计算机要做什么,所有这些被加载到内存中,这就是cpu如何访问你的程序并执行指令的。当你创建一个变量时,当你从硬盘中载入数据时,所有东西都会被存储到内存中,如果没有内存,你将什么也做不了。而指针对管理和操纵内存很重要。所以什么是指针?
一、指针的定义
指针是一个无符号整数(unsigned int),其值表示一个内存地址(其存储一个内存地址)。
因为我们代码所做的每一件事都是从内存中读取或者写入内存。内存可能是你拥有的至关重要的东西,你的电脑所能提供的重要的资源,所有的东西都依赖它,所以能够对这些内存有更多的控制至关重要。指针的类型与指针的值没有任何关系,类型只是我们虚构出来的,好让我们感觉轻松些,别管你有什么类型的指针,里面存放的都是个整数,都是个地址。
二、实例应用
新建项目,里面只有一个main.cpp文件。
这里新建个指针,void类型的,把变量var的地址存里面了。
再次强调,指针是没有什么类型的,里面存储的只是一个地址,一个整数,如果要是说有类型,只是指它所存储的地址所指的里面是存放什么类型的数据而已。
指针如果是空的话,我们可以这样写:
void*ptr=nullptr;
我们如上图所示F9来设置断点,按F5来调试程序。
当我们把鼠标悬停在ptr上时,显示一个地址0X0034FA58.我们把内存视图调出来
我们输入这个地址来看一看,这个地址中存放的是什么?
发现这个地址中正好存放的是8,变量var的值。
从这个实例中我们更加理解了,指针是个变量,是个存储地址的变量。
三、*指针
假设我想读取我的数据,我有指向那个数据的指针,现在我想写入或者读取这块数据,或者我想更改他,任何更改呢?
这时候逆向引用,就派上用场了,可以通过在指针前面加上个星号来实现。
*ptr,我实际上在逆向引用那个指针,也就是我现在在访问那块数据,我可以读取或者写入那块数据。例如我将值10写入。
如果这样做可能会产生错误,因为这里的类型指的是指针所指的地址的数据类型,为void。
测试下:
计算机如何从能把值写道一个void类型上去?这里的10,有可能是短整型2个字节,int是4个字节,long int是8个字节,它不知道该写入多少个字节的数据。其实这时应该用到类型了。程序修改如下:
我们来告诉编译器他就是个int类型,4个字节。记住是我们告诉编译器的,并不是编译器自己思考的。
我们来把这个值输出一下看看对不?
可以看到值已经被修改。
我们可以通过调试来观察内存的变化。
通过上面实例我们可以看出,通过星指针就是在访问那块数据,就可以写入那块内存。
现在对于指针的工作,大家应该很清楚了吧!
总结一下:指针就是指向内存中的一个位置。有些人说它指向一块内存,虽然不是很准确,因为我们不知道那块内存有多大。在上面这种情况下,我们知道,因为是int类型,是4个字节。
在实际的指针中并没有说内存有多大,当我们创建数组的时候,它有记录大小。确切的说它没有保存数据,我们不应该知道它有多大。
指针只是一个整数,也就是一个内存地址。
四、堆上内存分配
char是一个字节,这里我问他要的是8个字节的内存,并返回了指向这个内存开始地址的指针。然后我们可以使用memset的函数来填充这块内存,函数参数为指向这块地址开始地址的指针,然后是值,然后是大小就是需要填充多少字节。
调试运行
输入查询内存,发现
由于这里使用了new关键字,所以该数据是分配在heap上的
五、堆和栈
Heap 和 Stack 是计算机科学中两个基本的概念,它们在内存管理和数据结构方面扮演着重要的角色。以下是关于 Heap 和 Stack 的详细解释和比较。
堆(Heap)
定义:堆是一种内存区域,通常由程序员手动分配和释放内存。如果程序员不释放内存,程序结束时操作系统可能会回收这部分内存4。
特点:
- 手动管理:程序员需要显式地使用
malloc
或new
等函数来分配内存,使用free
或delete
来释放内存21。 - 动态大小:堆的大小在程序运行期间可以动态变化,没有固定的大小限制,但受到系统可用内存的限制2。
- 内存碎片:频繁的内存分配和释放可能导致内存碎片,影响性能2。
- 生长方向:堆的增长方向通常是向上的,即向内存地址增加的方向扩展2。
栈(Stack)
定义:栈是一种内存区域,由编译器自动分配和释放内存,通常用于存储函数的局部变量和参数4。
特点:
- 自动管理:编译器自动管理栈内存的分配和释放,不需要程序员显式地进行内存管理21。
- 固定大小:栈的大小在程序开始时就已经确定,通常较小,受限于系统资源2。
- 无内存碎片:由于栈采用先进后出(LIFO)的结构,不会产生内存碎片问题2。
- 生长方向:栈的增长方向通常是向下的,即向内存地址减小的方向扩展2。
主要区别
特性 | Heap(堆) | Stack(栈) |
---|---|---|
内存管理 | 手动分配和释放 | 自动分配和释放 |
内存大小 | 动态大小,较大但有限 | 固定大小,相对较小 |
内存碎片 | 可能产生内存碎片 | 不会产生内存碎片 |
生长方向 | 向上,内存地址增加 | 向下,内存地址减小 |
分配效率 | 较低,涉及复杂算法 | 较高,由硬件直接支持 |
C++ 中,声明一个对象并在堆上分配内存的示例如下:
bject* p = new Object();
而在栈上分配内存的示例如下
Object obj;
六、指针的指针
如下:
注意这里说了需要手动释放,所以应该为:
设置断点在第8行,我们来调试一下:
我们在ptr的值输入内存:
这个值006fe648正好是buff的值,
而buff变量的值作为地址,里面放的是0.也就是这个指针变量放的是另一个指针的地址,另一个指针的地址指向一个内存空间的开始地址。