C++ 中的指针
前言:
因为 C++ 所以指针就有了这篇文章。
一、指针的基本概念
指针在高级语言的编程中发挥着非常重要的作用,它能使得不同区域的代码可以轻易的共享内存数据。
指针使得一些复杂的链接性的数据结构的构建成为可能,有些操作必须使用指针,比如申请堆内存,还有在函数的调用的参数都是按值传递的,如果在函数中修改被传递的对象,就必须通过这个对象指针来完成。
指针就是内存地址,指针变量就是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因为数据类型不同,因此所占的存储空间长度也不同。使用指针不仅可以对数据本身,也可以对存储数据变量的地址进行操作。
简单理解:指针就是内存地址;可以利用指针来保存地址。
二、指针的引入(函数返回中return语句的局限性)
函数的缺陷:
一个函数只能返回一个值,就算我们在函数里面写多了return语句,但是只要执行任何一条return语句,整个函数调用就结束了。
数组:
数组可以帮助我们返回多个值,但是数组是相同数据类型的结合,对于不同数据类型则不能使用数组。
指针:
使用指针可以有效解决这个问题,使用指针我们想反回几个值就能返回几个值,想返回什么类型就可以返回什么类型的值。
简单理解:指针就是内存地址;可以利用指针来保存地址。
在程序设计过程中,存入数据还是取出数据都需要与内存单元打交道,计算机通过地址编码来表示内存单元。指针类型就是为了处理计算机地址数据的,计算机将内存划分为若干个存储空间大小的单元,每个单元大小就是一个字节,即计算机将内存换分为一个一个的字节,然后为每一个字节分配唯一的编码,这个编码即为这个字节的地址。指针就是用来表示这些地址的,即指针型数据不是什么字符型数据,而存的是我们内存中的地址编码。指针可以提高程序的效率,更重要的是能使一个函数访问另一个函数的局部变量,指针是两个函数进行数据交换必不可少的工具。
地址及指针的概念:
程序中的数据(变量,数组等)对象总是存放在内存中,在生命期内这些对象占据一定的内存空间,有确定的存储位置,实际上,每个内存单元都有一个地址,即以字节为单位连续编码。编译器将程序中的对象名转换成机器指令识别的地址,通过地址来存储对象值。
1 | int i; double f; |
计算机为 int 类型数据分配4个字节,为 double 类型分配8个字节。按对象名称存取对象的方式成为对象直接访问,如:i=100; f=3.14; 通过对象地址存取对象的方式成为指针间接访问
三、指针的定义和使用
1、指针变量定义语法:数据类型 * 变量名;
代码举例:
1 | int a = 18; |
运行结果:
1 | 0x7fff93715e1c |
2、指针的初始化,可以在定义指针时对它进行初始化:
1 | // 指针类型* 指针变量名 = 地址初值,...... |
3、指针运算
指针运算都是作用在连续存储空间上才有意义。
1 | (1)指针加减整数运算 |
通过代码举例,发现是不是跟变量有点相似?
指针变量和普通变量的区别:
- 普通变量存放的是数据,指针变量存放的是地址。
- 指针变量可以通过” * “操作符,操作指针变量指向的内存空间,这个过程称为解引用。
总结:
- 我们可以通过 & 符号 获取变量的地址。
- 利用指针可以记录地址。
- 对指针变量解引用,可以操作指针指向的内存数据。
四、指针所占内存空间
- 指针也是一种数据类型,那么这种数据类型占用多少内存空间呢?
- 在32位操作系统下:指针占4个字节空间大小,不管是什么数据类型;
- 在64位操作系统下:指针占8个字节空间大小,不管是什么数据类型。
代码演示:
1 | int main() |
解释:
因为内存是由字节组成的,每个字节都有一个地址编号。指针变量主要是存放相同数据类型的变量的首地址,这里的地址就是指内存中某个字节的编号,而这个编号是由地址总线决定的。
操作系统的位数决定了指针变量所占的字节数:
如果是32位操作系统,也就是地址总线是32位,则它的寻址范围就是0~232-1(4GB),所以每一个字节的编址就会由32个0或1组成。
例:第1个字节的编址是32个0,最后1个的编址是32个1。一个字节有8位,32位则需要4个字节。
如果是64位操作系统,也就是地址总线是64位,则它的寻址范围就是0~264-1(16GB),所以每一个字节的编址就会由64个0或1组成。
例:第1个字节的编址是64个0,最后1个的编址是64个1。一个字节有8位,64位则需要8个字节。
五、空指针和野指针
- 什么是空指针?空指针的指针变量指向内存中编号为0的空间,它的作用是用来初始化指针变量;空指针指向的内存是不可以访问的。
1 | int main() { |
- 什么是野指针?野指针就是指针变量指向非法的内存空间。
1 | int main() { |
空指针和野指针都不是我们申请的空间,因此不要访问。
如果程序中出现野指针的话有可能会导致程序的崩溃,所以我们在定制指针的时候还不知道指向什么的时候先让该指针指向空。
例如:
int *p = NULL;
六、const 修饰指针
const 修饰指针有三种情况:
- const 修饰指针——常量指针(常量指针只能修改指向,不能修改指向的值)
- const 修饰常量——指针常量(指针常量只能修改指向的值,不能修改指向)
- const 即修饰指针,又修饰常量(指向和指向的值都不能修改)
代码演示:
1 | int main() { |
小彩蛋:
1 | // 反骨操作: |
七、指针与数组
首先了解一下数组数组名的意义:
1 | // 例如: |
a 是数组名,a 的值是数组的首地址,例如 a 的值是0x00000001,那么a+1表示首地址加 int 大小的地址,即 0x00000005,对a进行解引用才是数组首个元素的值,a=a[0],(a+1)=a[1].
&a 是数组的指针,加一意味着数组首地址加上整个数组的长度(10个int型变量的长度)后的地址,即末尾元素的后一个元素的地址。
那既然有这个规律,我们就可以利用指针来快速的访问数组、遍历数组。
代码演示:
1 | int main() { |
八、指针与函数
在普通函数中只是值传递就会出现不可以修改函数实参的情况。
而利用指针作函数参数,可以修改实参的值,以为传入的是一个地址,可以通过改地址去修改值。
代码演示:
1 | void swap1(int a, int b) { |
C++ 中的指针更多操作正在完善中…..