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 <新类型> (表达式)
}