suivant extrait de ici
pw = (widget *)malloc(sizeof(widget));
alloue le stockage brut. En effet, l'appel malloc alloue de la mémoire c'est assez grand et bien aligné pour contenir un objet de type widget
voir aussi rapide pImpl de sutter d'herbe, il a dit:
Alignement. Tout alignement de mémoire. Toute mémoire allouée dynamiquement via new ou malloc garantit l’alignement correct pour les objets de tout type, mais les tampons qui ne sont pas alloués dynamiquement n'ont pas une telle garantie
Je suis curieux à ce sujet. Comment Malloc connaît-il l'alignement du type personnalisé?
Les exigences d'alignement sont récursives: l'alignement de tout struct
est simplement le plus grand alignement de l'un de ses membres, et cela est compris de manière récursive.
Par exemple, et en supposant que l'alignement de chaque type fondamental soit égal à sa taille (ce n'est pas toujours vrai en général), le struct X { int; char; double; }
a l'alignement de double
et sera complété par un multiple de la taille du double (par exemple 4 (int ), 1 (caractère), 3 (remplissage), 8 (double)). Le struct Y { int; X; float; }
a l'alignement X
, qui est le plus grand et égal à l'alignement double
, et Y
est aménagé en conséquence: 4 (int), 4 (remplissage), 16 (X), 4 (float), 4 (remplissage ).
(Tous les chiffres ne sont que des exemples et peuvent varier selon votre machine.)
Par conséquent, en décomposant les types fondamentaux, il suffit de connaître quelques alignements fondamentaux, et parmi ceux-ci, le plus grand est bien connu. C++ définit même un type max_align_t
dont l'alignement est cet alignement le plus grand.
Tout ce que malloc()
doit faire est de choisir une adresse qui est un multiple de cette valeur.
Je pense que la partie la plus pertinente de la citation de Herb Sutter est la partie que j'ai marquée en gras:
Alignement. Tout alignement de mémoire. Toute mémoire allouée dynamiquement via new ou malloc est garantie pour être correctement alignée pour les objets de tout type, mais les tampons qui ne sont pas alloués dynamiquement n’ont pas cette garantie.
Il n'a pas besoin de savoir quel type vous avez à l'esprit, car il s'aligne pour le type any. Sur un système donné, la taille maximale de l'alignement est toujours nécessaire ou significative; Par exemple, un système comportant des mots de quatre octets aura probablement un alignement maximal de quatre octets.
Ceci est également expliqué par la page de manuel malloc(3)
, qui indique en partie:
Les fonctions
malloc()
etcalloc()
renvoient un pointeur sur la mémoire allouée, qui est correctement aligné pour tout type de variable.
La seule information que malloc()
peut utiliser est la taille de la demande qui lui est transmise. En général, vous pouvez arrondir la taille passée à la plus grande puissance égale (ou égale à la plus proche) et aligner la mémoire en fonction de cette valeur. Il y aurait probablement aussi une limite supérieure sur la valeur d'alignement, telle que 8 octets.
Ce qui précède est une discussion hypothétique et l’implémentation réelle dépend de l’architecture de la machine et de la bibliothèque d’exécution que vous utilisez. Peut-être que votre malloc()
renvoie toujours des blocs alignés sur 8 octets et qu'il n'a jamais rien à faire d'autre.
1) Alignez le plus petit commun multiple de tous les alignements. par exemple. si ints nécessite un alignement de 4 octets, mais que les pointeurs en exigent 8, attribuez tout à un alignement de 8 octets. Cela provoque tout pour être aligné.
2) Utilisez l'argument de taille pour déterminer le bon alignement. Pour les petites tailles, vous pouvez en déduire que le type, tel que malloc(1)
(en supposant que les tailles de types ne soient pas 1), est toujours un caractère. C++ new
présente l'avantage d'être sûr de la saisie et peut donc toujours prendre des décisions d'alignement de cette façon.
Avant C++ 11, l'alignement était traité assez simplement en utilisant le plus grand alignement où la valeur exacte était inconnue et où malloc/calloc fonctionnait toujours de cette manière. Cela signifie que l'allocation malloc est correctement alignée pour tous les types.
Un mauvais alignement peut entraîner un comportement indéfini selon la norme, mais j’ai vu des compilateurs x86 généreux et punis uniquement par des performances inférieures.
Notez que vous pouvez également modifier l’alignement via les options du compilateur ou les directives. (Pragma Pack pour VisualStudio par exemple).
Mais lorsqu'il s'agit de placement new , C++ 11 nous apporte de nouveaux mots clés appelés alignof et alignas. Voici un code qui montre l'effet si l'alignement maximal du compilateur est supérieur à 1. Le premier placement nouveau ci-dessous est automatiquement bon, mais pas le second.
#include <iostream>
#include <malloc.h>
using namespace std;
int main()
{
struct A { char c; };
struct B { int i; char c; };
unsigned char * buffer = (unsigned char *)malloc(1000000);
long mp = (long)buffer;
// First placment new
long alignofA = alignof(A) - 1;
cout << "alignment of A: " << std::hex << (alignofA + 1) << endl;
cout << "placement address before alignment: " << std::hex << mp << endl;
if (mp&alignofA)
{
mp |= alignofA;
++mp;
}
cout << "placement address after alignment : " << std::hex <<mp << endl;
A * a = new((unsigned char *)mp)A;
mp += sizeof(A);
// Second placment new
long alignofB = alignof(B) - 1;
cout << "alignment of B: " << std::hex << (alignofB + 1) << endl;
cout << "placement address before alignment: " << std::hex << mp << endl;
if (mp&alignofB)
{
mp |= alignofB;
++mp;
}
cout << "placement address after alignment : " << std::hex << mp << endl;
B * b = new((unsigned char *)mp)B;
mp += sizeof(B);
}
Je suppose que les performances de ce code peuvent être améliorées avec certaines opérations au niveau des bits.
ÉDITER: remplacement du calcul modulo coûteux par des opérations au niveau des bits. Espérant toujours que quelqu'un trouve quelque chose encore plus vite.
malloc n'a aucune connaissance de ce qu'il alloue, car son paramètre est simplement la taille totale . Il s'aligne simplement sur un alignement sûr pour tout objet.