gnz*_*lbg 5 c++ operating-system memory-address undefined-behavior
注意:如果在读完这个问题之后你会想,"怎么会发生这种情况",这没关系.如果你想保持开放的心态,那么在你可以遵循的问题后面有一些要点,这些要点表明了这种情况如何发生以及为什么这有用.请记住,这只是一个问题,而不是任何这些主题的教程.评论已经有足够的噪音,很难遵循.如果您对这些主题有疑问,如果您将其作为问题发布在SO而不是评论中,我将不胜感激.
问题:如果我有一个类型的对象int存储在指向的地址c
int* c = /* allocate int (returns unique address) */;
*c = 3;
Run Code Online (Sandbox Code Playgroud)
由两个指针称为a和b:
int* a = /* create pointer to (*c) */;
int* b = /* create pointer to (*c) */;
Run Code Online (Sandbox Code Playgroud)
这样:
assert(a != b); // the pointers point to a different address
assert(*b == 3);
*a = 2;
assert(*b == 2); // but they refer to the same value
Run Code Online (Sandbox Code Playgroud)
这是未定义的行为吗?如果是,C++标准的哪一部分不允许这样做?如果没有,C++标准的哪些部分允许这样做?
注:记忆c点分配与返回一个唯一的地址(内存分配函数new,malloc...).使用不同值创建这些指针的方法是非常特定于平台的,尽管在大多数unix系统中,它可以mmap在Windows上完成,也可以在Windows上完成VirtualAlloc.
背景:大多数操作系统(具有不在0环上的用户空间的操作系统)在虚拟内存上运行其进程,并具有从虚拟内存页面到物理内存页面的映射.其中一些系统(Linux/MacOS/BSD/Unix和64位窗口)提供了一些系统调用(如mmap或VirtualAlloc),可用于将两个虚拟内存页映射到同一物理内存页.当进程执行此操作时,它实际上可以从两个不同的虚拟内存地址访问同一页物理内存.也就是说,这两个指针将具有不同的值,但它们将访问相同的物理内存存储.谷歌的关键词:mmap,虚拟内存,内存页面.使用此功能获取利润的数据结构是"魔术环缓冲区"(这是技术术语),以及非重新分配动态大小的向量(即,当它们增长时不需要重新分配内存的向量).谷歌提供了有关这些的更多信息,而不是我在这里适合的.
可能非工作的非常小的例子(仅限unix):
我们首先在堆上分配一个int.以下请求匿名,非文件支持的虚拟内存映射.一个人必须在这里请求至少一个完整的内存页面,但为了简单起见,我只需要一个大小int(mmap将分配一个完整的内存页面):
int* c= mmap(NULL, sizeof(int), PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE,-1, 0);
Run Code Online (Sandbox Code Playgroud)
现在我们需要将它映射到两个独立的存储器位置,因此我们将它映射到相同的存储器映射文件,两次,例如,两个相邻的存储器位置.我们不会真正使用此文件,但我们仍需要创建它并打开它:
mmap(c, sizeof(int), PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, some_fd, 0);
mmap(c + 1, sizeof(int), PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, some_fd, 0);
Run Code Online (Sandbox Code Playgroud)
现在我们差不多完成了:
int* a = c;
int* b = c + 1;
Run Code Online (Sandbox Code Playgroud)
这些显然是不同的虚拟地址:
assert(a != b);
Run Code Online (Sandbox Code Playgroud)
但他们指向同一个非文件支持的物理内存页面:
*a = 314;
assert(*b == 314);
Run Code Online (Sandbox Code Playgroud)
你去吧 使用VirtualAlloc相同的方法可以在Windows上完成,但API有点不同.
首先让我们看一下标准对于一个对象的看法
[intro.object]
C++程序中的构造创建,销毁,引用,访问和操作对象.对象是存储区域.[注意:函数不是对象,无论它是否以对象的方式占用存储.-end note]一个对象由定义(3.1),new-expression(5.3.4)或实现(12.2)在需要时创建.创建对象时确定对象的属性.对象可以有一个名称(第3条).对象的存储持续时间(3.7)会影响其生命周期(3.8).对象具有类型(3.9).术语对象类型是指用于创建对象的类型.有些对象是多态的(10.3); 该实现生成与每个这样的对象相关联的信息,使得可以在程序执行期间确定该对象的类型.对于其他对象,其中发现的值的解释由用于访问它们的表达式(第5条)的类型确定.
然后我们有
除非对象是零字段或零大小的基类子对象,否则该对象的地址是它占用的第一个字节的地址.如果一个是另一个的子对象,或者如果至少一个是零大小的基类子对象并且它们是不同类型的,则不是位字段的两个对象可以具有相同的地址; 否则,他们应有不同的地址.
所以我们知道一个对象有一个地址,它是它使用的存储的第一个字节.如果我们看一下我们有什么字节
[intro.memory]
C++内存模型中的基本存储单元是字节.一个字节至少足以包含基本执行字符集(2.3)的任何成员和Unicode UTF-8编码形式的八位代码单元,并由连续的位序列组成,其数量为implementationdefined.最低有效位称为低位; 最重要的位称为高位.C++程序可用的内存由一个或多个连续字节序列组成.每个字节都有一个唯一的地址.
强调我的
因此,如果我们有一个指向对象的指针,则指针将保存唯一值(地址).如果我们有另一个指向同一个对象的指针,那么它也必须具有相同的值(地址).未定义的行为甚至不会输入等式,因为您根本不能有两个指向具有不同值的同一对象的指针.
| 归档时间: |
|
| 查看次数: |
1132 次 |
| 最近记录: |