Comment malloc comprend-il l’alignement?

après extrait d’ ici

pw = (widget *)malloc(sizeof(widget)); 

alloue le stockage brut. En effet, l’appel malloc alloue un stockage suffisamment grand et convenablement aligné pour contenir un object de type widget.

voir également pImpl rapide de louche d’herbe, il a dit:

Alignement Tout alignement de mémoire. Toute mémoire allouée dynamicment via new ou malloc est garantie être correctement alignée pour les objects de tout type, mais les tampons qui ne sont pas alloués dynamicment ne sont pas garantis

Je suis curieux à ce sujet, comment malloc sait-il l’alignement du type personnalisé?

    Les exigences d’alignement sont récursives: l’alignement de n’importe quelle struct est simplement le plus grand alignement de l’un de ses membres, ce qui est compris de manière récursive.

    Par exemple, et en supposant que l’alignement de chaque type fondamental est égal à sa taille (ce n’est pas toujours vrai en général), la struct X { int; char; double; } struct X { int; char; double; } struct X { int; char; double; } a l’alignement du double , et il sera remplacé par un multiple de la taille du double (par exemple 4 (int), 1 (char), 3 (remplissage), 8 (double)). La struct Y { int; X; float; } struct Y { int; X; float; } struct Y { int; X; float; } a l’alignement de X , qui est le plus grand et égal à l’alignement du double , et Y est disposé en conséquence: 4 (int), 4 (remplissage), 16 (X), 4 (flottant), 4 (remplissage) .

    (Tous les chiffres ne sont que des exemples et peuvent différer sur votre machine.)

    Par conséquent, en le décomposant en types fondamentaux, il suffit de connaître quelques alignements fondamentaux, et parmi ceux-ci, il y en a un plus connu. C ++ définit même un type maxalign_t (je pense) dont l’alignement est ce plus grand alignement.

    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 dynamicment via new ou malloc est garantie être correctement alignée pour les objects de tout type , mais les tampons qui ne sont pas alloués dynamicment n’ont pas cette garantie

    Il n’a pas besoin de savoir quel type vous avez en tête, car il s’aligne pour n’importe quel type. Sur un système donné, la taille d’alignement maximale est toujours nécessaire ou significative. Par exemple, un système avec des mots à quatre octets aura probablement un alignement maximal de quatre octets.

    Cela est également clair avec la page de manuel de malloc(3) , qui indique notamment:

    Les fonctions malloc() et calloc() renvoient un pointeur sur la mémoire allouée qui est correctement alignée pour tout type de variable .

    La seule information que malloc() peut utiliser est la taille de la requête qui lui est transmise. En général, cela peut faire quelque chose comme arrondir la taille passée à la puissance supérieure (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() retourne toujours des blocs alignés sur 8 octets et qu’il ne doit jamais rien faire de différent.

    1) Aligner sur le plus petit multiple commun de tous les alignements. par exemple, si ints nécessite un alignement de 4 octets, mais que les pointeurs nécessitent 8, allouez tout à un alignement de 8 octets. Cela fait que tout est aligné.

    2) Utilisez l’argument de taille pour déterminer l’alignement correct. Pour les petites tailles, vous pouvez déduire le type, comme malloc(1) (en supposant que les autres types de tailles ne sont pas 1) est toujours un caractère. C ++ new a l’avantage d’être sécurisé et peut donc toujours prendre des décisions d’alignement de cette manière.

    Avant C ++ 11, l’alignement était assez simple en utilisant le plus grand alignement où la valeur exacte était inconnue et malloc / calloc fonctionnait toujours de cette façon. Cela signifie que l’allocation malloc est correctement alignée pour tout type.

    Un mauvais alignement peut entraîner un comportement indéfini selon la norme, mais j’ai vu les compilateurs x86 généreux et ne punissant que des performances plus faibles.

    Notez que vous pouvez également modifier l’alignement via les options ou les directives du compilateur. (pack pragma pour VisualStudio par exemple).

    Mais pour ce qui est du placement , C ++ 11 nous apporte de nouveaux mots-clés appelés alignof et alignas. Voici un code qui montre l’effet si l’alignement maximum du compilateur est supérieur à 1. Le premier placement nouveau ci-dessous est automatiquement bon mais pas le second.

     #include  #include  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 < 

    Je suppose que la performance de ce code peut être améliorée avec certaines opérations au niveau du bit.

    EDIT: Remplacement du calcul modulo coûteux par des opérations binarys. En espérant toujours que quelqu'un trouve quelque chose encore plus rapidement.

    malloc n’a aucune connaissance de ce qu’il alloue car son paramètre est juste la taille totale. Il s’aligne simplement sur un alignement sûr pour tout object.