Malloc vs new - различная прокладка

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

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

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

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

malloc и размещение new vs. new являются довольно популярным вопросом, но говорят только о new с использованием конструкторов, где malloc нет, а это не относящихся к этому.

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

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

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

Ответ 1

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

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

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

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

Может быть, он запутался, но, возможно, код, о котором он говорит, больше, чем прямое различие между 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 не требуется, чтобы вручную упаковать структуру, но не понимая, что это меньшая степень замешательства. Необходимо определить макет данных, отправленный по проводу. Различные реализации будут обрабатывать данные по-разному, когда используется структура.

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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

Ответ 7

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

myclass *my = new myclass();

или

int *i = new int(2);

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

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

void *v = malloc(23);

или

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

или

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

Ответ 8

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