C++之指针

指针是C++的一个核心概念,是程序员直接对内存进行操作的一种工具。这样的工具是一把双刃剑,一方面可以实现一些非常优化的程序,另一方面也会导致一些难以调试的错误。

一:使用指针遍历数组

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	int arr[5] = {0,1,2,3,4};
	int *ptr = arr;
	cout << "使用指针遍历数组: ";
	for (int i = 0; i < 5; ++i)
	{
		cout << *ptr << " ";
		ptr ++;
		//也可以直接写成 cout << *(ptr++) << " ";

	}
	cout << endl;
	
	return 0;
}

在这里插入图片描述

int *ptr = arr 该语句用数组名初始化指针。在这里数组名代表的是数组第一个元素的地址,之后在循环内程序会递增指针,以指向数组后面的几个元素。

二:指针的概念和理解

指针(Pointer),从英文字面上来理解就是一个指向某一物件的东西,在程序中就是指向数据的地址。计算机的内存可以看成一个机密排列的数据序列,每一小块数据(也就是字节)旁边都有一个编号代表数据的地址。
在这里插入图片描述
假设arr的地址是203,那么数组后面的几个元素的地址依次递增(这个例子中因为数组类型是int,所以真实的地址需要依次加4字节)。之前我们说过,指针实际上就是内存地址,所以arr的值就是203,而当ptr指向数组最后一个元素时,它的值是207。

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	int arr[5] = {0,1,2,3,4};
	int *ptr = arr;
	for (int i = 0; i < 5; ++i)
	{
		cout << *ptr << "指针地址" << ptr << endl;
		ptr ++;

	}
		return 0;
}

在这里插入图片描述
我们可以看到每个元素之间的间距正好是int的大小 - 4字节

三:指针的创建和初始化

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	
	float *floatPtr = NULL;
	string *strPtr;
	int *intPtr1, *intPtr2;
	int* intPtr3, intPtr4; //intPtr4只是一个整数

	cout <<  "float指针地址" << floatPtr << endl;
	cout <<  "string指针地址" << strPtr << endl;
	cout <<  "指针地址" << intPtr2 << endl;
	cout <<  "指针地址" << intPtr4 << endl;


	return 0;
}

注意:初学者很容易在声明多个指针的时候遗漏后面变量前的星号(),就像intPtr4一样,感觉像是定义了一个指针,其实只是一个整型。*
在这里插入图片描述

此外,示例中只有第一行的floatPtr初始化了,但是在实际编程中我们一定要初始化所有的指针,就和变量一样。floatPtr的初始值NULL是一个宏定义,它的实际数值是0,也就是地址0x00000000。一般我们把初始化为NULL的指针叫做空指针。因此我们只要检查指针是否为NULL就知道指针是否指向有效数据。

如果指针没有初始化,他可能指向一个未知的地址,那么我们在尝试读取数据的时候就有可能造成程序崩溃。此外,在初始化的时候,不能使用0以外的整型给指针赋值。
在这里插入图片描述

四:指针的基本操作

对于指针来说,解引用和取地址是重要的两个操作符;

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	int num = 4;
	int *intPtr = &num;
	cout << "初始num的值" << num << endl;
	cout << "num地址:" << &num << endl;
	cout << "指针的值是:" << intPtr << endl;
	if (intPtr) //检查指针是否为空
	{
		cout << "指针所指的数字是" << *intPtr << endl;
	}

	*intPtr = 5; //解引用操作符也可以用作为赋值语句的左值以修改数据

	cout << "修改后num的值" << num << endl;
	return 0;
}
  • "&"表示取地址操作符,他可以获得变量的内存地址。
  • "*"表示解引用操作符,他可以得到所知向地址的数据。

在这里插入图片描述

五:指针的算术操作

指针可以像整形那样进行一部分算术操作,还可以对地址进行修改。因为计算后的指针不一定会指向具有有效数据的地址,所以我们在进行指针的算术操作的时候需要格外小心。

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	int arr[5] = {0,10,20,30,40};
	int *ptr = arr;
	cout << "arr + 4: " << *(arr+4) << endl; 
	cout << "ptr + 4: " << *(ptr+4) << endl;
	cout << "ptr: " << ptr << endl;
	cout << "ptr + 2: " << ptr+2 << endl;
	cout << "++ptr: " << ++ptr << endl;
	cout << "ptr - 2: " << ptr-2 << endl;
	cout << "--ptr: " << --ptr << endl;


	return 0;
}

在这里插入图片描述
我们可以看到,指针与整型的算术操作不同于一般的数字加减,而是与指针的类型绑定的。由于一个int的大小是4字节,那么ptr+2会将地址加上8;在数组中就是指向第三个元素。

数组名其实可以看作指向第一个元素的指针。指针的各种操作都适用于数组名,但只有一点区别,那就是数组名不能重复赋值。这也是很容易理解的,因为数组是静态的,数组名当前作用域唯一的一个数组,不可能像指针那样指向其他地址。

指针除了与整型的算术操作之外,还可以进行指针相减。

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	int arr[5] = {0,10,20,30,40};
	int *ptr1 = arr + 1;
	int *ptr2 = arr + 3;

	cout << "ptr1:" << ptr1 << endl;
	cout << "ptr2:" << ptr2 << endl;

	cout << "ptr1 - ptr2 = " << ptr1 - ptr2 << endl;
	cout << "ptr2 - ptr1 = " << ptr2 - ptr1 << endl;

    

	return 0;
}

在这里插入图片描述

指针相减返回的是指针地址之间的距离,并且是分正负的。这个距离也与类型绑定,单位是该类型数据的个数。指针之间不存在加法,相加会报错。

六:const指针

指向const对象的指针 ( const int *Ptr)

  1. 不能修改解引用后的值。
  2. 可以修改指向的地址
  3. 可以指向普通变量。
#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	const int num = 3;
	//普通指针不能指向const变量
	// int *ptr1 = &num; 报错
	const int *ptr2 = &num;
	cout << "*ptr2:" << *ptr2 << endl;
	//指向const对象的指针不能修改解引用后的值
	// *ptr2 = 4;
	//指向const对象的指针可以修改指向的地址
	const int num1 = 4;
	ptr2 = &num1;
	cout << "*ptr2:" << *ptr2 << endl;
	//指向const对象的指针也可以指向普通变量
	int num2 =5;
	ptr2 = &num2;
	cout << "*ptr2:" << *ptr2 << endl;

	//同样也不能修改解引用的值
	// *ptr2 = 10;
	// cout << "num2:" << num2 << endl;


    

	return 0;
}

在这里插入图片描述
const指针 (int *const Ptr)

  1. 不能修改指向的地址
  2. 可以修改指向变量的值
  3. 只想const对象的const指针既不能修改地址,也不能修改值
#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	int num1 = 3;
	int num2 = 4;
	int *const ptr1 = &num1;
	//const指针不能修改指向地址
	ptr1 = &num;

	const int num3 =5;
	const int num4 =6;
	//指向const对象的指针既不能修改地址,也不能修改值;
	const int *const ptr2 = num3;
	ptr2 = num4;
	*ptr2 = 100;


    

	return 0;
}

上面代码报错不能执行。

七:指针的数组和数组的指针

指针作为一种变量类型,自然可以被声明成数组,而数组作为一种变量类型,也可以有指向他的指针。所以指针的数组是一种数组,而数组的指针则是一种指针。

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	int arr[5] = {0,1,2,3,4};
	//数组的指针
	int (*arrPtr)[5] = &arr;
	//指针的数组
	int *ptrArr[5] = {&arr[0],&arr[1],&arr[2],&arr[3],&arr[4]};
	cout << "arrPtr:" << arrPtr << endl;
	cout << "*arrPtr:" << *arrPtr << endl;
	for (int i = 0; i < 5; ++i)
	{
		cout << (*arrPtr)[i] << "  ";
		cout << ptrArr[i] << "  ";
		cout << *(ptrArr[i]) << "  ";
		cout << endl;
	}
    

	return 0;
}

在这里插入图片描述
数组的指针和指针的数组的语法区别在于:数组的指针需要在星号和变量名外加一个括号,而指针的数组却没有。这一点其实很好理解,因为声明数组的时候元素类型名int和数组大小[5]是被变量名隔开的,在这里我们添加一个星号,并且用括号括起来,表示这个指针int(arrPtr)[5]是指向整个数组的;如果不加括号,编译器就只会将星号联系到前面的类型名int所以ptrArr就只是生命了一个数组,数组类型是int

八:指针的指针

指针可以指向任何变量或对象,所以也可以指向指针。

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
	
	int num = 3;
	int *numPtr  = &num;
	int **numPtrPtr = &numPtr;

	cout << "num: " << num << endl;
	cout << "numPtr: " << numPtr << endl;
	cout << "*numPtr: " << *numPtr << endl;
	cout << "numPtrPtr: " << numPtrPtr << endl;
	cout << "*numPtrPtr: " << *numPtrPtr << endl;
	cout << "**numPtrPtr: " << **numPtrPtr << endl;

 
	return 0;
}

在这里插入图片描述
我们可以看到, 指针的指针的声明就是多加了一个星号,以表示指针指向的是指针类型,因此我们将numPtr的地址赋值给它。可以由以下图解解释
在这里插入图片描述
这样看来指针的指针就和普通的指针没有什么区别。不过一般情况下,我们也不使用这种不直观的类型,指针的指针一般用于函数传参数时修改传入的指针。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈游戏开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值