当你学习C++时,或者至少当我通过C++ Primer学习它时,指针被称为它们指向的元素的"内存地址".我想知道这是多少.
例如,做两个元素*p1
并*p2
拥有属性,p2 = p1 + 1
或者p1 = p2 + 1
当且仅当它们在物理内存中相邻时?
您应该将指针视为虚拟内存的地址:现代消费者操作系统和运行时环境在物理内存和您看到的指针值之间至少放置一层抽象.
至于你的最终陈述,即使在虚拟内存地址空间中也无法做出这种假设.指针算法仅在连续内存(如数组)的块内有效.虽然允许(在C和C++中)将指针指向一个经过数组(或标量)的点,但是引用这样一个指针的行为是未定义的.在C和C++的上下文中假设物理内存中的邻接是没有意义的.
一点也不.
C++是对计算机将执行的代码的抽象.我们在一些地方看到这种抽象泄漏(例如,类成员引用需要存储),但是一般来说,如果你对抽象进行编码而没有别的东西,那么你会更好.
指针是指针.他们指向事物.它们会被实现为内存地址吗?也许.它们也可以被优化,或者(例如指向成员的指针)它们可能比简单的数字地址更复杂.
当您开始将指针视为映射到内存中地址的整数时,您会开始忘记例如,未定义指向不存在的对象的指针(您不能只是无法增加和减少指针)你喜欢的任何内存地址).
正如许多答案已经提到的那样,它们不应该被认为是内存地址.查看这些答案,并在此处了解它们.解决你的上一个陈述
*p1和*p2具有属性p2 = p1 + 1或p1 = p2 + 1当且仅当它们在物理内存中相邻时
只有当p1
且p2
属于同一类型或指向相同大小的类型时才是正确的.
绝对正确地将指针视为内存地址.这就是我所使用的所有编译器中的内容 - 用于许多不同的处理器架构,由许多不同的编译器生产商制造.
但是,编译器会做一些有趣的魔术,以帮助您实现正常内存地址[至少在所有现代主流处理器中]都是字节地址,而指针引用的对象可能不是一个字节.因此,如果我们有T* ptr;
,ptr++
会做((char*)ptr) + sizeof(T);
或者ptr + n
是((char*)ptr) + n*sizeof(T)
.这也意味着你的p1 == p2 + 1
要求p1
和p2
属于同一类型T
,因为+1
实际上是这样+sizeof(T)*1
.
上面的"指针是内存地址"有一个例外,那就是成员函数指针.它们是"特殊的",现在,请忽略它们实际实现的方式,足以说它们不仅仅是"内存地址".
操作系统为您的程序提供物理机的抽象(即您的程序在虚拟机中运行).因此,您的程序无法访问您计算机的任何物理资源,无论是CPU时间,内存等; 它只需向操作系统询问这些资源.
对于内存,您的程序在由操作系统定义的虚拟地址空间中工作.此地址空间具有多个区域,例如堆栈,堆,代码等.指针的值表示此虚拟地址空间中的地址.实际上,指向连续地址的2个指针将指向该地址空间中的连续位置.
但是,这个地址空间被操作系统分成页面和段,这些页面和段根据需要从内存中交换出来,因此你的指针可能会或可能不会指向连续的物理内存位置,并且在运行时无法告诉它是否是是不是真的.这还取决于操作系统用于分页和分段的策略.
底线是指针是内存地址.但是,它们是虚拟内存空间中的地址,由操作系统决定如何将其映射到物理内存空间.
就您的计划而言,这不是问题.这种抽象的一个原因是让程序相信它们是机器的唯一用户.想象一下,如果您在编写程序时需要考虑其他进程分配的内存,那么您必须经历的噩梦 - 您甚至不知道哪些进程将与您的进程同时运行.此外,这是一种强制执行安全性的好方法:您的进程不能(至少应该不能)恶意访问另一个进程的内存空间,因为它们在2个不同的(虚拟)内存空间中运行.