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 pour référence. J’aimerais voir ce que l’équipe de stackoverflow peut faire avec:
La fonction «memset_16aligned» nécessite qu’un pointeur aligné de 16 octets lui soit transmis ou qu’elle se bloque.
a) Comment alloueriez-vous 1024 octets de mémoire et alignez-le sur une limite de 16 octets?
b) Libérez la mémoire après l’exécution de memset_16aligned.
{ void *mem; void *ptr; // answer a) here memset_16aligned(ptr, 0, 1024); // answer b) here }
{ void *mem = malloc(1024+16); void *ptr = ((char *)mem+16) & ~ 0x0F; memset_16aligned(ptr, 0, 1024); free(mem); }
{ void *mem = malloc(1024+15); void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F; memset_16aligned(ptr, 0, 1024); free(mem); }
La première étape consiste à allouer suffisamment d’espace disponible, au cas où. Puisque la mémoire doit être alignée sur 16 octets (ce qui signifie que l’adresse de l’octet principal doit être un multiple de 16), l’ajout de 16 octets supplémentaires garantit que nous avons suffisamment d’espace. Quelque part dans les 16 premiers octets, il existe un pointeur aligné sur 16 octets. (Notez que malloc()
est supposé renvoyer un pointeur suffisamment bien aligné pour quelque raison que ce soit. Cependant, la signification de «any» est principalement pour des choses comme les types de base – long
, double
, long double
, long long
objects et pointeurs vers des fonctions Lorsque vous faites des choses plus spécialisées, comme jouer avec des systèmes graphiques, ils peuvent nécessiter un alignement plus rigoureux que le rest du système, d’où des questions et des réponses comme celles-ci.
L’étape suivante consiste à convertir le pointeur vide en pointeur de caractère; Nonobstant GCC, vous n’êtes pas censé faire de l’arithmétique de pointeur sur les pointeurs de vide (et GCC a des options d’avertissement pour vous dire quand vous en abusez). Puis ajoutez 16 au pointeur de début. Supposons que malloc()
vous a renvoyé un pointeur incroyablement mal aligné: 0x800001. L’ajout du 16 donne 0x800011. Maintenant, je veux arrondir à la limite de 16 octets – je veux donc remettre les 4 derniers bits à 0. 0x0F a les 4 derniers bits mis à un; Par conséquent, tous les bits de ~0x0F
sont définis sur un, à l’exception des quatre derniers. Et cela avec 0x800011 donne 0x800010. Vous pouvez parcourir les autres décalages et voir que la même arithmétique fonctionne.
La dernière étape, free()
, est simple: vous retournez toujours à free()
uniquement une valeur que malloc()
, calloc()
ou realloc()
renvoyée – tout le rest est un désastre. Vous avez correctement fourni mem
pour conserver cette valeur – merci. Le gratuit le libère.
Enfin, si vous connaissez les éléments internes du paquet malloc
de votre système, vous pouvez deviner qu’il pourrait bien renvoyer des données alignées sur 16 octets (ou être aligné sur 8 octets). S’il était aligné sur 16 octets, vous ne devriez pas avoir à vous soucier des valeurs. Cependant, ceci est douteux et non portable – les autres packages malloc
ont des alignements minimum différents et, par conséquent, en supposant que quelque chose de différent soit fait, cela conduirait à des vidages de mémoire. Dans de larges limites, cette solution est portable.
Quelqu’un d’autre a mentionné posix_memalign()
comme un autre moyen d’obtenir la mémoire alignée; ce n’est pas disponible partout, mais peut souvent être mis en œuvre en utilisant cette base. Notez qu’il était commode que l’alignement soit une puissance de 2; d’autres alignements sont plus compliqués.
Un autre commentaire – ce code ne vérifie pas que l’allocation a réussi.
Windows Programmer a souligné que vous ne pouvez pas effectuer d’opérations de masquage de bits sur les pointeurs, et que GCC (3.4.6 et 4.3.1 testé) se plaint effectivement de cette manière. Ainsi, une version modifiée du code de base – convertie en un programme principal, suit. J’ai également pris la liberté d’append seulement 15 au lieu de 16, comme cela a été souligné. J’utilise uintptr_t
depuis que C99 existe depuis assez longtemps pour être accessible sur la plupart des plates-formes. S’il n’y avait pas eu l’utilisation de PRIXPTR
dans les instructions printf()
, il suffirait de #include
au lieu d’utiliser #include
. [Ce code inclut le correctif signalé par CR , qui réitère un point soulevé par Bill K il y a quelques années, que j’ai réussi à négliger jusqu’à présent.]
#include #include #include #include #include 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 une version légèrement plus généralisée, qui fonctionnera pour des tailles ayant une puissance de 2:
#include #include #include #include #include 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’allocation à usage général, la valeur de retour unique de l’allocateur doit coder l’adresse de publication, comme plusieurs personnes l’ont indiqué dans leurs réponses.
Uri a commenté: J’ai peut-être un problème de compréhension à la lecture ce matin, mais si la question de l’entretien dit spécifiquement: “Comment alloueriez-vous 1024 octets de mémoire” et vous allouez clairement plus que cela. Ne serait-ce pas un échec automatique de l’interviewer?
Ma réponse ne rentre pas dans un commentaire de 300 caractères …
Cela dépend, je suppose. Je pense que la plupart des gens (y compris moi) ont pris la question pour signifier “Comment alloueriez-vous un espace dans lequel 1024 octets de données peuvent être stockés, et où l’adresse de base est un multiple de 16 octets”. Si l’intervieweur voulait vraiment savoir comment allouer 1024 octets (uniquement) et les aligner sur 16 octets, les options sont plus limitées.
Cependant, si l’intervieweur s’attendait à l’une ou l’autre de ces réponses, je m’attendrais à ce qu’elles reconnaissent que cette solution répond à une question étroitement liée, puis à recadrer leur question pour orienter la conversation dans la bonne direction. (De plus, si l’enquêteur était vraiment désemparé, alors je ne voudrais pas le faire; si la réponse à une exigence insuffisamment précise est tirée sans correction, l’intervieweur n’est pas quelqu’un pour qui travailler est sans danger.)
Le titre de la question a changé récemment. C’était la question de l’alignement de la mémoire dans C interview qui m’a déconcerté . Le titre révisé ( Comment atsortingbuer une mémoire alignée uniquement à l’aide de la bibliothèque standard? ) Exige une réponse légèrement révisée – cet addendum le fournit.
C11 (ISO / IEC 9899: 2011) a ajouté la fonction aligned_alloc()
:
7.22.3.1 La fonction
aligned_alloc
Synopsis
#include
void *aligned_alloc(size_t alignment, size_t size); La description
La fonctionaligned_alloc
alloue de l’espace pour un object dont l’alignement est spécifié paralignment
, dont la taille est spécifiée par lasize
et dont la valeur est indéterminée. La valeur de l’alignment
doit être un alignement valide pris en charge par l’implémentation et la valeur desize
doit être un multiple entier de l’alignment
.Résultats
La fonctionaligned_alloc
renvoie un pointeur nul ou un pointeur sur l’espace alloué.
Et POSIX définit posix_memalign()
:
#include
int posix_memalign(void **memptr, size_t alignment, size_t size); LA DESCRIPTION
La fonction
posix_memalign()
doit allouer des octets desize
alignés sur une limite spécifiée par l’alignment
et doit renvoyer un pointeur sur la mémoire allouée dansmemptr
. La valeur de l’alignment
doit être une puissance de deux multiples desizeof(void *)
.Une fois l’opération terminée, la valeur indiquée par
memptr
doit être un multiple de l’alignment
.Si la taille de l’espace demandé est 0, le comportement est défini par l’implémentation; la valeur renvoyée dans
memptr
doit être soit un pointeur nul, soit un pointeur unique.La fonction
free()
doit désallouer la mémoire précédemment allouée parposix_memalign()
.VALEUR DE RETOUR
Après avoir réussi,
posix_memalign()
doit retourner zéro; sinon, un numéro d’erreur doit être renvoyé pour indiquer l’erreur.
L’un ou l’autre ou les deux pourraient être utilisés pour répondre à la question maintenant, mais seule la fonction POSIX était une option lorsque la question a été répondue à l’origine.
Dans les coulisses, la nouvelle fonction de mémoire alignée fait sensiblement le même travail que celui décrit dans la question, sauf qu’ils ont la capacité de forcer l’alignement plus facilement et de suivre le début de la mémoire alignée en interne de sorte que le code ne fonctionne pas. doivent faire face à spécialement – il libère simplement la mémoire retournée par la fonction d’allocation qui a été utilisée.
Trois réponses légèrement différentes selon la manière dont vous examinez la question:
1) Assez bien pour la question exacte posée, la solution de Jonathan Leffler, sauf que pour arrondir à 16 alignés, vous n’avez besoin que de 15 octets supplémentaires, pas de 16.
UNE:
/* 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 d’allocation de mémoire plus générique, l’appelant ne veut pas avoir à suivre deux pointeurs (un à utiliser et un à libérer). Donc, vous stockez un pointeur sur le “vrai” tampon sous le tampon aligné.
UNE:
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]);
Notez que contrairement à (1), où seulement 15 octets ont été ajoutés à mem, ce code pourrait en fait réduire l’alignement si votre implémentation garantit un alignement de 32 octets de malloc (peu probable, mais en théorie une implémentation C pourrait avoir 32 octets). type aligné). 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, cela pourrait avoir de l’importance.
Je ne suis pas certain que ce soit une bonne solution (sauf pour avertir l’utilisateur que le tampon retourné n’est pas nécessairement adapté à des structures arbitraires), car il n’y a aucun moyen de déterminer par programme la garantie d’alignement spécifique à l’implémentation. Je suppose qu’au démarrage, vous pouvez allouer deux ou plusieurs tampons de 1 octet, et supposer que l’alignement le plus défavorable est l’alignement garanti. Si vous vous trompez, vous perdez de la mémoire. Quelqu’un avec une meilleure idée, s’il vous plaît dites-le …
[ Ajouté : l’astuce «standard» consiste à créer une union de «types susceptibles d’être alignés au maximum» pour déterminer l’alignement requirejs. Les types alignés au maximum sont susceptibles d’être (en C99) « long long
», « long double
», « void *
» ou « void (*)(void)
»; Si vous incluez
, vous pourriez probablement utiliser ” intmax_t
” à la place de long long
(et, sur 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 avec un seul caractère suivi de 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 utiliseriez alors le plus grand de l’alignement demandé (dans l’exemple, 16) et la valeur d’ align
calculée ci-dessus.
Sur Solaris 10 (64 bits), il semble que l’alignement de base du résultat de malloc()
soit 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é. Ainsi, l’utilisateur transmettra la taille de la structure qui les intéresse (ou la puissance la plus faible égale ou supérieure à 2) et tout ira bien.
3) Utilisez ce que votre plate-forme fournit: posix_memalign
pour POSIX, _aligned_malloc
sous Windows.
4) Si vous utilisez C11, l’option la plus propre – portable et concise – consiste à utiliser la fonction de bibliothèque standard aligned_alloc
introduite dans cette version de la spécification de langage.
Vous pouvez également essayer posix_memalign()
(sur les plates-formes POSIX, bien sûr).
Voici une approche alternative à la partie «arrondi». 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 (en plus, cela fonctionnerait pour des valeurs d’alignement qui ne sont pas une puissance de 2). La dissortingbution de uintptr_t
était nécessaire pour apaiser le compilateur; l’arithmétique du pointeur n’aime pas beaucoup la division ou la multiplication.
void *mem = malloc(1024 + 15); void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16; memset_16aligned(ptr, 0, 1024); free(mem);
Malheureusement, en C99, il semble difficile de garantir un alignement quelconque d’une manière qui serait portable pour toute implémentation C conforme à C99. Pourquoi? Parce qu’un pointeur n’est pas garanti pour être “l’adresse d’octet” on pourrait imaginer avec un modèle de mémoire à plat. La représentation de uintptr_t n’est pas non plus garantie, ce qui en soi est un type facultatif.
Nous pourrions connaître certaines implémentations qui utilisent une représentation de void * (et par définition, aussi char * ) qui est une simple adresse d’octet, mais par C99, elle est opaque pour nous, les programmeurs. Une implémentation peut représenter un pointeur par un ensemble { segment , offset } où le décalage peut avoir un alignement “qui sait quoi” “dans la réalité”. Pourquoi, un pointeur pourrait même être une forme de valeur de recherche de table de hachage, ou même une valeur de recherche de liste liée. Il pourrait encoder des informations de limites.
Dans un brouillon C1X récent pour un standard C, nous voyons le mot clé _Alignas . Cela pourrait aider un peu.
La seule garantie que C99 nous donne est que les fonctions d’allocation de mémoire renverront un pointeur pouvant être affecté à un pointeur pointant sur n’importe quel type d’object. Comme nous ne pouvons pas spécifier l’alignement des objects, nous ne pouvons pas implémenter nos propres fonctions d’allocation avec la responsabilité de l’alignement de manière portable et bien définie.
Il serait bon de se tromper à propos de cette revendication.
Sur le front de remplissage de 16 vs 15 octets, le nombre réel que vous devez append pour obtenir un alignement de N est max (0, NM) où M est l’alignement naturel de l’allocateur de mémoire (et les deux sont des puissances de 2).
Puisque l’alignement minimal de la mémoire de tout allocateur est de 1 octet, 15 = max (0,16-1) est une réponse prudente. Cependant, si vous savez que votre allocateur de mémoire va vous donner des adresses alignées sur 32 bits (ce qui est assez fréquent), vous auriez pu utiliser 12 comme pad.
Ce n’est pas important pour cet exemple, mais cela peut être important sur un système embarqué avec 12 Ko de mémoire vive, où chaque fichier enregistré est important.
La meilleure façon de l’implémenter si vous voulez réellement enregistrer chaque octet possible est une macro afin de pouvoir l’aligner avec l’alignement de votre mémoire native. Encore une fois, cela n’est probablement utile que pour les systèmes embarqués sur lesquels vous devez enregistrer chaque octet.
Dans l’exemple ci-dessous, sur la plupart des systèmes, la valeur 1 convient parfaitement à MEMORY_ALLOCATOR_NATIVE_ALIGNMENT
, mais pour notre système embarqué théorique avec des allocations alignées sur 32 bits, les éléments suivants pourraient permettre d’économiser un 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)
Peut-être auraient-ils été satisfaits d’une connaissance de memalign ? Et comme le souligne Jonathan Leffler, il y a deux nouvelles fonctions préférables à connaître.
Oups, Florin m’a battu. Cependant, si vous lisez la page de manuel à laquelle je suis lié, vous comprendrez probablement l’exemple fourni par une affiche antérieure.
Nous faisons ce genre de choses tout le temps pour Accelerate.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 plus haut.
La méthode la plus rapide pour un petit tableau comme celui-ci consiste simplement à la coller sur la stack. Avec GCC / clang:
void my_func( void ) { uint8_t array[1024] __atsortingbute__ ((aligned(16))); ... }
Non gratuit () requirejs. Il s’agit généralement de deux instructions: soustrayez 1024 du pointeur de stack, puis AND le pointeur de stack avec -alignment. Vraisemblablement, le demandeur avait besoin des données sur le tas car sa durée de vie dépassait celle de la stack, ou la récursivité était à l’œuvre ou l’espace de la stack était très élevé.
Sur OS X / iOS, tous les appels vers malloc / calloc / etc. sont toujours alignés sur 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);
Certaines personnes ont mentionné l’interface C ++ qui fonctionne de manière similaire.
Il ne faut pas oublier que les pages sont alignées sur de grandes puissances de deux, donc les tampons alignés sur la page sont également alignés sur 16 octets. Ainsi, mmap () et valloc () et d’autres interfaces similaires sont également des options. mmap () a l’avantage que le tampon peut être alloué préinitialisé avec quelque chose de non nul, si vous voulez. Étant donné que ceux-ci ont une taille d’alignement de page, vous n’obtiendrez pas l’allocation minimale de ceux-ci et il sera probablement soumis à une erreur de VM la première fois que vous la toucherez.
Cheesy: Allumez le garde malloc ou similaire. Les tampons de taille n * 16 octets tels que celui-ci seront alignés sur n * 16, car la VM est utilisée pour intercepter les dépassements et ses limites se situent aux limites de la page.
Certaines fonctions de Accelerate.framework intègrent un tampon temporaire fourni par l’utilisateur à utiliser comme espace de travail. Ici, nous devons supposer que le tampon qui nous a été transmis est extrêmement mal aligné et que l’utilisateur essaie activement de rendre notre vie difficile, malgré le dépit. (Nos cas de test collent une page de garde juste avant et après le tampon temporaire pour souligner le caractère négatif). Ici, nous retournons la taille minimale nécessaire pour garantir un segment aligné de 16 octets, puis alignons manuellement le tampon. Cette taille est desire_size + alignment – 1. Donc, dans ce cas, c’est 1024 + 16 – 1 = 1039 octets. Alignez ensuite comme suit:
#include void My_func( uint8_t *tempBuf, ... ) { uint8_t *alignedBuf = (uint8_t*) (((uintptr_t) tempBuf + ((uintptr_t)alignment-1)) & -((uintptr_t) alignment)); ... }
L’ajout de alignment-1 déplacera le pointeur au-delà de la première adresse alignée, puis ANDing with -alignment (par exemple, 0xfff … ff0 pour l’alignement = 16) le ramènera à l’adresse alignée.
Comme décrit par d’autres articles, sur d’autres systèmes d’exploitation sans garantie d’alignement sur 16 octets, vous pouvez appeler malloc avec la plus grande taille, mettre le pointeur de free () plus tard, puis aligner comme décrit ci-dessus et utiliser le pointeur aligné. décrit pour notre cas tampon temporaire.
En ce qui concerne alignment_memset, c’est plutôt idiot. Vous devez seulement boucler jusqu’à 15 octets pour atteindre une adresse alignée, puis continuer avec les magasins alignés 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 magasins non alignés 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. Cependant, si l’intervieweur veut savoir si vous êtes à l’aise avec stdint.h, les opérateurs binarys et les bases de la mémoire, il est probablement raisonnable de poser des questions.
Je suis surpris que personne n’ait voté pour la réponse de Shao selon laquelle, si je comprends bien, il est impossible de faire ce qui est demandé dans la norme C99, car convertir un pointeur en un type intégral est un comportement indéfini. (Mis à part la norme permettant la conversion de uintptr_t
<-> void*
, mais la norme ne semble pas permettre de manipuler la valeur de uintptr_t
et de la reconvertir.)
L’utilisation de memalign, Aligned-Memory-Blocks pourrait être une bonne solution pour le problème.
La première chose qui m’a frappé lors de la lecture de cette question a été de définir une structure alignée, de l’instancier, puis de la pointer.
Y a-t-il une raison fondamentale qui me manque, car personne d’autre ne l’a suggéré?
En tant qu’autre note, comme j’ai utilisé un tableau de caractères (en supposant que le caractère du système est de 8 bits (1 octet)), je ne vois pas nécessairement la nécessité de l’ atsortingbut ((compressé)) (corrigez-moi si je me trompe) ), mais je le mets de toute façon.
Cela fonctionne sur deux systèmes, je l’ai essayé, mais il est possible qu’il y ait une optimisation du compilateur que je ne suis pas au courant de me donner des faux positifs par rapport à l’efficacité du code. J’ai utilisé gcc 4.9.2 sur OSX et gcc 5.2.1 sur Ubuntu.
#include #include int main () { void *mem; void *ptr; // answer a) here struct __atsortingbute__((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; }
MacOS X spécifique:
C11 est pris en charge, vous pouvez donc simplement appeler alignment_malloc (16, taille).
MacOS X choisit le code optimisé pour les processeurs individuels au démarrage pour memset, memcpy et memmove et ce code utilise des astuces dont vous n’avez jamais entendu parler pour le rendre rapide. 99% de chances que memset s’exécute plus vite que n’importe quel memset écrit à la main16, ce qui rend toute la question inutile.
Si vous voulez une solution 100% portable, il n’y en a pas avant C11. Parce qu’il n’y a pas de moyen portable pour tester l’alignement d’un pointeur. S’il ne doit pas être portable à 100%, 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 unsigned int. La conversion en non signé int perd les informations et est définie par l’implémentation, mais cela n’a pas d’importance car nous ne convertissons pas le résultat en pointeur.
L’horrible est bien sûr que le pointeur d’origine doit être enregistré quelque part pour appeler free () avec lui. Donc, dans l’ensemble, je doute vraiment de la sagesse de cette conception.
Utilisez simplement memalign? http://linux.die.net/man/3/memalign
Vous pouvez également append environ 16 octets puis pousser le ptr d’origine vers 16 bits en ajoutant le (16-mod) 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); }
S’il y a des contraintes, vous ne pouvez pas gaspiller un seul octet, alors cette solution fonctionne: Note: Il y a un cas où cela peut être exécuté à l’infini: D
void *mem; void *ptr; try: mem = malloc(1024); if (mem % 16 != 0) { free(mem); goto try; } ptr = mem; memset_16aligned(ptr, 0, 1024);
For the solution i used a concept of padding which aligns the memory and do not waste the memory of a single byte .
If there are constraints that, you cannot waste a single byte. All pointers allocated with malloc are 16 bytes aligned.
C11 is supported, so you can just call aligned_malloc (16, size).
void *mem = malloc(1024+16); void *ptr = ((char *)mem+16) & ~ 0x0F; memset_16aligned(ptr, 0, 1024); free(mem);
long add; mem = (void*)malloc(1024 +15); add = (long)mem; add = add - (add % 16);//align to 16 byte boundary ptr = (whatever*)(add);