comment malloc comprend l'alignement?

extrait de ici

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

alloue stockage brut. En effet, l'appel malloc alloue du stockage c'est assez grand et convenablement aligné pour tenir un objet de type widget

voir aussi "1519120920 rapide" pImpl de herb sutter, il a dit:

alignement . Tout Alignement de la mémoire. Toute la mémoire allouée dynamiquement via NEW ou malloc est garanti pour être correctement aligné pour objets de n'importe quel type, mais des tampons qui ne sont pas alloués dynamiquement n'ont pas une telle garantie

je suis curieux de savoir comment malloc connaît l'alignement du type personnalisé?

42
demandé sur Mat 2012-01-06 06:05:03

6 réponses

les exigences d'alignement sont récursives: l'alignement de tout struct est simplement le plus grand alignement de tous ses membres, et cela est compris récursivement.

par exemple, et en supposant que l'alignement de chaque type fondamental égale sa taille (ce n'est pas toujours vrai en général), le struct X { int; char; double; } a l'alignement de double , et il sera capitonné pour être un multiple de la taille de double (par exemple 4 (int), 1 (char), 3 (padding), 8 (double)). Le struct Y { int; X; float; } a l'alignement de la X , qui est le plus grand et égal à l'alignement de la double , et Y est aménagé en conséquence: 4 (int), 4 (rembourrage), 16 (X), 4 (float), 4 (rembourrage).

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

par conséquent, en le décomposant aux types fondamentaux, nous avons seulement besoin de connaître une poignée d'alignements fondamentaux, et parmi ceux-ci il y a un plus grand bien connu. C++ définit même un type maxalign_t (je pense) dont l'alignement est que le plus grand alignement.

Tous malloc() a besoin de faire est de choisir une adresse qui est un multiple de cette valeur.

41
répondu Kerrek SB 2012-01-06 02:34:16

je pense que la partie la plus pertinente de la citation de Herb Sutter est la partie que j'ai marquée en gras:

l'Alignement. Tout Alignement de la mémoire. Toute mémoire allouée dynamiquement via NEW ou malloc est garantie d'être correctement alignée pour les objets de tout type , mais les tampons qui ne sont pas alloués dynamiquement n'ont aucune garantie de ce type

il ne doit pas savoir quel type vous avez à l'esprit, parce que il est aligné pour n'importe quel type. Sur un système donné, il y a une taille d'alignement maximale qui est toujours nécessaire ou significative; par exemple, un système avec des mots de quatre octets aura probablement un alignement maximum de quatre octets.

cela est également précisé par le malloc(3) man-page , qui dit en partie:

les fonctions malloc() et calloc() renvoient un pointeur à l'attribut mémoire convenablement alignée pour tout type de variable .

17
répondu ruakh 2012-01-06 02:13:55

La seule information que malloc() peut utiliser est la taille de la demande est transmis. En général, il pourrait faire quelque chose comme arrondir la taille passée à la plus proche plus grande (ou égale) puissance de deux, et aligner la mémoire basée sur cette valeur. Il y aurait probablement une limite supérieure sur la valeur d'alignement, de 8 octets.

ce qui précède est une discussion hypothétique, et la mise en œuvre réelle dépend de l'architecture de la machine et de la bibliothèque d'exécution qui vous êtes en utilisant. Peut-être que votre malloc() renvoie toujours des blocs alignés sur 8 octets et il n'a jamais à faire quelque chose de différent.

4
répondu Greg Hewgill 2012-01-06 02:10:16

1) aligner sur le multiple le moins commun de tous les alignements. par exemple, si les ints nécessitent un alignement de 4 octets, mais que les pointeurs requièrent 8 octets, alors attribuez tout à l'alignement de 8 octets. Cela provoque tout pour être aligné.

2) Utilisez l'argument 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 tailles de types ne sont pas 1) est toujours un char. C++ new a l'avantage d'être de type sécurisé et peut donc toujours faire l'alignement des décisions de cette façon.

3
répondu Pubby 2012-01-06 02:39:16

antérieur à l'alignement C++11 a été traité assez simplement en utilisant le plus grand alignement où la valeur exacte était inconnue et malloc/calloc fonctionnent toujours de cette façon. Cela signifie que l'allocation de malloc est alignée correctement pour n'importe quel type.

mauvais alignement peut entraîner un comportement non défini selon la norme, mais j'ai vu x86 compilateurs être généreux et ne punir avec des performances plus faibles.

notez que vous pouvez également modifier l'alignement via options ou directives de compilation. (pragma pack pour VisualStudio par exemple).

mais quand il s'agit de placement Nouveau , puis C++11 nous apporte de nouveaux mots-clés appelés alignof et alignas. voici un code qui montre l'effet si l'alignement max du compilateur est supérieur à 1. Le premier placement Nouveau ci-dessous est automatiquement bon, mais pas le deuxième.

#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 la performance de ce code peut être améliorée avec quelques opérations bitwise.

EDIT: remplace les calculs modulo coûteux par des opérations bitwise. J'espère toujours que quelqu'un trouvera quelque chose encore plus vite.

2
répondu Patrick Fromberg 2013-08-30 01:18:51

malloc n'a aucune connaissance de ce pour quoi il affecte parce que son paramètre est juste la taille totale. Il s'aligne juste sur un alignement qui est sûr pour n'importe quel objet.

0
répondu John Paul 2018-09-08 20:52:06