重新发一贴,关于上一篇 C++ 代码中,结构体析构导致内存异常的问题,我做了更多的测试

360 天前
 villivateur

上一篇是 https://v2ex.com/t/941007

结构体定义:

struct ModuleConfig
{
	ModuleConfig()
	{
		printf("ModuleConfig::constructor\n");
	}
	~ModuleConfig()
	{
		printf("ModuleConfig::destructor\n");
	}
	
	uint32_t identity;
	std::string pdoMapName;
	uint32_t pdoMapInOffset;
	uint32_t pdoMapOutOffset;
};

调用并崩溃的代码:

void ESI_SetModuleIdentities(int slaveId, std::vector<uint32_t>& moduleIdentities)
{
	
	ModuleConfig* newModule = new ModuleConfig;
	printf("line: %d\n", __LINE__); // 917
	newModule->identity = 243423;
	printf("EEEEE %d\n", newModule->identity);
	delete newModule;
	printf("line: %d\n", __LINE__); // 921
	newModule = NULL;
	printf("line: %d\n", __LINE__); // 923

	SlaveFileConfig* config = database[slaveId];
	config->SetModuleIdentities(moduleIdentities);
}

直接运行后的打印输出:

ModuleConfig::constructor
line: 917
EEEEE 243423
ModuleConfig::destructor

(只有这么多,打完这些就崩溃)

使用 valgrind 调试,相关信息如下:

ModuleConfig::constructor
line: 917
EEEEE 243423
ModuleConfig::destructor
==8820== Conditional jump or move depends on uninitialised value(s)
==8820==    at 0x49D65CE: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==8820==    by 0x170615: ModuleConfig::~ModuleConfig() (SlaveConfigParser.h:26)
==8820==    by 0x17F992: ESI_SetModuleIdentities(int, std::vector<unsigned int, std::allocator<unsigned int> >&) (SlaveConfigParser.cpp:920)
==8820==    by 0x161637: EcatAdapter::InitSlaveFileConfig() (EcatAdapter.cpp:1321)
==8820==    by 0x15F583: EcatAdapter::ConfigSlaves() (EcatAdapter.cpp:762)
==8820==    by 0x15DB78: EcatAdapter::Connect() (EcatAdapter.cpp:247)
==8820==    by 0x15C4D4: AdapterManager::Connect() (AdapterManager.cpp:250)
==8820==    by 0x15C0F6: AdapterManager::Run() (AdapterManager.cpp:151)
==8820==    by 0x15CA7F: StartAdapter (CExport.cpp:22)
==8820==    by 0x18F8CA: main (main.c:5)
==8820==  Uninitialised value was created by a heap allocation
==8820==    at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==8820==    by 0x17F939: ESI_SetModuleIdentities(int, std::vector<unsigned int, std::allocator<unsigned int> >&) (SlaveConfigParser.cpp:916)
==8820==    by 0x161637: EcatAdapter::InitSlaveFileConfig() (EcatAdapter.cpp:1321)
==8820==    by 0x15F583: EcatAdapter::ConfigSlaves() (EcatAdapter.cpp:762)
==8820==    by 0x15DB78: EcatAdapter::Connect() (EcatAdapter.cpp:247)
==8820==    by 0x15C4D4: AdapterManager::Connect() (AdapterManager.cpp:250)
==8820==    by 0x15C0F6: AdapterManager::Run() (AdapterManager.cpp:151)
==8820==    by 0x15CA7F: StartAdapter (CExport.cpp:22)
==8820==    by 0x18F8CA: main (main.c:5)
==8820== 
==8820== Conditional jump or move depends on uninitialised value(s)
==8820==    at 0x483CF75: operator delete(void*) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==8820==    by 0x170615: ModuleConfig::~ModuleConfig() (SlaveConfigParser.h:26)
==8820==    by 0x17F992: ESI_SetModuleIdentities(int, std::vector<unsigned int, std::allocator<unsigned int> >&) (SlaveConfigParser.cpp:920)
==8820==    by 0x161637: EcatAdapter::InitSlaveFileConfig() (EcatAdapter.cpp:1321)
==8820==    by 0x15F583: EcatAdapter::ConfigSlaves() (EcatAdapter.cpp:762)
==8820==    by 0x15DB78: EcatAdapter::Connect() (EcatAdapter.cpp:247)
==8820==    by 0x15C4D4: AdapterManager::Connect() (AdapterManager.cpp:250)
==8820==    by 0x15C0F6: AdapterManager::Run() (AdapterManager.cpp:151)
==8820==    by 0x15CA7F: StartAdapter (CExport.cpp:22)
==8820==    by 0x18F8CA: main (main.c:5)
==8820==  Uninitialised value was created by a heap allocation
==8820==    at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==8820==    by 0x17F939: ESI_SetModuleIdentities(int, std::vector<unsigned int, std::allocator<unsigned int> >&) (SlaveConfigParser.cpp:916)
==8820==    by 0x161637: EcatAdapter::InitSlaveFileConfig() (EcatAdapter.cpp:1321)
==8820==    by 0x15F583: EcatAdapter::ConfigSlaves() (EcatAdapter.cpp:762)
==8820==    by 0x15DB78: EcatAdapter::Connect() (EcatAdapter.cpp:247)
==8820==    by 0x15C4D4: AdapterManager::Connect() (AdapterManager.cpp:250)
==8820==    by 0x15C0F6: AdapterManager::Run() (AdapterManager.cpp:151)
==8820==    by 0x15CA7F: StartAdapter (CExport.cpp:22)
==8820==    by 0x18F8CA: main (main.c:5)
==8820== 
==8820== Invalid free() / delete / delete[] / realloc()
==8820==    at 0x483CFBF: operator delete(void*) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==8820==    by 0x170615: ModuleConfig::~ModuleConfig() (SlaveConfigParser.h:26)
==8820==    by 0x17F992: ESI_SetModuleIdentities(int, std::vector<unsigned int, std::allocator<unsigned int> >&) (SlaveConfigParser.cpp:920)
==8820==    by 0x161637: EcatAdapter::InitSlaveFileConfig() (EcatAdapter.cpp:1321)
==8820==    by 0x15F583: EcatAdapter::ConfigSlaves() (EcatAdapter.cpp:762)
==8820==    by 0x15DB78: EcatAdapter::Connect() (EcatAdapter.cpp:247)
==8820==    by 0x15C4D4: AdapterManager::Connect() (AdapterManager.cpp:250)
==8820==    by 0x15C0F6: AdapterManager::Run() (AdapterManager.cpp:151)
==8820==    by 0x15CA7F: StartAdapter (CExport.cpp:22)
==8820==    by 0x18F8CA: main (main.c:5)
==8820==  Address 0x4ff42e800000000 is not stack'd, malloc'd or (recently) free'd
==8820== 
line: 921
line: 923

环境:g++ 9.4.0 / Ubuntu 20.04 / c++11

std::string pdoMapName 改成 std::string pdoMapName{} 也不行

2383 次点击
所在节点    C++
28 条回复
cnbatch
360 天前
其实可以换个没这种 bug 的编译器吧,嵌入式设备的环境又不是必须固定使用 Ubuntu 20.04
Rothschild
360 天前
@saturn7 恰恰是 c++需要的开发地方,对齐内存是很常见的事儿,这不算魔改算日常操作
exch4nge
360 天前
按理来说这种情况编译器应该有 warning 的,我也没环境无法确定,也可能是用参数忽略掉了这种 warning ,也可能是编译过程有大量 warning 输出大家都不看。如果这版本 gcc 真没有(可能性很小)那建议升级或换 clang 。
junmoxiao
360 天前
@loveumozart 有一说一不是 c++的锅,自己改了内存对齐能怪谁呢
saturn7
360 天前
@Rothschild 都用到 stl ,class ,object 了,修改内存对齐肯定不是日常操作呀!!!正常项目代码编程时,注意减少对象内存复制,基本可以减少硬件 90%内存压力。如果真是要求内存极限使用场景,正确是做法是根据自己公司业务,用 C/C++封装一套自己的数据容器才是明智的做法。我认为用到 c++,还去结构体还要修改内存对齐的方式,肯定算是铤而走险做法。
Rothschild
360 天前
@saturn7 这只能说明你压根就没在过什么 C++的组里干过,内存对齐在网络传输序列化反序列化以及嵌入式行业是必须的,而且无处不在,你在任何大点 c++项目的代码里都能看到这些
saturn7
357 天前
@Rothschild 用过 google protobuf2/3 ,modbus, focas, 没见过那一个协议官方文档建议开发者手动改 pragma 对齐。不过既然是用网络流通信了,发送 /接收和内存对齐有毛关系,通用做法不都是大端字节序编码?
Rothschild
357 天前
@saturn7 所以说你见得太少了啊,你为什么以为大型项目里只是那点 crud 的代码?你不会在 github 里搜一下#pragma pack(push,1)看看用的多频繁?

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

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

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

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

© 2021 V2EX