c++如何判断二进制相同的对象?

2023-08-17 00:23:58 +08:00
 LuckyPocketWatch

假如有两个自定义类 class A 和 class B ,这两个自定义类的对象长度均为 32 字节,然后有如下代码

A* ptr = new A(/*paratemerA*/);  //先生成一个 class A 的对象为 objectA
A* objectA = ptr;

if(/*condition == true*/){
    ptr->~A();  //销毁对象 class A ,但内存没有还给系统
    ptr = new B(/*paratemerB*/);//在原来的内存上生成一个 class B 的对象为 objectB
}

if(ptr == objectA)
    ptr->doSomething();

objectA 和 objectB 这两个类对象,长度均为 32 字节,这两个对象创建在相同的内存里(创建时间不一样),然后这段代码在运行时出现了一个巧合,生成的这两个对象二进制完全一样

那这种情况下 C++时如何判断 ptr == objectA 这段代码的?

2962 次点击
所在节点    C++
36 条回复
codehz
2023-08-17 00:41:17 +08:00
需要考虑的问题是为什么你会有一个无效的指针需要比较?有这个无效的指针在,你问题是解决不完的…
sunstar
2023-08-17 00:50:11 +08:00
ABA 问题?可以看看 brpc 的对象池
polaa
2023-08-17 00:54:47 +08:00
感觉你可能需要看 malloc 或者 calloc 相关堆实现
包括 free chunk 之后插入 fast bin 或者 tcache 之类的
好像涉及 uaf 或者 undefined behavior 漏洞吧
堆相关知识忘光了 就这些吧
Inn0Vat10n
2023-08-17 00:55:25 +08:00
c++这里比的就是指针里存的地址,实际指向什么,指向的东西是不是合法的是不管的
ashong
2023-08-17 01:18:57 +08:00
调用了析构而没有删除,但新的 ptr 应该是指向新的 B 对象
xiadong1994
2023-08-17 01:52:32 +08:00
你是可以要求编译器在指定的内存地址里面 new object 的,所以这不一定是巧合。https://isocpp.org/wiki/faq/dtors#placement-new

ptr 能放 B 对象的指针说明这是一个父类指针指向子类的情况?那么比较 A*和 B*地址的时候应该会隐式转换成父类指针再比较。同类型指针指向同样地地址就是相同的。
ryd994
2023-08-17 01:57:45 +08:00
你这个情况不正常。既然 A 的内存没有还给系统,那下面的 new 分配内存的时候就不会分配到 A 的内存。否则内存管理就有 bug 了。libc 这么多年,你随便就遇到内存管理 bug 的概率基本可以忽略。

最简单的办法就是全局内存日志。在构造和析构函数里打日志用来分析

一般没人乱飞野指针的。一般是配合引用计数或者 autoptr/smartptr 之类的使用。
那引用计数也可以打日志
ryd994
2023-08-17 01:59:30 +08:00
如果你只是想区分 A 对象和 B 对象,那让对象储存自己的名字即可,在构造函数里写入这个名字
lany
2023-08-17 03:10:41 +08:00
objectA 指针里面存的 class A 的起始地址
geelaw
2023-08-17 03:12:09 +08:00
假设你的代码是

A *ptr = new A();
A *objectA = ptr;
ptr->~A();
ptr = new (ptr) B(); // 注意这里需要用 placement new
if (ptr == objectA) ptr->doSomething();

并且 B 是 A 的派生类,并且假设 struct B : A { /* 没有额外的成员 */ };

第一,我不知道 new (ptr) B() 是否是未定义行为,这需要翻阅标准。(适合于 A 的 storage 一定适合于 B 吗?)

第二,接下来比较 ptr 和 objectA 绝对是 undefined behavior ,因为 objectA 最后一次赋值的时候指向的对象已经不存在了,所以 objectA 不是有效的指针——这件事情和原来的 storage 上面现在有没有 B 类型的对象、B 是不是 A 的子类没有任何关系。

让我来写一个正确的版本:

char alignas(B) storage[sizeof(B)];
A *ptr = new (storage) A();
A *objectA = ptr;

ptr->~A();
ptr = new (ptr) B();

objectA = std::launder(objectA); // 这一步非常重要

if (objectA == ptr) ptr->doSomething(); /* if 的 true 分支会运行 */
geelaw
2023-08-17 03:16:01 +08:00
@geelaw #10 原来的问题:C++ 如何判断 ptr == objectA 这段代码的?

既然是未定义行为,编译器可以选择格式化你的硬盘,但大多数编译器并不会这样做。
在我转写的第一段代码里,比较可能发生的有两种:

1. 无事发生,直接进行数值的比较,因此 true 分支会运行。
2. 编译器意识到
(i) ptr 赋值获得的是新对象
(ii) objectA 在 ptr 赋值之后就没有再赋值过
于是意识到 objectA 和 ptr 在标准看来不可能同时有效地指向同一个对象,因此直接删除 if 的 true 分支,导致 true 分支不执行。
dangyuluo
2023-08-17 04:06:09 +08:00
```
ptr->~A(); //销毁对象 class A ,但内存没有还给系统
ptr = new B(/*paratemerB*/);//在原来的内存上生成一个 class B 的对象为 objectB
```
并不能保证在原来的内存上生成一个 class B ,除非你用了 placement new

而且这是个 UB
dangyuluo
2023-08-17 04:11:28 +08:00
@gleelaw `operator==`在两个同类型指针上应该是 well defined 。`*ptr`才是 UB 。

https://godbolt.org/z/7Kzs4x4Mn
iceheart
2023-08-17 06:10:46 +08:00
就是地址比较,地址肯定不同,所以 if 条件肯定不成立
lopssh
2023-08-17 07:48:27 +08:00
你这代码会在编译时就被咔擦掉吧。。。
你可以将 A*的变量赋值为 B*,你这 A/B 是相互不兼容的吧?那不是动态类型语言的活儿么。
lopssh
2023-08-17 07:50:02 +08:00
@sunstar ABA 是先 A 然后 B ,然后再 A ,丢失了有关于 B 的信息,这个例子就只是 AA 。
lopssh
2023-08-17 07:53:38 +08:00
A* ptr = new A(/*paratemerA*/); //先生成一个 class A 的对象为 objectA
A* objectA = ptr;

if(/*condition == true*/){
ptr->~A(); //销毁对象 class A ,但内存没有还给系统
}

if(ptr == objectA)
ptr->doSomething();

我看你还是用这个来举例吧。
那句 ptr 的赋值毫不影响程序逻辑,如果 B 是 A 的子类的话,
blinue
2023-08-17 08:41:57 +08:00
你对指针有很大误解,学好基础啊
hankai17
2023-08-17 08:43:03 +08:00
感觉是设计不合理 出现内存池条件竞争
可以看一下 brpc 内存池管理
mingl0280
2023-08-17 09:02:56 +08:00
你不会觉得 ptr = new B 能编译过吧?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/965933

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX