前言:
打牢基础,才能写出准确的代码.C++ Prime Plus第六版
引入:
C++提供了new来使用程序的内存空间,并且提供了定位符功能.
一.举例
data.h
namespace myns {
struct datas {
int age;
int position;
};
}
new运算符和定位符.cpp
#ifndef new运算符和定位符_H
#define new运算符和定位符_H
#include<iostream>
#include"data.h"
const int NUMBER = 512;
int main() {
using myns::datas;
using namespace std;
char* p = new char[NUMBER]; //分配静态内存,具体地址未知.用char分配内存,其他类型有问题
for (int i = 0; i < NUMBER; i++) {
p[i] = 65; //给元素赋值,看使用该地址的地方是否能用
}
cout << "分配空间返回地址是:" << (void*)p << endl; //打印分配静态内存的地址,如果直接用p,得到字符串非地址
//把一个含有5个整型的数组放到地址中,可以放数组,可以放对象,所以所有类型数据都可以放到某个位置
int* pip = new(p) int[5];
for (int i = 0; i < 5; i++) {
cout <<"已赋值是:"<< pip[i] << endl; //警告"未初始化内存pip[i]"但不出错,可以使用地址中已赋值
}
cout << "定位后使用空间地址是:" << pip << endl; //调用定位符new函数的返回值就是定位地址,和上面地址相同
//delete[] p; //释放内存,为了下面程序不出错,注释
for (int i = 0; i < 5; i++) {
cout << "你需要的数字是:" << pip[i] << endl; //如果释放内存,结果会改变;此时不变
}
cout << "整型数据长度为:" << sizeof(int) << endl;
cout << "double型数据长度为:" << sizeof(double) << endl;
double* dp = new(p + 5 * sizeof(int)) double; //地址偏移5个int;
cout << "偏移5个整型量后现在所在地址是:" << dp << endl; //打印当前地址,验证和上面地址是否偏移了20个字节,结果正确
char* dpm = (char*)(p + 5 * sizeof(int)); //计算地址并强转类型
double* dp3 = new(dpm) double; //地址偏移5个int;
cout << "偏移5个整型量后现在所在地址是:" << dp3 << endl; //打印当前地址,验证和上面地址是否一致,结果正确
datas* pstruct = new(p + 5 * sizeof(int) +sizeof(double)) datas[2]; //数组放到指定位置,地址偏移1个double类型
cout << "偏移1个double后现在所在地址是:" << pstruct << endl; //打印当前地址,验证和上面地址是否偏移了8个字节,结果正确
datas* ps = new(dp +sizeof(double)) datas[2]; //警告并得不到正确结果,提示"sizeof 和 countof 量值之间可能不一致。使用 sizeof() 调整字节大小"
cout << "偏移1个double后现在所在地址是:" << ps << endl; //打印当前地址,验证和上面地址是否偏移了8个字节,结果错误,64个字节
char *dp4 = (char*)(p + 5 * sizeof(int) + sizeof(double)); //计算地址并强转类型
datas* ps2 = new(dp4) datas[2]; //警告同上
cout << "偏移1个double后现在所在地址是:" << ps2 << endl; //打印当前地址,验证和上面地址是否偏移了8个字节,结果正确
//for (int i = 0; i < 2; i++) { //给结构赋值
// cout << "请输入第" << i << "个的年龄:" << endl;
// cin >> pstruct[i].age;
// cout << "请输入第" << i << "个的位置:" << endl;
// cin >> pstruct[i].position;
// }
int* ip = new(p + 5 * sizeof(int) + sizeof(double) + 2 * sizeof(datas)) int;//地址再次偏移;
cout << "偏移1个datas后现在所在地址是:" << ip << endl; //打印当前地址,相差16字节,结果正确
char* pp = (char*)(dpm + sizeof(double) + 2 * sizeof(datas));
int* ip2 = new(pp) int;
cout << "偏移1个datas后现在所在地址是:" << ip2 << endl; //打印当前地址,验证是否和上面一致,结果正确
}
#endif
二.代码说明
怎样方便使用位置偏移,
datas* ps = new(dp +sizeof(double)) datas[2]; //这句里的写法最方便,但是得不到正确的结果.
参考自C++ Prime Plus Edition 6th. 里面没提到new定位符的函数原型,提到了返回值是(void *)可被任意指针接收. 但其传入的地址其实是有要求的,类型为char *,字符指针.所以在每次偏移地址时,将返回值类型强转后才可以继续调用new()定位函数.
char* dpm = (char*)(p + 5 * sizeof(int)); //计算地址并强转类型
同样,在分配空间时用new char[],得到不会产生歧义的结果.
三.引申
1.char *作为形参传入函数,而这里表示定位地址. 我们知道char *另外有个含义是字符串.所以可以推导出字符串实际上是有单独地址的.
2.虽然类型是地址,但是char *和int *,double *,或者其他类型的地址的数据类型不一样.从例子中可以反映出来.
四 映射硬件
在C++ Prime Plus Edition 6th中,有一句提到了可以控制硬件,但具体操作没说.因为话题会牵涉到操作系统.,不能简单说完.首先语言中使用的内存是虚拟内存,操作系统负责将其转化为物理内存.物理内存可以映射到硬件的寄存器上.这中间可能还会涉及硬件驱动程序.但一般会由硬件设计人员提供接口给软件设计者使用.
C++语言繁琐,但是速度很快是其最大的优势所在.而速度快的原因在于可以直接操作内存.直接操作内存,new定位符就是其中一种方式.下面就试想一下操作内存的两种方式.
假设屏幕上有一个点,有3个寄存器控制颜色,每个寄存器为1字节.其中1个寄存器控制红色,1个控制绿色,1个控制蓝色.(当3个值都为0显示白色,都为255时显示黑色,其余颜色由不同数值控制).这三个寄存器的虚拟地址为0x1a2b3c4d,0x1a2b3c4e,0x1a2b3c4f.
1.直接取得地址,向其中写数据
char *red=(char *)0x1a2b3c4d;
char *green=(char *)0x1a2b3c4e;
char *blue=(char *)0x1a2b3c4f;
*red=128;
*green=128;
*blue=128;
2.使用定位符,定位地址
char *color=new(0x1a2b3c4d) char[3];
color[0]=128; //红色寄存器
color[1]=128; //绿色寄存器
color[2]=128; //蓝色寄存器
这里因为假设地址是在一起的,所以可以把字节型数组放到起始地址上,逐个赋值.
上述数据是简单类型和数组类型,其他复杂类型,全部用对象或者结构来写入,方法是一样的.
题外话:
C++比C语言繁琐,但应用起来会方便一些.举例:如果有复杂的数据类型,比如某种数据结构来表示的数据,(树的结点还是树),在C++里可以用容器类,结点是容器类对象来表示.如果用C语言的话,定义数据结构的每层增删查都要写算法,相对麻烦.
结语:
new定位符使用时注意传入地址的数据类型是char *.