当我们在C/C++中定义动态数组时,它使用头段来跟踪数组中的元素数量(在堆中)。例如:
int* mem = new int[8];
编译器将分配sizeof(int)*8个字节。
int *temp = malloc(sizeof(int)*9)
将"8"存储在(int)字节的第一个大小中,它就像一样
*temp = 8;
然后将mem地址设置为temp的下一个连续地址
mem = temp + 1;
因此,mem将指向一个由8个元素组成的数组,而不是9个。
当删除发生时,编译器将在与上述相反的过程中使用mem来释放堆块中的内存
delete[] mem;
我的问题是
如果我们分配一个动态内存,该内存将在运行时用于不同的模块,并且我们使用分配的堆内存的头段成功检索了大量元素,那么在多线程环境中使用安全吗?
(请假设在程序设计的每个模块中,没有提供任何辅助函数或属性来检索定义的动态数组中的元素数量(size)。每个模块只向数组传递地址(指针),但不传递其大小)
不,它在任何环境中都不安全。不能依赖编译器在分配的内存之前存储数组中的元素数量,因为这不是定义的行为。你永远不应该试图达到这个值。
相反,甚至不要使用动态数组,而是选择std::vector
。
访问mem - 1
有未定义的行为。
编译器将。。。
如果您的目标是那个版本的编译器,并且只是那个版本,并且编译器供应商记录了行为,那么也许您可以相信编译器的版本确实如您所观察到的那样。
但是,依赖它的程序将不可移植到其他编译器,可能是同一编译器的其他版本,也可能是使用同一编译器相同版本的其他平台。
这一切都与多线程与单线程无关。
如果我们分配一个动态内存,该内存将在运行时用于不同的模块,并且我们设法使用分配的堆内存的头段来检索元素数量,那么在多线程环境中使用安全吗?
在多线程程序中使用动态内存是安全的,就像使用任何内存一样安全。当然,多线程意味着数据竞争的可能性。您必须将修改同步到其他线程可能同时访问的任何对象。对象是否在动态内存中没有区别。
使用未记录的系统信息原则上是不可能的,即使它看起来有效:它在意外情况下可能会表现出不同的行为,而且无法保证可移植性,即使是在同一制造商之间。
如果你无论如何都想玩匹配,请确保在你读取大小的那一刻到你访问块的那一时刻之间,没有其他线程执行与内存分配相关的操作;为此,您需要实现一个关键部分。
我用Visual Studio发布版本测试了这一点
int * mem = new int[8];
导致对malloc的调用相当于
int * mem = malloc(32); // 32 = 8 * sizeof(int)
整数数组的大小不会存储在任何地方(据我所知,包括malloc标头,malloc标题只有某种类型的链接/标志,用于重新组合从堆中分配(和虚拟映射)的块)。