Malloc vs新 – 不同的填充

我正在为使用MPI进行高性能计算(10 ^ 5 – 10 ^ 6内核)的项目审查其他人的C ++代码。 该代码旨在允许不同体系结构(可能)不同机器之间的通信。 他写了一条评论,内容如下:

我们通常会使用newdelete ,但是在这里我使用mallocfree 。 这是必要的,因为一些编译器会在使用new时候填充不同的数据,导致在不同平台之间传输数据的错误。 malloc不会发生这种情况。

这不符合我从标准的newmalloc问题中所知道的任何东西。

new / delete和malloc / free有什么区别? 暗示编译器能够以不同的方式计算对象的大小(但为什么与使用sizeof不同呢?)。

malloc&placement new与new是一个相当stream行的问题,但是只讨论malloc不使用的new构造malloc ,这与此无关。

malloc如何理解alignment? 说,内存保证与newmalloc正确的alignment,这是我以前的想法。

我的猜测是,他在过去的某个时间误诊了自己的bug,并推断newmalloc给出了不同数量的填充,我认为这可能是不正确的。 但是我找不到Google或者之前任何问题的答案。

帮助我,StackOverflow,你是我唯一的希望!

IIRC有一个挑剔点。 malloc保证返回任何标准typesalignment的地址。 ::operator new(n)只能保证返回一个不超过n的标准typesalignment的地址,如果T不是一个字符types,那么只需要new T[n]返回一个与Talignment的地址。

但是,这只有在玩特定于实现的技巧时才有意义,比如使用指针的底部几位来存储标志,或者依靠地址来实现比严格需要更多的alignment。

它不会影响对象内的填充,不pipe分配占用的内存如何,其布局必须完全相同。 所以很难看出差异如何导致传输数据的错误。

有没有什么迹象表明这个评论的作者对堆栈或全局variables的对象有什么看法,无论他认为它们是“像malloc一样填充还是像新填充一样”? 这可能会给出这个想法从何而来的线索。

也许他感到困惑,但也许他所说的代码不仅仅是malloc(sizeof(Foo) * n)new Foo[n]之间的直线差别。 也许更像是:

 malloc((sizeof(int) + sizeof(char)) * n); 

 struct Foo { int a; char b; } new Foo[n]; 

也就是说,也许他 “我使用malloc”,但意思是 “我手动将数据打包到未alignment的位置,而不是使用结构”。 实际上malloc是不需要手动打包的结构,但没有意识到这是一个较小程度的混淆。 有必要定义通过线路发送的数据布局。 当使用结构时,不同的实现将填充不同的数据。

你的同事可能已经有了new[]/delete[]的魔法cookie(这是删除数组时执行的信息)。 但是,如果使用由new[]返回的地址开始的分配(与分配器相对),这将不会成为问题。

包装似乎更有可能。 ABI中的变化可能(例如)导致结尾处添加不同数量的尾随字节(这受到alignment的影响,也考虑到数组)。 使用malloc,可以指定结构的位置,从而更容易移植到外国的ABI。 通常通过指定传输结构的alignment和包装来防止这些变化。

对象的布局不能取决于它是使用malloc还是new分配的。 它们都返回相同types的指针,当你将这个指针传递给其他函数时,它们将不知道该对象是如何分配的。 sizeof *ptr只是依赖于ptr的声明,而不是如何分配的。

我想你是对的。 填充是由编译器不是newmalloc 。 即使你声明了一个数组或结构体而不使用newmalloc ,填充的考虑也是适用的。 在任何情况下,当我看到newmalloc不同实现在平台之间移植代码时会产生什么问题,我完全不会看到它们如何导致在平台之间传输数据的问题。

当我想控制我的普通旧数据结构的布局时,使用MS Visual编译器,我使用#pragma pack(1) 。 我想大多数编译器都支持这样的预编译器指令,例如gcc 。

这有一个在另一个后面的结构的所有领域的结果,没有空的空间。

如果另一端的平台做同样的事情(即编译它的数据交换结构,填充为1),那么两边检索的数据都很合适。 所以我从来不用在C ++中玩malloc。

在最坏的情况下,我会考虑重载新操作符,因为它执行一些棘手的事情,而不是直接在C ++中使用malloc。

这是我猜这个东西从哪里来的。 正如你所提到的,问题在于通过MPI传输数据。

就我个人而言,对于我想要通过MPI发送/接收的复杂数据结构,我总是实现序列化/反序列化方法,将整个事件打包到/从一个字符数组中解压缩。 现在,由于填充,我们知道结构的大小可能大于其成员的大小,因此还需要计算数据结构的未填充大小,以便知道正在发送/接收的字节数。

例如,如果你想用上述技术发送/接收std::vector<Foo> A ,那么假设结果字符数组的大小通常是A.size()*sizeof(Foo)是错误的。 换句话说,每个实现serialize / deserialize方法的类都应该实现一个报告数组大小的方法(或者更好地将数组存储在容器中)。 这可能成为一个错误背后的原因。 不pipe怎样,这个与new vs malloc没有任何关系,正如在这个线程中指出的那样。

在c ++中: new关键字用于为某些数据结构分配一些特定的内存字节。 例如,你已经定义了一些类或结构,并且你想为它的对象分配内存。

 myclass *my = new myclass(); 

要么

 int *i = new int(2); 

但在任何情况下,您都需要定义的数据types(类,结构体,联合体,整型,字符等),并且只分配其对象/variables所需的内存字节。 (即:该数据types的倍数)。

但是,如果使用malloc()方法,则可以分配任何内存字节,并且不需要始终指定数据types。 在这里你可以观察到malloc()的几种可能性:

 void *v = malloc(23); 

要么

 void *x = malloc(sizeof(int) * 23); 

要么

 char *c = (char*)malloc(sizeof(char)*35); 

malloc是一种函数,new是c ++中的一种数据types,如果我们使用malloc比我们必须要使用types转换,否则编译器给你错误,如果我们使用新的数据types分配内存比我们没有必要到types转换