Estoy revisando el código C ++ de otra persona para nuestro proyecto que usa MPI para computación de alto rendimiento (10 ^ 5 - 10 ^ 6 núcleos). El código está destinado a permitir las comunicaciones entre (potencialmente) diferentes máquinas en diferentes arquitecturas. Ha escrito un comentario que dice algo parecido a:
Normalmente usamos
new
ydelete
, pero aquí estoy usandomalloc
yfree
. Esto es necesario porque algunos compiladores rellenarán los datos de manera diferente cuandonew
se utilicen, lo que provocará errores en la transferencia de datos entre diferentes plataformas. Esto no sucede conmalloc
.
Esto no encaja con nada de lo que sé de las preguntas estándar new
frente a las malloc
preguntas.
¿Cuál es la diferencia entre new / delete y malloc / free? insinúa la idea de que el compilador podría calcular el tamaño de un objeto de manera diferente (pero entonces, ¿por qué eso difiere del uso sizeof
?).
malloc y ubicación new vs new es una pregunta bastante popular, pero solo habla sobre el new
uso de constructores donde malloc
no lo hace, lo cual no es relevante para esto.
¿Cómo entiende malloc la alineación? dice que se garantiza que la memoria se alineará correctamente con cualquiera de los dos new
o malloc
que es lo que pensaba anteriormente.
Supongo que diagnosticó erróneamente su propio error en el pasado y lo dedujo new
y le malloc
dio diferentes cantidades de relleno, lo que creo que probablemente no sea cierto. Pero no encuentro la respuesta en Google ni en ninguna pregunta anterior.
Ayúdame, StackOverflow, ¡eres mi única esperanza!
fuente
malloc
ynew
, comonew
en algunos entornos asignan un bloque, agrega algunos datos al principio y devuelve un puntero a una ubicación justo después de estos datos. (Estoy de acuerdo con los demás, dentro del bloque de datos,malloc
ynew
debo usar el mismo tipo de relleno).Respuestas:
IIRC hay un punto delicado.
malloc
se garantiza que devolverá una dirección alineada para cualquier tipo estándar.::operator new(n)
solo se garantiza que devuelva una dirección alineada para cualquier tipo estándar no mayor que n , y siT
no es un tipo de carácter,new T[n]
solo se requiere que devuelva una dirección alineada paraT
.Pero esto solo es relevante cuando está jugando trucos específicos de implementación, como usar los pocos bits inferiores de un puntero para almacenar banderas, o confiar en la dirección para tener más alineación de la estrictamente necesaria.
No afecta el relleno dentro del objeto, que necesariamente tiene exactamente el mismo diseño, independientemente de cómo haya asignado la memoria que ocupa. Por lo tanto, es difícil ver cómo la diferencia podría resultar en errores en la transferencia de datos.
¿Hay alguna señal de lo que el autor de ese comentario piensa sobre los objetos en la pila o en los globales, ya sea que en su opinión estén "acolchados como malloc" o "acolchados como nuevos"? Eso podría dar pistas sobre el origen de la idea.
Tal vez está confundido, pero tal vez el código que está hablando es más que una diferencia recta entre
malloc(sizeof(Foo) * n)
vsnew Foo[n]
. Tal vez sea más como:vs.
Es decir, tal vez esté diciendo "yo uso malloc", pero significa "empaqueto manualmente los datos en ubicaciones no alineadas en lugar de utilizar una estructura". En realidad,
malloc
no es necesario para empaquetar manualmente la estructura, pero no darse cuenta de eso es un grado menor de confusión. Es necesario definir el diseño de los datos enviados por cable. Las diferentes implementaciones rellenarán los datos de manera diferente cuando se use la estructura .fuente
char
matrices nunca se rellenan, así que me quedo con "confuso" como explicación.Es posible que su colega haya tenido
new[]/delete[]
en mente la cookie mágica (esta es la información que usa la implementación al eliminar una matriz). Sin embargo, esto no habría sido un problema sinew[]
se usara la asignación que comienza en la dirección devuelta por (a diferencia de la del asignador).Empacar parece más probable. Las variaciones en las ABI podrían (por ejemplo) resultar en un número diferente de bytes finales agregados al final de una estructura (esto está influenciado por la alineación, también considere las matrices). Con malloc, se podría especificar la posición de una estructura y, por lo tanto, se podría transportar más fácilmente a una ABI extranjera. Estas variaciones normalmente se evitan especificando la alineación y el empaquetamiento de las estructuras de transferencia.
fuente
El diseño de un objeto no puede depender de si se asignó mediante
malloc
onew
. Ambos devuelven el mismo tipo de puntero, y cuando pasa este puntero a otras funciones, no sabrán cómo se asignó el objeto.sizeof *ptr
depende de la declaración deptr
, no de cómo se asignó.fuente
Creo que tienes razón. El relleno lo realiza el compilador, no
new
omalloc
. Las consideraciones de relleno se aplicarían incluso si declarara una matriz o estructura sin usarnew
omalloc
en absoluto. En cualquier caso, si bien puedo ver cómo las diferentes implementacionesnew
ymalloc
podrían causar problemas al portar código entre plataformas, no veo cómo podrían causar problemas al transferir datos entre plataformas.fuente
new
como un buen envoltorio para,malloc
pero parece que por otras respuestas eso no es del todo cierto. El consenso parece ser que el acolchado debería ser el mismo con ambos; Creo que el problema con la transferencia de datos entre plataformas solo surge si su mecanismo de transferencia es defectuoso :)Cuando quiero controlar el diseño de mi estructura de datos antigua simple, utilizo compiladores de MS Visual
#pragma pack(1)
. Supongo que dicha directiva de precompilador es compatible con la mayoría de los compiladores, como por ejemplo gcc .Esto tiene la consecuencia de alinear todos los campos de las estructuras uno detrás del otro, sin espacios vacíos.
Si la plataforma del otro extremo hace lo mismo (es decir, compiló su estructura de intercambio de datos con un relleno de 1), entonces los datos recuperados en ambos lados encajan bien. Por lo tanto, nunca tuve que jugar con malloc en C ++.
En el peor de los casos, habría considerado sobrecargar el nuevo operador para que realice algunas cosas complicadas, en lugar de usar malloc directamente en C ++.
fuente
pragma pack
o similares? Me doy cuenta de que no será parte del estándar.Esta es mi loca suposición de de dónde viene esta cosa. Como mencionaste, el problema está en la transmisión de datos a través de MPI.
Personalmente, para mis complicadas estructuras de datos que quiero enviar / recibir a través de MPI, siempre implemento métodos de serialización / deserialización que empaquetan / descomprimen todo en / desde una matriz de caracteres. Ahora, debido al relleno, sabemos que ese tamaño de la estructura podría ser mayor que el tamaño de sus miembros y, por lo tanto, también es necesario calcular el tamaño sin relleno de la estructura de datos para saber cuántos bytes se envían / reciben.
Por ejemplo, si desea enviar / recibir a
std::vector<Foo> A
través de MPI con dicha técnica, es incorrecto asumir que el tamaño de la matriz resultante de caracteres esA.size()*sizeof(Foo)
en general. En otras palabras, cada clase que implementa métodos de serialización / deserialización, también debe implementar un método que informe el tamaño de la matriz (o mejor aún, almacenar la matriz en un contenedor). Esta podría convertirse en la razón detrás de un error. De una forma u otra, sin embargo, eso no tiene nada que ver connew
vsmalloc
como se señala en este hilo.fuente
En c ++: la
new
palabra clave se usa para asignar algunos bytes particulares de memoria con respecto a alguna estructura de datos. Por ejemplo, ha definido alguna clase o estructura y desea asignar memoria para su objeto.o
Pero en todos los casos, necesita el tipo de datos definido (clase, estructura, unión, int, char, etc.) y solo se asignarán los bytes de memoria que se requieren para su objeto / variable. (es decir, múltiplos de ese tipo de datos).
Pero en el caso del método malloc (), puede asignar cualquier byte de memoria y no necesita especificar el tipo de datos en todo momento. Aquí puedes observarlo en pocas posibilidades de malloc ():
o
o
fuente
malloc es un tipo de función y new es un tipo de tipo de datos en c ++ en c ++, si usamos malloc, entonces debemos y debemos usar typecast; de lo contrario, el compilador le dará un error y si usamos un nuevo tipo de datos para la asignación de memoria, entonces no necesitamos encasillar
fuente