Malloc vs new-разные прокладки


я просматриваю чужой код на C++ для нашего проекта, который использует MPI для высокопроизводительных вычислений (10^5 - 10^6 ядер). Код предназначен для обеспечения связи между (потенциально) различными машинами на разных архитектурах. Он написал комментарий, который говорит что-то вроде:

мы обычно используем new и delete, но здесь я использую malloc и free. Это необходимо, потому что некоторые компиляторы будут заполнять данные по-разному когда new используется, что приводит к ошибкам в передаче данных между различными платформами. Это не происходит с malloc.

это не соответствует ничему, что я знаю от standard new vs malloc вопросы.

в чем разница между new/delete и malloc/free? намекает на мысль, что компилятор не может вычислить размер объекта по-разному (но тогда почему это отличается от использования sizeof?).

malloc & размещение новый против нового-довольно популярный вопрос, но только говорит о new использование конструкторов, где malloc нет, что не имеет отношения к этому.

как Маллок понимает выравнивание? говорит, что память гарантированно будет правильно выровнен с new или malloc это то, что я раньше думал.

Я думаю, что он неправильно диагностировал свою собственную ошибку некоторое время назад и сделал вывод, что new и malloc дайте различные количества прокладки, которые я думаю, вероятно, это не так. Но я не могу найти ответ с помощью Google или в любом предыдущем вопросе.

помоги мне, StackOverflow, ты моя единственная надежда!

8 108

8 ответов:

IIRC есть один придирчивый момент. malloc гарантированно возвращает адрес, выровненный для любого стандартного типа. ::operator new(n) гарантируется только возврат адреса, выровненного для любого стандартного типа не больше n, а если T это не тип символа тогда new T[n] требуется только для возврата адреса, выровненного для T.

но это актуально только тогда, когда вы играете специфические для реализации трюки, такие как использование нижних нескольких бит указателя для хранения флаги, или иначе полагаясь на адрес, чтобы иметь больше выравнивания, чем это строго необходимо.

это не влияет на заполнение внутри объекта, который обязательно имеет точно такой же макет независимо от того, как вы выделили память, которую он занимает. Поэтому трудно понять, как разница может привести к ошибкам при передаче данных.

есть ли какой-либо признак того, что автор этого комментария думает об объектах в стеке или в глобалах, независимо от того, по его мнению, они " дополнены, как Танос" или "мягкий как новый"? Это может дать ключ к разгадке, откуда взялась идея.

может быть, он запутался, но, возможно, код, о котором он говорит, больше, чем прямая разница между malloc(sizeof(Foo) * n) vs new Foo[n]. Может быть, это больше похоже на:

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

и

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

то есть, может быть, он слова "я использую malloc", но означает "я вручную упаковываю данные в невыровненные места вместо использования структуры". На самом деле malloc is не требуется для того, чтобы вручную упаковать структуру, но не понимая, что это меньшая степень путаницы. Надо определить формат данных, передаваемых по сети. Различные реализации будут заполнять данные по-разному, когда struct это.

ваш коллега, возможно, имел new[]/delete[]волшебный cookie в виду (это информация, которую реализация использует при удалении массива). Однако это не было бы проблемой, если бы выделение начиналось с адреса, возвращенного new[] были использованы (в отличие от распределителя).

упаковка кажется более вероятным. Вариации в ABIs могут (например) привести к различному количеству конечных байтов, добавленных в конце структуры (на это влияет выравнивание, также рассмотрим массивы). С malloc, положение структуры смогло быть определено и таким образом более легко портативно к чужому ABI. Эти изменения обычно предотвращаются путем указания выравнивания и упаковки передаточных структур.

макет объекта не может зависеть от того, был ли он выделен с помощью malloc или new. Они оба возвращают один и тот же указатель, и когда вы передаете этот указатель другим функциям, они не будут знать, как был выделен объект. sizeof *ptr просто зависит от объявления ptr, не так, как это было назначено.

Я думаю, что вы правы. Заполнение выполняется компилятором не new или malloc. Соображения заполнения будут применяться, даже если вы объявили массив или структуру без использования new или malloc на всех. В любом случае, пока я вижу, как разные реализации new и malloc может вызвать проблемы при переносе кода между платформами, я полностью не вижу, как они могут вызвать проблемы с передачей данных между платформами.

когда я хочу контролировать макет моей простой старой структуры данных, с помощью MS Visual compilers я использую #pragma pack(1). Я полагаю, что такая директива precompiler поддерживается для большинства компиляторов, например gcc.

это приводит к выравниванию всех полей структур друг за другом, без пустых пространств.

Если платформа на другом конце делает то же самое ( т. е. скомпилировала свою структуру обмена данными с дополнением 1), то данные восстановленные с обеих сторон justs хорошо подходит. Таким образом, мне никогда не приходилось играть с malloc в C++.

в худшем случае я бы рассмотрел перегрузку нового оператора, так как он выполняет некоторые сложные вещи, а не использует malloc непосредственно в C++.

Это я догадываюсь, откуда это идет. Как вы уже упоминали, проблема заключается в передаче данных через MPI.

лично для моих сложных структур данных, которые я хочу отправлять/получать через MPI, я всегда реализую методы сериализации/десериализации, которые упаковывают/распаковывают все это в/из массива символов. Теперь из-за заполнения мы знаем, что этот размер структуры может быть больше, чем размер ее членов, и поэтому также необходимо рассчитать неупакованный размер структуры данных, чтобы мы знали, сколько байтов отправляется / принимается.

например, если вы хотите отправить/получить std::vector<Foo> A над MPI с помощью указанной техники неверно предполагать, что размер результирующего массива символов равен A.size()*sizeof(Foo) в целом. Другими словами, каждый класс, реализующий методы serialize/deserialize, должен также реализовать метод, который сообщает размер массива (или еще лучше хранить массив в контейнере). Это может стать причина за ошибкой. Так или иначе, но это не имеет никакого отношения к new vs malloc как указано в этой теме.

в c++:newключевое слово используется для выделения некоторых конкретных байтов памяти по отношению к некоторой структуре данных. Например, вы определили некоторый класс или структура, и вы хотите выделить память для объекта.

myclass *my = new myclass();

или

int *i = new int(2);

но во всех случаях вам нужен определенный тип данных (class, struct, union, int, char и т. д...) и только тот байт памяти будет выделен, который требуется для его объекта / переменной. (т. е. кратные этот тип данных).

но в случае метода malloc () вы можете выделить любые байты памяти, и вам не нужно указывать тип данных в любое время. Здесь вы можете наблюдать это в нескольких возможностях malloc ():

void *v = malloc(23);

или

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

или

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

malloc-это тип функции и new-это тип типа данных в c++ в c++, если мы используем malloc, чем мы должны и должны использовать typecast в противном случае компилятор даст вам ошибку и если мы используем новый тип данных для выделения памяти, то нам не нужно набирать