当前位置: 首页 > news >正文

C++ 复杂的数据类型(指针)

计算机的内存地址和指针的关系

指针(pointer),就是一个存储计算机内存地址的一个变量,通过这个变量,可以很方便的操作内存中的值,创建变量时,系统将分配一些内存块用保存他们的值,为了随时能找到这些内存块,每个内存块拥有一个独一无二的地址(即寻址索引),且变量的地址可以用&取地址符取得,取得的16进制的地址信息便是变量指针,利用指针我们便可以对变量进行快速的访问.

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

在这里插入图片描述


C++中指针的特性

C++中允许多个指针指向同一个值
C++支持无类型指针,就是没有被声明为某种特定类型的指针,例如void*pointer,对于一个无类型指针进行解引用前,必须先把他转换为适当的数据类型,且无类型指针可以指向任何类型的数据,常用于函数的参数交换.


整型指针和字符型指针在输出时的异同

并使用reinterpret_cast重释转换符对指针地址进行类型转换并利用指针输出值和指针的地址

void ArrayPointer()
{
	const unsigned short ITEMS = 5;//建立静态变量

	int intArray[ITEMS] = { 1,2,3,4,5 };
	char charArray[ITEMS] = {'F','i','g','h','t' };

	int* intPtr = intArray;
	char* charPtr = charArray;

	cout << "整型数组输出" << endl;
	for (int i = 0; i < ITEMS; i++)
	{
		cout << *intPtr << "使用强制类型符转换后地址为" << reinterpret_cast<unsigned long>(intPtr) << '\n';
		//todo输出的第一个元素的地址为  11532476 最后一个元素的地址为 11532492 (10进制)

		cout << *intPtr << "强制转换后的地址为" << (int*)intPtr << '\n';
		//todo输出的第一个元素的地址为 00AFF8AC  最后一个元素的地址为 00AFF8B0 (16进制)
		intPtr++;//整型每个元素地址占4个字节空间
		cout << endl;
	}

整型数组元素值及其指针地址输出结果
在这里插入图片描述

cout << "字符型数组输出" << endl;
	for (int i = 0; i < ITEMS; i++)
	{
		//使用强制转换格式符对字符串指针地址进行类型转换 便于显示
		cout << *charPtr << "使用强制类型符转换后地址为" << reinterpret_cast<unsigned long>(charPtr)<< '\n';
		//todo输出的第一个元素的地址为  11532460 最后一个元素的地址为 11532464 (10进制)

		//直接输出字符指针的指针名,结果会是整个字符串 要想获取字符串的地址 需强制转换为其他指针(非/char*) 
		cout << *charPtr << "强制转换后的地址为" << (int*)charPtr << '\n'; 
		charPtr++;//字符型每个元素地址占1个字节空间
		//todo输出的第一个元素的地址为 00AFF8BC  最后一个元素的地址为 00AFF8CC (16进制)
		cout << endl;
	}
	//🔥为什么我们要用“reinterpret_cast” (reinterpret_cast允许任何类型的指针转换到别的任何类型的指针)
	//对于C++编译器而言,数组的名字同时有两层涵义,一是数组在内存的首地址,二就是字面意义上的数组名字
	//当我们的指令形如:cout<<数组名的时候,编译器就面临一个选择:人类到底是想输出数组内容呢?还是输出元素地址呢
	//最后它根据概率统计,会自作聪明地认为我们大部分时候是想(从当前指针位置开始)输出数组的内容,而不是当前元素的地址
	//todo因此,必须用一个强制转换格式符  reinterpret_cast<unsigned long>() 来明确告诉编译器,我这里需要输出的是当前元素的地址
	//todo语法 reinterpret_cast <新类型> (表达式)
}

字符型数组元素值及其指针地址输出结果
在这里插入图片描述


以上两个结果为正常结果 但是在不使用reinterpret_cast对字符型数组指针进行转换后 (直接输入指针)就会出现判别错误
在这里插入图片描述

在这里插入图片描述
注意:🎯

char字符类型的指针在打印指针地址时需要进行指针类型转换:
可以发现,在不使用reinterpret_cast对字符型数组指针进重释转换的情况下,int整型数组的指针仍正常输出(16进制),而char字符型数组的指针则出现了乱码异常,通过观察异常情况,发现指针在初始位置上进行了递增

这是因为,对于C++编译器而言,数组的名字同时有两层涵义,一是数组在内存的首地址,二就是数组名字,此时当我们的指令如下:cout<<数组名的时候,编译器就面临一个选择,人类到底是想输出数组内容呢?还是输出元素地址呢,最后它根据概率统计,会自作聪明地认为我们大部分时候是想从当前指针位置开始,输出数组的内容,而不是输出当前指针所指向的数组内的元素的地址
因此,在输出字符型指针的地址前,我们必须使用一个强制转换格式符reinterpret_cast<unsigned long>()来明确告诉编译器,我这里需要输出的是当前元素的地址!

强制重释转换符语法 reinterpret_cast <新类型> (表达式)


指针数组与重载函数

建立重载函数

在这里插入图片描述
以不同的参数类型进行函数的重载
在这里插入图片描述
结果
在这里插入图片描述


完整代码

#include <iostream>

using namespace std;

void demo();

void ArrayPointer();

//todo两个名为printf的重载函数  分别用整型数组和字符型数组的指针操作并解引用进行数据读取
//接收实参一为整型数组指针的首地址也就是第一个元素的地址 接收实参二为设置的整型数组指针的第n个元素的指针地址
void print(int *Pbegin, int* Pend)
{
	while (Pbegin != Pend)//循环递增打印数组中的指针所指向的各个元素的值 其终止条件为 需要被打印的指针位置的元素值不大于print函数设置的第二个参数的指针位置
	{
		cout << *Pbegin;//进行数组型指针中第Pbegin个指针的解引用并打印指针所指向的数组元素值
		++Pbegin;
	}
}

void print(char* Pbegin, char* Pend)
{
	while (Pbegin != Pend)
	{
		cout << *Pbegin;
		++Pbegin;
	}
}

int main()
{
	//ArrayPointer();

	//todo 案例二指针数组与重载

	int num[5] = { 0,1,2,3,4 };
	char name[5] = { 'B','r','o','o','k' };

	print(num, num + 5);//形参一为数组型指针数组的指针首个元素地址 形参二为数组型指针数组的第五个元素的指针地址
	cout << endl;

	print(name, name + 5);//形参一为字符型型指针数组的指针首个元素地址 形参二为字符型指针数组的第五个元素的指针地址
	cout << endl;
}

void demo()
{
	int a = 456;//存放数字
	char b = 'c';//存放c的ASCII码
	int* apointer = &a;
	char* bpointer = &b;

	//根据上方4个变量可知
	//程序这时保留了4个内存块,两个给变量保留,两个给指针保留

	//当我们知道某个变量在内存中的地址(通过指针) 就可以利用指针访问位于该地址的数据
	//访问该地址的数据 需要进行解引用处理 即在指针名的前面加一个*
	//例如
	cout << *apointer << endl;

	//todo *apointer和变量a 是等价的 因此当 *apointer=123 会 改变变量a的值

	//todo指针和数组
	//数组是以一组连续的内存块保存的 数组中的每个字节 均占有4个字节的内存  且内存块是连贯且 首位相接的

	//todo数组的名字其实也是一个指针(指向数组的基地址,也就是第一个元素的地址)

	int array[50];

	int* pter1 = array;
	//int* pter1 = &array[0];  等值于  int* pter1 = array; 

	//todo使用指针访问同一数组的其他元素

	//使用  pter1++;(不是加1 是加一个内存块 4个字节i)
}


//todo 案例一使用reinterpret_cast<unsigned long>对指针地址进行类型转换并利用指针输出值和指针的地址
void ArrayPointer()
{
	const unsigned short ITEMS = 5;//建立静态变量

	int intArray[ITEMS] = { 1,2,3,4,5 };
	char charArray[ITEMS] = {'F','i','g','h','t' };

	int* intPtr = intArray;
	char* charPtr = charArray;

	cout << "整型数组输出" << endl;
	for (int i = 0; i < ITEMS; i++)//todoC++中指针地址可以进行+操作
	{
		cout << *intPtr << "使用强制类型符转换后地址为" << reinterpret_cast<unsigned long>(intPtr) << '\n';
		//todo输出的第一个元素的地址为  11532476 最后一个元素的地址为 11532492 (10进制)

		cout << *intPtr << "强制转换后的地址为" << (int*)intPtr << '\n';
		//todo输出的第一个元素的地址为 00AFF8AC  最后一个元素的地址为 00AFF8B0 (16进制)
		intPtr++;
		cout << endl;
		//整型每个元素地址占4个字节空间
	}

	cout << "字符型数组输出" << endl;
	for (int i = 0; i < ITEMS; i++)
	{
		//使用强制转换格式符对字符串指针地址进行类型转换 便于显示
		cout << *charPtr << "使用强制类型符转换后地址为" << reinterpret_cast<unsigned long>(charPtr)<< '\n';
		//todo输出的第一个元素的地址为  11532460 最后一个元素的地址为 11532464 (10进制)

		//直接输出字符指针的指针名,结果会是整个字符串 要想获取字符串的地址 需强制转换为其他指针(非/char*) 
		cout << *charPtr << "强制转换后的地址为" << (int*)charPtr << '\n'; 
		charPtr++;
		//todo输出的第一个元素的地址为 00AFF8BC  最后一个元素的地址为 00AFF8CC (16进制)
		cout << endl;
		//字符型每个元素地址占1个字节空间
	}
	//todo为啥要用“reinterpret_cast” (reinterpret_cast允许任何类型的指针转换到别的任何类型的指针)
	//对于C++编译器而言,数组的名字同时有两层涵义,一是数组在内存的首地址,二就是数组名字
	//当我们的指令形如:cout<<数组名的时候,编译器就面临一个选择:人类到底是想输出数组内容呢?还是输出元素地址呢
	//最后它根据概率统计,会自作聪明地认为我们大部分时候是想(从当前指针位置开始)输出数组的内容,而不是当前元素的地址
	//todo因此,必须用一个强制转换格式符  reinterpret_cast<unsigned long>() 来明确告诉编译器,我这里需要输出的是当前元素的地址
	//todo语法 reinterpret_cast <新类型> (表达式)
}

相关文章:

  • 功利?
  • 格式输入输出 cin.width()cout.width()
  • scp和sftp
  • C++ 结构体
  • 中兴事业部你真行
  • C++ 对象的基础(结构及结构指针)
  • 《Linux内核设计与实现》读书笔记(三)- Linux的进程
  • C++联合枚举和类型别名
  • while循环与中断语句break、continue、return以及goto的使用
  • CentOS使用Screen管理会话选项
  • 循环程序跳出(_kbhit()、getch() 、getchar())
  • C++ 类与对象
  • (转) Android中ViewStub组件使用
  • C++ 构造函数与析构函数
  • MFC应用程序中处理消息的顺序,创建窗口的过程关闭窗口的顺序(非模态窗口),打开模式对话框的函数调用顺序...
  • [译] React v16.8: 含有Hooks的版本
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • canvas绘制圆角头像
  • ComponentOne 2017 V2版本正式发布
  • Debian下无root权限使用Python访问Oracle
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Iterator 和 for...of 循环
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • MobX
  • mysql 数据库四种事务隔离级别
  • React+TypeScript入门
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • Redux 中间件分析
  • SpiderData 2019年2月16日 DApp数据排行榜
  • Spring声明式事务管理之一:五大属性分析
  • uva 10370 Above Average
  • webpack入门学习手记(二)
  • 翻译:Hystrix - How To Use
  • 网络应用优化——时延与带宽
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • (1)STL算法之遍历容器
  • (Note)C++中的继承方式
  • (八)Flask之app.route装饰器函数的参数
  • (第61天)多租户架构(CDB/PDB)
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)ssm高校实验室 毕业设计 800008
  • (四)JPA - JQPL 实现增删改查
  • .bashrc在哪里,alias妙用
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .net FrameWork简介,数组,枚举
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET 中让 Task 支持带超时的异步等待
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • .NET运行机制
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • /ThinkPHP/Library/Think/Storage/Driver/File.class.php  LINE: 48
  • @html.ActionLink的几种参数格式