写在前面:
如果文章对你有帮助,记得点赞关注加收藏一波,利于以后需要的时候复习,多谢支持!
【C++语言基础学习】系列文章
第一章 《项目与程序结构》
第二章 《数据类型》
第三章 《运算符》
第四章 《流程控制》
第五章 《Vector向量》
第六章 《String字符串》
第七章 《Array数组》
第八章 《函数》
第九章 《指针》
第十章 《结构体》
文章目录
- 【C++语言基础学习】系列文章
- 一、指针定义
- 二、特殊指针
- (一)空指针
- (二)野指针
- (三)const修饰指针
- 三、指针与数组
- 四、指针与函数
一、指针定义
指针(Pointer)是C++中的一种数据类型,用于存储变量的内存地址。它提供了直接访问内存位置的功能,并可以用于实现高效的数据结构和操作。
指针变量存储着其他变量的内存地址,可以通过解引用操作符*
来访问指向的内存地址上存储的值。解引用操作符用于返回指针指向位置的值。
在C++中,可以使用以下语法来定义指针变量。
数据类型* 指针变量名;
以下是简单的示例。
// 定义变量
int a = 10;
// 定义指针
int* p;
// 让指针记录变量a的地址
p = &a;
cout << "a的值为:" << a << endl;
cout << "a的地址为:" << &a << endl;
cout << "指针p:" << p << endl;
// 使用指针
*p = 1000;
cout << "通过指针更改后a的值为:" << a << endl;
cout << "解引用后的*p:" << *p << endl;
返回结果如下。
a的值为:10
a的地址为:0000000B8CB6FA64
指针p:0000000B8CB6FA64
通过指针更改后a的值为:1000
解引用后的*p:1000
从示例可以看到,定义了一个变量后,可以通过定义指针的方式找到变量的储存空间(地址),表现在取址后的变量&a
和指针p
直接储存的信息相同;而通过解引用的方式可以找到指针指向的内存中的数据,从而可以更改其数据,这里就是通过解引用指针*p
,向此数据重新赋值了1000的值,再次输出可以发现解引用指针*p
与原本的变量a
都随之改变。
其中,指向变量a
的指针定义方式也可以简化为如下所示。
int* p = &a;
而指针既然也属于一种数据类型,同样也会占用一定的内存空间。一般情况下,指针变量的大小为机器的字长(即指针所占的位数),通常为4个字节(32位系统)或8个字节(64位系统)。指针变量保存的是一个内存地址,这个地址指向存储在内存中的某个变量或对象。因此,即使指针变量本身只占用很小的空间,它所引用的对象可能会占用更多的空间。
cout << "sizeof(int*) = " << sizeof(int*) << endl;
cout << "sizeof(float*) = " << sizeof(float*) << endl;
cout << "sizeof(double*) = " << sizeof(double*) << endl;
cout << "sizeof(char*) = " << sizeof(char*) << endl;
使用x86调试器(32位)返回结果如下。
sizeof(int*) = 4
sizeof(float*) = 4
sizeof(double*) = 4
sizeof(char*) = 4
使用x64调试器(64位)返回结果如下。
sizeof(int*) = 8
sizeof(float*) = 8
sizeof(double*) = 8
sizeof(char*) = 8
可以看到,无论是32位还是64位,各种数据类型的指针占用的字节数都一样,这说明无论指向什么数据类型,其储存地址的指针所占用的内存都是相同的,不会因为指向的数据类型本身的数据大小而改变。
二、特殊指针
除了指向各种数据类型的普通指针,在C++中同样还有着特殊的指针类型。
(一)空指针
空指针(Null Pointer)是指没有指向任何有效对象或者函数的指针,也就是指向内存中编号为0的空间(0~255是系统储存的空间,是无法访问的),其用途主要是初始化指针变量。在C++中,可以用常量值0
或者特殊关键字nullptr
表示空指针。
需要注意的是,空指针指向的内存是不可以访问的。
int* ptr = nullptr; // 初始化为空指针
当定义一个指针变量时,如果没有为其赋予初始值,可以将其初始化为空指针,这避免了指针悬空的风险。
(二)野指针
野指针(Dangling Pointer)是指指针仍然保留在内存中,但指向的内存已经被释放或者无效的情况。使用野指针是一种常见的编程错误,可能导致程序崩溃、错误的读写内存数据或产生不可预测的行为。
int* p = (int*)0x1100;
例如以上的代码,使用(int*)
可以手动将一段地址0x1100
输入到指针内,但由于这样的地址并没有通过系统的申请,以至于虽然语法正确,编译器仍然无法为这样一个非法地址分配空间,进而导致系统报错。
(三)const修饰指针
const修饰指针有三种情况。
- const修饰指针——常量指针
- const修饰常量——指针常量
- const既修饰指针,又修饰常量
对于第一种情况的常量指针,其特点是指针的指向可以修改,但是指针指向的值不可以修改。
以下是简单的示例。
int a = 10;
int b = 10;
const int* p = &a;
*p = 20;// 错误,指针指向的值从10到了20,值发生了改变
p = &b;// 正确,指针的指向从a转移到了b,但值为10没有发生改变
从代码可以看到,此示例const
关键字后是int*
这个确定的内存空间,即指针指向的常量。
int a = 10;
int b = 10;
int* const p = &a;
*p = 20;// 正确,指针指向的值从10到了20,但指向a没有发生改变
p = &b;// 错误,指针的指向从a转移到了b,指向发生了改变
而对于第二种情况,此示例const
关键字后是p = &a
这个指向确定的指针,即指针本身。
int a = 10;
int b = 10;
const int* const p = &a;
*p = 20;// 错误,指针指向的值从10到了20,值发生了改变
p = &b;// 错误,指针的指向从a转移到了b,指向发生了改变
综合以上两个案例即为第三种情况:此示例的两个const
关键字既有int*
这个确定的内存空间,又有p = &a
这个指向确定的指针,所以此时指针的指向和值都被锁定,无法发生改变。
三、指针与数组
在C++中,同样可以利用指针来访问数组。
int arr[10] = { 1,2,3,4,5,6,7,8,9 };
cout << "第一个元素为:" << arr[0] << endl;
int* p = arr;
cout << "利用指针访问第一个元素:" << *p << endl;
p++;
cout << "利用指针访问第二个元素:" << *p << endl;
返回结果如下。
第一个元素为:1
利用指针访问第一个元素:1
利用指针访问第二个元素:2
以上示例代码中,首先定义了一个数组,由于本系列第七章已经证明数组本身的首地址既是第一行元素的首地址也是第一个元素的首地址,所以指针p
直接指向数组arr
本身就可以直接得到第一个元素的首地址;而p++
则将首地址增加了4个字节,由此得到第二个元素的首地址,输出解引用的指针即可访问相应元素。
四、指针与函数
在本系列第八章中已经说明,函数的值传递只会令函数形参发生改变却不会改变实参。然而,通过指针的地址传递,函数的实参同样可以被改变。
#include <iostream>
using namespace std;
void swap(int* p1, int* p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
int a = 10;
int b = 20;
swap(&a, &b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
返回结果如下。
a = 20
b = 10
在此次定义的swap()
函数中,定义的函数内部通过解引用指针的方式重新给内存数据赋值,使得最后的结果表现为作为实参的变量a
和b
的值也发生了改变,这就是地址传递。
我是EC,一个永远在学习中的探索者,关注我,让我们一起进步!