共享指针和回调注册的结构。当出于我以外的原因调用回调时,原始指针值发生了变化
Struct of shared pointers and callback registration. Raw pointer value changed when callback is called for reasons beyond me
我已经实现了一个用于注册回调的接口,但遇到了一个我无法理解的问题。
特别是,如果将函数注册为回调,并将共享指针结构的副本指定为参数,则调用回调时共享指针的原始指针值会发生变化,如果出现以下情况,则会导致出现分段错误指针被取消引用(因为它不再指向原始对象)。但是,当我使用对共享指针结构的引用注册回调时。它工作正常。
要理解我的意思,请考虑以下代码块。
test.h
和test.c
分别包含回调注册的接口和实现,而test_cb.cpp
是使用该接口的主要客户端代码。
test.h
#ifndef __TEST_H__
#define __TEST_H__
#ifdef __cplusplus
extern "C"
{
#endif
typedef void (*TestFnCb)(void *pArgs);
void RegisterCallback(TestFnCb fn, void *pArgs);
void PeriodicActions();
#ifdef __cplusplus
}
#endif
#endif
test.c
typedef struct TestFnData
{
TestFnCb pFunc;
void *pArgs;
} TestFnData;
TestFnData fndata;
void RegisterCallback(TestFnCb fn, void *pArgs)
{
fndata.pFunc = fn;
fndata.pArgs = pArgs;
}
void PeriodicActions()
{
while(1)
{
if(fndata.pFunc)
{
fndata.pFunc(fndata.pArgs);
}
usleep(1000000);
}
}
test_cb.cpp:
#include <iostream>
#include <thread>
#include <memory>
#include "test.h"
class B
{
public:
virtual void print()
{
std::cout << "B::print()\n";
}
};
class D: public B
{
public:
void print() override
{
std::cout << "D::print()\n";
}
};
struct TestStruct
{
std::shared_ptr<B> m1;
std::shared_ptr<B> m2;
};
TestStruct myStruct;
void ThreadFuncProc()
{
while(1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void MyCallback(void *pArgs)
{
TestStruct *pStruct = static_cast<TestStruct*>(pArgs);
std::cout << "MyCallback: pStruct " << pStruct << "\n";
std::cout << "MyCallback: myStruct.m1.get(): " << pStruct->m1.get() << "\n";
std::cout << "MyCallback: myStruct.m2.get(): " << pStruct->m2.get() << "\n";
pStruct->m1->print(); // Segfault here because m1.get() isn't pointing to the correct raw pointer.
pStruct->m2->print();
}
void TestInit(TestStruct testSt) // This is not.
// void TestInit(TestStruct &testSt) // This is fine.
{
std::cout << "TestInit: &testSt: " << &testSt << "\n";
std::cout << "TestInit: testSt.m1.get(): " << testSt.m1.get() << "\n";
std::cout << "TestInit: testSt.m2.get(): " << testSt.m2.get() << "\n";
RegisterCallback(MyCallback, &testSt);
}
int main(int argc, char **argv)
{
myStruct.m1 = std::make_shared<D>();
myStruct.m2 = std::make_shared<D>();
std::cout << "myStruct.m1.get(): " << myStruct.m1.get() << '\n';
std::cout << "myStruct.m2.get(): " << myStruct.m2.get() << '\n';
TestInit(myStruct);
std::thread t1(ThreadFuncProc);
std::thread t2(PeriodicActions);
t1.join();
t2.join();
return 0;
}
如您所见,我创建了两个共享指针对象,每个都封装了一个指向 class D
实例的指针。 TestInit()
然后使用包含两个共享指针对象的结构调用。它只是将 MyCallback()
注册为回调并传入结构。这就是它变得奇怪的地方。如果 TestInit()
被定义为接受 TestStruct &
而不是 TestStruct
,一切都很好。
输出
$ gcc -o test.o -c test.c
$ g++ -o test_cb.o -c test_cb.cpp -std=c++11
$ g++ -o main test_cb.o test.c -lpthread
$ ./main
myStruct.m1.get(): 0x6bdc30
myStruct.m2.get(): 0x6bdc50
TestInit: &testSt: 0x7fff9578e470
TestInit: testSt.m1.get(): 0x6bdc30
TestInit: testSt.m2.get(): 0x6bdc50
MyCallback: pStruct 0x7fff9578e470
MyCallback: myStruct.m1.get(): 0x7f303f730700
MyCallback: myStruct.m2.get(): 0x6bdc50
Segmentation fault (core dumped)
我不明白的是,为什么 TestInit()
中的副本 m1.get()
与 main()
中的输出相同,但在 [=19= 中调用它们时却不同]. pStruct
显然指向 TestInit()
中的结构副本。是什么导致 m1
封装的原始指针发生变化?而且,为什么m2
没有这个问题?
我完全不明白为什么会这样。我有 运行 个假设要检验。
任何见解将不胜感激!
在您的 TestInit()
函数中:
RegisterCallback(MyCallback, &testSt);
&testSt
是你的testSt
函数参数的地址,正被传值,这样地址就不再有效了TestInit()
函数 returns.
我已经实现了一个用于注册回调的接口,但遇到了一个我无法理解的问题。
特别是,如果将函数注册为回调,并将共享指针结构的副本指定为参数,则调用回调时共享指针的原始指针值会发生变化,如果出现以下情况,则会导致出现分段错误指针被取消引用(因为它不再指向原始对象)。但是,当我使用对共享指针结构的引用注册回调时。它工作正常。
要理解我的意思,请考虑以下代码块。
test.h
和test.c
分别包含回调注册的接口和实现,而test_cb.cpp
是使用该接口的主要客户端代码。
test.h
#ifndef __TEST_H__
#define __TEST_H__
#ifdef __cplusplus
extern "C"
{
#endif
typedef void (*TestFnCb)(void *pArgs);
void RegisterCallback(TestFnCb fn, void *pArgs);
void PeriodicActions();
#ifdef __cplusplus
}
#endif
#endif
test.c
typedef struct TestFnData
{
TestFnCb pFunc;
void *pArgs;
} TestFnData;
TestFnData fndata;
void RegisterCallback(TestFnCb fn, void *pArgs)
{
fndata.pFunc = fn;
fndata.pArgs = pArgs;
}
void PeriodicActions()
{
while(1)
{
if(fndata.pFunc)
{
fndata.pFunc(fndata.pArgs);
}
usleep(1000000);
}
}
test_cb.cpp:
#include <iostream>
#include <thread>
#include <memory>
#include "test.h"
class B
{
public:
virtual void print()
{
std::cout << "B::print()\n";
}
};
class D: public B
{
public:
void print() override
{
std::cout << "D::print()\n";
}
};
struct TestStruct
{
std::shared_ptr<B> m1;
std::shared_ptr<B> m2;
};
TestStruct myStruct;
void ThreadFuncProc()
{
while(1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void MyCallback(void *pArgs)
{
TestStruct *pStruct = static_cast<TestStruct*>(pArgs);
std::cout << "MyCallback: pStruct " << pStruct << "\n";
std::cout << "MyCallback: myStruct.m1.get(): " << pStruct->m1.get() << "\n";
std::cout << "MyCallback: myStruct.m2.get(): " << pStruct->m2.get() << "\n";
pStruct->m1->print(); // Segfault here because m1.get() isn't pointing to the correct raw pointer.
pStruct->m2->print();
}
void TestInit(TestStruct testSt) // This is not.
// void TestInit(TestStruct &testSt) // This is fine.
{
std::cout << "TestInit: &testSt: " << &testSt << "\n";
std::cout << "TestInit: testSt.m1.get(): " << testSt.m1.get() << "\n";
std::cout << "TestInit: testSt.m2.get(): " << testSt.m2.get() << "\n";
RegisterCallback(MyCallback, &testSt);
}
int main(int argc, char **argv)
{
myStruct.m1 = std::make_shared<D>();
myStruct.m2 = std::make_shared<D>();
std::cout << "myStruct.m1.get(): " << myStruct.m1.get() << '\n';
std::cout << "myStruct.m2.get(): " << myStruct.m2.get() << '\n';
TestInit(myStruct);
std::thread t1(ThreadFuncProc);
std::thread t2(PeriodicActions);
t1.join();
t2.join();
return 0;
}
如您所见,我创建了两个共享指针对象,每个都封装了一个指向 class D
实例的指针。 TestInit()
然后使用包含两个共享指针对象的结构调用。它只是将 MyCallback()
注册为回调并传入结构。这就是它变得奇怪的地方。如果 TestInit()
被定义为接受 TestStruct &
而不是 TestStruct
,一切都很好。
输出
$ gcc -o test.o -c test.c
$ g++ -o test_cb.o -c test_cb.cpp -std=c++11
$ g++ -o main test_cb.o test.c -lpthread
$ ./main
myStruct.m1.get(): 0x6bdc30
myStruct.m2.get(): 0x6bdc50
TestInit: &testSt: 0x7fff9578e470
TestInit: testSt.m1.get(): 0x6bdc30
TestInit: testSt.m2.get(): 0x6bdc50
MyCallback: pStruct 0x7fff9578e470
MyCallback: myStruct.m1.get(): 0x7f303f730700
MyCallback: myStruct.m2.get(): 0x6bdc50
Segmentation fault (core dumped)
我不明白的是,为什么 TestInit()
中的副本 m1.get()
与 main()
中的输出相同,但在 [=19= 中调用它们时却不同]. pStruct
显然指向 TestInit()
中的结构副本。是什么导致 m1
封装的原始指针发生变化?而且,为什么m2
没有这个问题?
我完全不明白为什么会这样。我有 运行 个假设要检验。
任何见解将不胜感激!
在您的 TestInit()
函数中:
RegisterCallback(MyCallback, &testSt);
&testSt
是你的testSt
函数参数的地址,正被传值,这样地址就不再有效了TestInit()
函数 returns.