Comment allouer une mémoire alignée uniquement en utilisant la bibliothèque standard?

je viens de terminer un test dans le cadre d'un entretien d'embauche, et une question m'a déconcerté - même en utilisant google comme référence. Je voudrais voir ce que l'équipe de stackoverflow peut en faire:

la fonction "memset_16aligned" nécessite un pointeur aligné 16byte qui lui est passé, sinon il se brisera.

a) comment attribueriez-vous 1024 octets de mémoire et l'aligneriez-vous sur une limite de 16 octets?

b) libérer la mémoire après l'exécution du memset_16aligned.

{

   void *mem;

   void *ptr;

   // answer a) here

   memset_16aligned(ptr, 0, 1024);

   // answer b) here

}
376
demandé sur legends2k 2008-10-23 03:23:41

17 réponses

réponse originale

{
    void *mem = malloc(1024+16);
    void *ptr = ((char *)mem+16) & ~ 0x0F;
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

réponse fixe

{
    void *mem = malloc(1024+15);
    void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

explication as requested

la première étape consiste à allouer suffisamment d'espace libre, juste au cas où. Puisque la mémoire doit être alignée de 16 octets (ce qui signifie que l'adresse de l'octet principal doit être un multiple de 16 octets), l'ajout de 16 octets supplémentaires garantit que nous avons assez d'espace. Quelque part dans les 16 premiers octets 16 octets aligné pointeur. (Note que malloc() est censé retourner un pointeur qui est suffisamment bien aligné pour n'importe quel but. Cependant, la signification de "tous" est principalement pour des choses comme les types de base - long , double , long double , long long , et des pointeurs vers des objets et des pointeurs vers des fonctions. Lorsque vous faites des choses plus spécialisées, comme jouer avec des systèmes graphiques, ils peuvent avoir besoin d'alignement plus rigoureux que le reste du système - donc des questions et des réponses comme ce.)

l'étape suivante est de convertir le pointeur de vide en pointeur de char; GCC nonobstant, vous n'êtes pas censé faire l'arithmétique des pointeurs sur les pointeurs de vide (et GCC a des options d'avertissement pour vous dire quand vous l'abusez). Puis ajoutez 16 au pointeur de départ. Supposons que malloc() vous renvoie un pointeur incroyablement mal aligné: 0x800001. Ajouter le 16 donne 0x800011. Maintenant je veux arrondir à la limite de 16 octets - donc je veux réinitialiser les 4 derniers bits à 0. 0x0f a les 4 derniers bits sont définis à un; par conséquent, ~0x0F a tous les bits définis à un sauf les quatre derniers. Et ça avec 0x800011 donne 0x800010. Vous pouvez itérer sur les autres décalages et voir que le même arithmétique fonctionne.

la dernière étape, free() , est facile: vous toujours, et seulement, revenir à free() une valeur que l'un de malloc() , calloc() ou realloc() retourné à vous - quoi que ce soit d'autre est un désastre. Vous avez correctement fourni mem à maintenez cette valeur - je vous remercie. Les versions libres.

enfin, si vous connaissez les internes du paquet malloc de votre système, vous pouvez deviner qu'il pourrait bien retourner des données alignées de 16 octets (ou qu'il pourrait être aligné de 8 octets). Si elle était alignée à 16 octets, alors vous n'auriez pas besoin de faire de bruit avec les valeurs. Toutefois, il s'agit d'un colis douteux et non portatif-les autres colis malloc ont des alignements minimaux différents, et par conséquent, en présumant une chose quand il fait quelque chose différent conduirait à des décharges de noyau. Dans de larges limites, cette solution est portable.

Quelqu'un d'autre a mentionné posix_memalign() comme une autre façon d'obtenir la mémoire alignée; qui n'est pas disponible partout, mais pourrait souvent être mis en œuvre en utilisant cette comme une base. Notez qu'il était commode que l'alignement soit une puissance de 2; les autres alignements sont plus désordonnés.

encore un commentaire - ce code ne vérifie pas que l'allocation a réussi.

amendement

Windows programmeur a souligné que vous ne pouvez pas faire des opérations de masque de bits sur les pointeurs, et, en effet, GCC (3.4.6 et 4.3.1 testé) ne se plaint comme cela. Ainsi, une version modifiée du code de base - converti en un programme principal, suit. J'ai aussi pris la liberté d'ajouter 15 au lieu de 16, comme cela a été souligné. J'utilise uintptr_t depuis que C99 est là depuis assez longtemps pour être accessible sur la plupart plate. Si ce n'était pas pour l'utilisation de PRIXPTR dans le printf() , il suffirait de #include <stdint.h> au lieu d'utiliser #include <inttypes.h> . [ce code comprend le correctif souligné par C. R. , qui réitérait un point d'abord fait par projet de loi K il y a plusieurs années, que j'ai réussi à négliger jusqu'à présent.]

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void memset_16aligned(void *space, char byte, size_t nbytes)
{
    assert((nbytes & 0x0F) == 0);
    assert(((uintptr_t)space & 0x0F) == 0);
    memset(space, byte, nbytes);  // Not a custom implementation of memset()
}

int main(void)
{
    void *mem = malloc(1024+15);
    void *ptr = (void *)(((uintptr_t)mem+15) & ~ (uintptr_t)0x0F);
    printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
    memset_16aligned(ptr, 0, 1024);
    free(mem);
    return(0);
}

et voici un légèrement plus généralisée version, qui fonctionnera pour les tailles qui sont une puissance de 2:

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void memset_16aligned(void *space, char byte, size_t nbytes)
{
    assert((nbytes & 0x0F) == 0);
    assert(((uintptr_t)space & 0x0F) == 0);
    memset(space, byte, nbytes);  // Not a custom implementation of memset()
}

static void test_mask(size_t align)
{
    uintptr_t mask = ~(uintptr_t)(align - 1);
    void *mem = malloc(1024+align-1);
    void *ptr = (void *)(((uintptr_t)mem+align-1) & mask);
    assert((align & (align - 1)) == 0);
    printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

int main(void)
{
    test_mask(16);
    test_mask(32);
    test_mask(64);
    test_mask(128);
    return(0);
}

pour convertir test_mask() en une fonction d'affectation générale, la valeur de retour unique de l'allocateur devrait encoder l'adresse de diffusion, comme plusieurs personnes l'ont indiqué dans leurs réponses.

problèmes avec les enquêteurs

Uri commente: peut-être que j'ai [un] problème de compréhension de la lecture matin, mais si la question de l'entrevue dit spécifiquement: "comment attribueriez-vous 1024 octets de mémoire" et vous attribuez clairement plus que cela. Ce ne serait pas un échec automatique de l'interviewer?

ma réponse ne rentre pas dans un commentaire de 300 caractères...

ça dépend, je suppose. Je pense que la plupart des gens (y compris moi) ont pris la question à signifier " comment attribueriez-vous un espace dans lequel 1024 octets de données peuvent être stockées, et où la base l'adresse est un multiple de 16 octets". Si l'intervieweur voulait vraiment dire comment vous pouvez allouer 1024 octets (seulement) et le faire aligner 16 octets, alors les options sont plus limitées.

  • de toute évidence, une possibilité consiste à attribuer 1024 octets puis à donner l'adresse du "traitement d'alignement"; le problème avec cette approche est que l'espace disponible réel n'est pas correctement déterminé (l'espace utilisable se situe entre 1008 et 1024 octets, mais il n'y avait pas de mécanisme disponible pour spécifier quelle taille), ce qui le rend moins que utile.
  • une autre possibilité est que vous devez écrire un allocateur mémoire complet et vous assurer que le bloc de 1024 octets que vous retournez est correctement aligné. Si c'est le cas, vous finissez probablement par faire une opération assez similaire à ce que la solution proposée a fait, mais vous la cachez à l'intérieur de l'allocateur.

réponses, je m'attends à ce qu'ils reconnaissent que cette solution répond à une question étroitement liée, puis de recadrer leur question pour orienter la conversation dans la bonne direction. (De plus, si l'intervieweur est vraiment stroppy, alors je ne voudrais pas le travail; si la réponse à une exigence insuffisamment précise est rejetée en flammes sans correction, alors l'intervieweur n'est pas quelqu'un pour qui il est sécuritaire de travailler.)

Le monde bouge sur

le titre de la question a changé récemment. C'était résoudre l'alignement de la mémoire dans C question d'entrevue qui m'a stumped . Le titre révisé ( ) comment attribuer une mémoire alignée uniquement en utilisant la bibliothèque standard? ) exige une réponse légèrement révisée - cet addendum la fournit.

C11 (ISO/IEC 9899:2011) a ajouté la fonction aligned_alloc() :

7.22.3.1 La aligned_alloc fonction

Synopsis

#include <stdlib.h>
void *aligned_alloc(size_t alignment, size_t size);

Description

La fonction aligned_alloc attribue de l'espace à un objet dont l'alignement est spécifiée par alignment , dont le montant est spécifié par size , et dont la valeur est indéterminé. La valeur de alignment doit être un alignement valide la valeur size doit être un multiple intégral de alignment .

Renvoie

La fonction aligned_alloc renvoie soit un pointeur nul, soit un pointeur vers l'espace alloué.

et POSIX définit posix_memalign() :

#include <stdlib.h>

int posix_memalign(void **memptr, size_t alignment, size_t size);

DESCRIPTION

la fonction posix_memalign() attribuera size octets alignés sur une limite spécifiée par alignment , et retournera un pointeur à la mémoire allouée dans memptr . La valeur de alignment doit être une puissance de deux multiples de sizeof(void *) .

Lorsqu'il est achevé avec succès, la valeur indiquée par memptr doit être un multiple de alignment .

si la taille de l'espace demandé est 0, le behavior est défini par implémentation; la valeur retournée dans memptr doit être soit un pointeur nul, soit un pointeur unique.

la fonction free() doit désallouer la mémoire qui a déjà été attribuée par posix_memalign() .

VALEUR DE RETOUR

une fois le travail terminé avec succès, posix_memalign() doit retourner zéro; sinon, un numéro d'erreur doit être retourné pour indiquer l'erreur.

L'un ou l'autre ou les deux pourraient être utilisés pour répondre à la question maintenant, mais seulement la fonction POSIX était une option lorsque la question a été initialement répondu.

dans les coulisses, la nouvelle fonction mémoire alignée fait à peu près le même travail que décrit dans la question, sauf qu'ils ont la capacité de forcer l'alignement plus facilement, et garder la trace du début de la mémoire alignée à l'intérieur de sorte que le code n'a pas à traiter spécialement - il il suffit de libérer la mémoire retournée par la fonction d'allocation qui a été utilisée.

545
répondu Jonathan Leffler 2017-05-23 12:34:37

trois réponses légèrement différentes selon la façon dont vous regardez la question:

1) Assez bon pour la question exacte posée est la solution de Jonathan Leffler, sauf que pour arrondir jusqu'à 16-aligné, vous n'avez besoin que de 15 octets supplémentaires, Pas 16.

:

/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */
void *mem = malloc(1024+15);
ASSERT(mem); // some kind of error-handling code
/* round up to multiple of 16: add 15 and then round down by masking */
void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;

B:

free(mem);

2) pour une fonction plus générique d'allocation de mémoire, l'appelant ne veut pas avoir à garder la trace de deux pointeurs (un à utiliser et un à libérer). Vous stockez donc un pointeur vers le 'vrai' buffer sous le buffer aligné.

:

void *mem = malloc(1024+15+sizeof(void*));
if (!mem) return mem;
void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F;
((void**)ptr)[-1] = mem;
return ptr;

B:

if (ptr) free(((void**)ptr)[-1]);

noter que contrairement à (1), où seulement 15 octets ont été ajoutés à mem, ce code pourrait en fait réduire l'alignement si votre mise en œuvre se trouve à garantir un alignement de 32 octets à partir de malloc (peu probable, mais en théorie une mise en œuvre C pourrait avoir un 32 octets aligné type). Cela n'a pas d'importance si tout ce que vous faites est d'appeler memset_16aligned, mais si vous utilisez la mémoire pour une structure alors cela pourrait avoir de l'importance.

Je ne suis pas sûr de ce qu'est un bon correctif pour ceci (autre que d'avertir l'utilisateur que le buffer retourné n'est pas nécessairement adapté à des structures arbitraires) puisqu'il n'y a aucun moyen de déterminer programmatiquement ce qu'est la garantie d'alignement spécifique à l'implémentation. Je suppose qu'au démarrage vous pourriez allouer deux ou plusieurs 1-octet tampons, et de supposer que le pire alignement que vous voyez est l'alignement garanti. Si vous vous trompez, vous perdez la mémoire. Quelqu'un à une meilleure idée, merci de le dire...

[ ajouté : Le truc "standard" est de créer une union de "types susceptibles d'être alignés au maximum" pour déterminer l'alignement requis. Le maximum aligné types sont susceptibles d'être (en C99) ' long long ', ' long double ', ' void * ', ou " void (*)(void) '; si vous incluez <stdint.h> , vous pourriez probablement utiliser intmax_t à la place de long long (et, sur les machines Power 6 (AIX), intmax_t vous donnerait un type entier de 128 bits). Les exigences d'alignement pour cette union peuvent être déterminées en l'incorporant dans une structure à structure unique suivie par l'union:

struct alignment
{
    char     c;
    union
    {
        intmax_t      imax;
        long double   ldbl;
        void         *vptr;
        void        (*fptr)(void);
    }        u;
} align_data;
size_t align = (char *)&align_data.u.imax - &align_data.c;

vous utiliserez alors le plus grand de l'alignement demandé (dans l'exemple, 16) et la valeur align calculée ci-dessus.

sur (64 bits) Solaris 10, il apparaît que l'alignement de base pour le résultat de malloc() est un multiple de 32 octets.

]

en pratique, les allocateurs alignés prennent souvent un paramètre pour l'alignement plutôt que d'être câblés. Afin que l'utilisateur passe à la taille de la structure qu'ils se soucient de (ou du moins la puissance de 2 supérieure ou égale à) et tout ira bien.

3) Utilisez ce que votre plate-forme fournit: posix_memalign pour POSIX, _aligned_malloc sur Windows.

4) Si vous utilisez C11, alors l'option la plus propre - portable et concise - est d'utiliser la fonction de bibliothèque standard aligned_alloc qui a été introduite dans cette version de la spécification de langue.

54
répondu Steve Jessop 2015-07-10 11:43:37

, Vous pouvez aussi essayer posix_memalign() (sur les plate-formes POSIX, bien sûr).

37
répondu florin 2016-02-12 21:44:49

voici une autre approche pour la partie "arrondir". Ce n'est pas la solution la plus brillamment codée, mais elle fait le travail, et ce type de syntaxe est un peu plus facile à retenir (plus fonctionnerait pour des valeurs d'alignement qui ne sont pas une puissance de 2). La distribution uintptr_t était nécessaire pour apaiser le compilateur; l'arithmétique pointer n'est pas très friande de division ou de multiplication.

void *mem = malloc(1024 + 15);
void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16;
memset_16aligned(ptr, 0, 1024);
free(mem);
19
répondu An̲̳̳drew 2008-10-24 17:59:13

malheureusement, en C99 il semble assez difficile de garantir un alignement de toute sorte d'une manière qui serait portable à travers n'importe quelle implémentation C conforme à C99. Pourquoi? Parce qu'un pointeur n'est pas garanti pour être "l'adresse octet" que l'on pourrait imaginer avec un modèle de mémoire plate. La représentation de uintptr_t n'est pas non plus Garantie, ce qui est en soi un type optionnel.

nous pourrions connaître certaines implémentations qui utilisent un représentation pour nul * (et par définition, aussi char * ) qui est une adresse byte simple, mais par C99 il est opaque pour nous, les programmeurs. Une implémentation peut représenter un pointeur par un ensemble { segment , offset } où offset pourrait avoir qui sait-quel alignement "en réalité."Pourquoi, un pointeur pourrait même être une forme de valeur de recherche de table de hachage, ou même une liste liée valeur de recherche. Il pourrait encoder l'information des limites.

dans un récent projet C1X pour une norme C, nous voyons le _Alignas mot-clé. Qui pourrait aider un peu.

la seule garantie que nous donne C99 est que les fonctions d'allocation de mémoire retourneront un pointeur approprié pour affectation à un pointeur pointant vers n'importe quel type d'objet. Puisque nous ne pouvons pas spécifier l'alignement des objets, nous ne pouvons pas implémenter nos propres fonctions d'allocation avec responsabilité de l'alignement de façon bien définie et portable.

Il serait bon de se tromper au sujet de cette allégation.

18
répondu Shao 2014-08-14 10:18:28

sur le front de padding 16 vs 15 byte-count, le nombre réel que vous devez ajouter pour obtenir un alignement de N est max(0,N-M) où M est l'alignement naturel de l'allocateur mémoire (et les deux sont des puissances de 2).

étant donné que l'alignement minimal de mémoire d'un allocateur est de 1 octet, 15=max(0,16-1) est une réponse conservatrice. Cependant, si vous savez que votre allocateur de mémoire va vous donner des adresses alignées int de 32 bits (ce qui est assez commun), vous pourrait avoir utilisé 12 comme tampon.

ce n'est pas important pour cet exemple, mais il pourrait être important sur un système intégré avec 12K de RAM où chaque int enregistré compte.

la meilleure façon de le mettre en œuvre si vous allez réellement essayer de sauver chaque octet possible est comme une macro de sorte que vous pouvez le nourrir votre alignement de mémoire natif. Encore une fois, cela n'est probablement utile que pour les systèmes embarqués où vous devez sauvegarder chaque octet.

In l'exemple ci-dessous, sur la plupart des systèmes, la valeur 1 est très bien pour MEMORY_ALLOCATOR_NATIVE_ALIGNMENT , mais pour notre système intégré théorique avec des allocations alignées de 32 bits, ce qui suit pourrait sauver un tout petit peu de mémoire précieuse:

#define MEMORY_ALLOCATOR_NATIVE_ALIGNMENT    4
#define ALIGN_PAD2(N,M) (((N)>(M)) ? ((N)-(M)) : 0)
#define ALIGN_PAD(N) ALIGN_PAD2((N), MEMORY_ALLOCATOR_NATIVE_ALIGNMENT)
15
répondu Adisak 2009-10-21 16:40:40

peut-être auraient-ils été satisfaits de la connaissance de memalign ? Et comme le souligne Jonathan Leffler, il y a deux nouvelles fonctions préférables à connaître.

Oups, florin m'a devancé. Cependant, si vous lisez la page de manuel à laquelle j'ai fait référence, vous comprendrez très probablement l'exemple fourni par une affiche précédente.

8
répondu Don Wakefield 2016-02-13 03:04:19

Nous faisons ce genre de chose tout le temps pour Accélérer.framework, une bibliothèque OS X / iOS fortement vectorisée, où nous devons faire attention à l'alignement tout le temps. Il y a plusieurs options, dont une ou deux que je n'ai pas vues mentionnées ci-dessus.

la méthode La plus rapide pour un petit tableau comme ceci est juste le coller sur la pile. Avec GCC / clang:

 void my_func( void )
 {
     uint8_t array[1024] __attribute__ ((aligned(16)));
     ...
 }

Pas de free (). Il s'agit généralement de deux instructions: soustraire 1024 de le pointeur de pile, puis le pointeur de pile avec-alignement. Vraisemblablement, le demandeur a eu besoin des données sur le tas parce que sa durée de vie du réseau a dépassé la pile ou la récursion est au travail ou l'espace de la pile est à un prix élevé.

sur OS X/iOS tous les appels à malloc/calloc / etc. sont toujours alignés de 16 octets. Si vous aviez besoin de 32 octets alignés pour AVX, par exemple, vous pouvez utiliser posix_memalign:

void *buf = NULL;
int err = posix_memalign( &buf, 32 /*alignment*/, 1024 /*size*/);
if( err )
   RunInCirclesWaivingArmsWildly();
...
free(buf);

certains ont mentionné le c++ interface qui fonctionne de façon similaire.

il ne faut pas oublier que les pages sont alignées à de grandes puissances de deux, donc les tampons alignés à la page sont aussi alignés à 16 octets. Ainsi, mmap() et valloc () et d'autres interfaces similaires sont également des options. mmap () a l'avantage que le buffer peut être attribué préinitialisé avec quelque chose de non-nul, si vous voulez. Depuis ces pages alignées taille, vous ne serez pas obtenir l'allocation minimale de ceux-ci, et il pourrait être assujetti à un VM fault la première fois que vous le touchez.

Cheesy: Tour de garde malloc ou similaire. Les tampons de taille n*16 octets comme celui-ci seront alignés n * 16 octets, car VM est utilisé pour capturer les dépassements et ses limites sont aux limites de la page.

Certains Accélèrent.les fonctions de cadre prennent dans un tampon temp fourni par l'utilisateur pour utiliser comme espace de rayure. Ici, nous devons supposer que le tampon qui nous a été transmis est totalement désaligné et que l'utilisateur essaie activement pour nous rendre la vie dure par dépit. (Nos cas de test collent une page de garde juste avant et après le tampon temp pour souligner le dépit. Ici, nous retournons la taille minimale dont nous avons besoin pour garantir un segment aligné de 16 octets quelque part, puis nous alignons manuellement le buffer par la suite. Cette taille est désired_size + alignment-1. Donc, Dans ce cas c'est 1024 + 16 - 1 = 1039 octets. Puis alignez-vous comme suit:

#include <stdint.h>
void My_func( uint8_t *tempBuf, ... )
{
    uint8_t *alignedBuf = (uint8_t*) 
                          (((uintptr_t) tempBuf + ((uintptr_t)alignment-1)) 
                                        & -((uintptr_t) alignment));
    ...
}

ajouter l'alignement-1 déplacera le pointeur au-delà de la d'abord adresse alignée et ensuite ANDing avec-alignement (par exemple 0xfff...ff0 pour l'alignement=16) ramène à l'adresse alignée.

comme décrit par d'autres postes, sur d'autres systèmes d'exploitation sans garantie d'alignement de 16 octets, vous pouvez appeler malloc avec la plus grande taille, mettre de côté le pointeur pour free() plus tard, puis aligner comme décrit immédiatement ci-dessus et utiliser le pointeur aligné, tout comme décrit pour notre cas tampon temp.

comme pour aligned_memset, c'est assez stupide. Vous n'avez qu'à Boucler jusqu'à 15 octets pour atteindre une adresse alignée, puis procéder avec des magasins alignés après cela avec un code de nettoyage possible à la fin. Vous pouvez même faire les bits de nettoyage dans le code vectoriel, soit en tant que stockage non aligné qui chevauchent la région alignée (à condition que la longueur soit au moins la longueur d'un vecteur) ou en utilisant quelque chose comme movmaskdqu. Quelqu'un est juste paresseux. Toutefois, il est probablement raisonnable de poser une question d'entrevue si l'intervieweur veut savoir si vous êtes à l'aise avec stdint.h, les opérateurs au niveau du bit et de la mémoire des fondamentaux, de sorte que l'exemple artificiel peut être pardonné.

5
répondu Ian Ollmann 2014-06-05 05:19:14

je suis surpris que personne n'a voté Shao 's réponse que, si je comprends bien, il est impossible de faire ce qui est demandé dans la norme C99, puisque convertir un pointeur en type intégral est formellement un comportement non défini. (À l'exception de la norme permettant la conversion uintptr_t <-> void* , mais la norme ne semble pas permettre de faire des manipulations de la valeur uintptr_t puis de la convertir en arrière.)

5
répondu Lutorm 2017-05-23 12:34:37

l'utilisation de memalign, Alignés-Mémoire-Blocs pourrait être une bonne solution pour le problème.

3
répondu neuron 2010-10-12 18:09:09

la première chose qui m'est venue à l'esprit en lisant cette question a été de définir une structure alignée, de l'instancier, puis de la pointer vers elle.

y a-t-il une raison fondamentale à ma disparition puisque personne d'autre ne l'a suggéré?

comme sidenote, puisque j'ai utilisé un tableau de char (en supposant que le char du système est de 8 bits (i.e. 1 octet)), Je ne vois pas la nécessité de l'attribut ((emballé)) nécessairement (corriger moi si je me trompe), mais je l'ai mis dans de toute façon.

cela fonctionne sur deux systèmes sur lesquels je l'ai essayé, mais il est possible qu'il y ait une optimisation du compilateur que je ne sache pas me donner de faux positifs vis-à-vis de l'efficacité du code. J'ai utilisé gcc 4.9.2 sur OSX et gcc 5.2.1 sur Ubuntu.

#include <stdio.h>
#include <stdlib.h>

int main ()
{

   void *mem;

   void *ptr;

   // answer a) here
   struct __attribute__((packed)) s_CozyMem {
       char acSpace[16];
   };

   mem = malloc(sizeof(struct s_CozyMem));
   ptr = mem;

   // memset_16aligned(ptr, 0, 1024);

   // Check if it's aligned
   if(((unsigned long)ptr & 15) == 0) printf("Aligned to 16 bytes.\n");
   else printf("Rubbish.\n");

   // answer b) here
   free(mem);

   return 1;
}
3
répondu J-a-n-u-s 2016-05-10 21:28:41

MacOS X spécifique:

  1. tous les pointeurs attribués à malloc sont alignés sur 16 octets.
  2. C11 est supporté, donc vous pouvez simplement appeler aligned_malloc (16, size).

  3. MacOS X choisit un code qui est optimisé pour les processeurs individuels au démarrage pour memset, memcpy et memmove et qui utilise des trucs dont vous n'avez jamais entendu parler pour le rendre rapide. 99% de chance que memset fonctionne plus vite que n'importe quel memset16 écrit à la main, ce qui rend toute la question inutile.

si vous voulez une solution 100% portable, avant C11 il n'y en a pas. Car il n'est pas portable moyen de tester l'alignement d'un pointeur. Si elle ne doit pas être 100% portable, vous pouvez utiliser

char* p = malloc (size + 15);
p += (- (unsigned int) p) % 16;

cela suppose que l'alignement d'un pointeur est stocké dans les bits les plus bas lors de la conversion d'un pointeur en int non signé. Conversion à non signé int perd des informations et est implémentation définie, mais cela n'a pas d'importance parce que nous ne convertissons pas le résultat en pointeur.

l'horrible partie est bien sûr que le pointeur original doit être sauvegardé quelque part pour appeler free () avec lui. Donc dans l'ensemble j'ai vraiment douter de la sagesse de cette conception.

1
répondu Chris 2013-11-25 13:44:21

utilisez juste memalign? http://linux.die.net/man/3/memalign

0
répondu PhonicUK 2012-09-04 11:35:55

vous pouvez également ajouter quelques 16 octets, puis pousser le ptr original à 16 bits aligné en ajoutant le (16-mod) comme sous le pointeur:

main(){
void *mem1 = malloc(1024+16);
void *mem = ((char*)mem1)+1; // force misalign ( my computer always aligns)
printf ( " ptr = %p \n ", mem );
void *ptr = ((long)mem+16) & ~ 0x0F;
printf ( " aligned ptr = %p \n ", ptr );

printf (" ptr after adding diff mod %p (same as above ) ", (long)mem1 + (16 -((long)mem1%16)) );


free(mem1);
}
0
répondu resultsway 2013-03-25 18:27:14

S'il y a des contraintes que, vous ne pouvez pas gaspiller un seul octet, alors cette solution fonctionne: Note: il existe un cas où cela peut être exécuté en continu: d

   void *mem;  
   void *ptr;
try:
   mem =  malloc(1024);  
   if (mem % 16 != 0) {  
       free(mem);  
       goto try;
   }  
   ptr = mem;  
   memset_16aligned(ptr, 0, 1024);
0
répondu Deepthought 2013-11-25 14:06:59

pour la solution j'ai utilisé un concept de rembourrage qui aligne la mémoire et ne gaspille pas le la mémoire d'un seul octet .

S'il y a des contraintes, vous ne pouvez pas gaspiller un seul octet. Tous les pointeurs attribués avec malloc sont alignés sur 16 octets.

C11 est supporté, donc vous pouvez simplement appeler aligned_malloc (16, size).

void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
0
répondu user3415603 2014-03-14 14:05:20
long add;   
mem = (void*)malloc(1024 +15);
add = (long)mem;
add = add - (add % 16);//align to 16 byte boundary
ptr = (whatever*)(add);
-2
répondu Ramana 2012-10-09 05:49:49