Comment allouer aligné à la mémoire qu'à l'aide de la bibliothèque standard?
Je viens de terminer un test dans le cadre d'une entrevue d'emploi, et une question sans réponse moi, même à l'aide de Google pour référence. J'aimerais voir ce que le StackOverflow l'équipage peut faire avec elle:
La
memset_16aligned
fonction nécessite une 16 octets aligné pointeur est passé, ou il va se planter.a) Comment souhaitez vous allouer 1024 octets de mémoire, et d'aligner un 16 frontière d'octet?
b) libérer la mémoire après lamemset_16aligned
a exécuté.
{
void *mem;
void *ptr;
//answer a) here
memset_16aligned(ptr, 0, 1024);
//answer b) here
}
- hmmm...à long terme du code de la viabilité, comment au sujet de "le Feu celui qui a écrit memset_16aligned et de le réparer ou de le remplacer à ce qu'il n'a pas de limite particulière de la condition"
- Certainement une bonne question à poser: "pourquoi l'étrange alignement de la mémoire". Mais il peut y avoir de bonnes raisons pour cela - dans ce cas, il se pourrait que la memset_16aligned() peut utiliser 128 bits entiers et c'est plus facile si la mémoire est connu pour être aligné. Etc.
- Celui qui a écrit memset pourrait usage interne de 16 octets d'alignement pour la compensation de la boucle intérieure et un petit prologue/épilogue pour nettoyer les non-alignés se termine. Ce serait beaucoup plus facile que de faire des codeurs manipule des pointeurs de mémoire.
- Juste
malloc(1024);
. Tous lesmalloc(3)
implémentations sur les systèmes modernes sont déjà au moins ce aligné de toute façon. - Pourquoi quelqu'un voudrait-il des données alignées à 16 frontière d'octet? Probablement pour le charger dans 128 bits des registres SSE. Je crois que le (nouveau) non alignés movs (par exemple, movupd, lddqu) sont plus lents, ou peut-être qu'ils sont le ciblage des processeurs sans SSE2/3
- L'alignement de l'adresse mène à l'optimisation de l'utilisation du cache, ainsi qu'à l'augmentation de la bande passante entre les différents niveaux de cache et la RAM (pour la plupart des charges de travail courantes). Voir ici stackoverflow.com/questions/381244/purpose-of-memory-alignment
- Si il y a une justice dans le monde, la réponse est
ptr=malloc_16aligned(1024);
etfree_16aligned(ptr)
parce que quelqu'un qui écrit un utilisable de la bibliothèque doit fournir des services publics pour y accéder. NB: si malloc_16aligned(.) des appels à malloc(.) il a besoin d'ajouter une surcharge de sortefree_16aligned(ptr)
peut annuler le décalage deptr
et appel gratuit(.). - Valide les cas d'utilisation pour exiger de 16 octets aligné mémoire lorsque vous souhaitez l'aide d'un code pour être capable de courir très vite sans avoir à faire des contrôles supplémentaires. Apple Métal graphiques-cadre (similaire à Vulkan) a cette exigence dans certains endroits que vous ne pouvez pas vraiment se plaindre, étant donné que le Métal est conçue pour les appareils avec un partage des CPU/GPU de la RAM (et donc, zéro-copie de transfert, de l'accès de verrouillage/déverrouillage et la mutation des mises à jour).
Vous devez vous connecter pour publier un commentaire.
Réponse originale à cette question
Fixes réponse
Explication comme demandé
La première étape consiste à allouer suffisamment d'espace disque de secours, juste au cas où. Depuis le mémoire doit être de 16 octets alignés (ce qui signifie que le premier octet de l'adresse doit être un multiple de 16), l'ajout de 16 octets supplémentaires garanties que nous avons assez d'espace. Quelque part dans les 16 premiers octets 16 octets aligné pointeur. (Notez que
malloc()
est censé renvoyer un pointeur qui est suffisamment bien alignés pour tout but. Cependant, la signification de "toute" 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 de fonctions. Quand vous faites plus de choses spécialisées, comme jouer avec des systèmes graphiques, ils peuvent avoir besoin de plus strictes de l'alignement que le reste du système, d'où les questions et les réponses de ce genre.)La prochaine étape est de convertir le vide pointeur vers un pointeur de char; GCC malgré tout, vous n'êtes pas censé faire de l'arithmétique des pointeurs sur des pointeurs void (et GCC possède des options d'alerte pour vous avertir lorsque vous en abuser). Puis ajouter 16 au début de pointeur. Supposons que
malloc()
retour, un incroyablement mal alignées pointeur: 0x800001. Ajout du 16 donne 0x800011. Maintenant, je veux arrondir à l'16-frontière d'octet — donc, je veux réinitialiser les 4 derniers bits à 0. 0x0F a les 4 derniers bits de l'un; par conséquent,~0x0F
a tous les bits mis à part le dernier de quatre. Anding qu'avec 0x800011 donne 0x800010. Vous pouvez itérer sur les autres compensations et de voir que la même arithmétique des œuvres.La dernière étape,
free()
, c'est facile: vous pour toujours, et uniquement, de retour àfree()
une valeur que l'un desmalloc()
,calloc()
ourealloc()
retourné pour vous — tout le reste est une catastrophe. Vous avez bien fournimem
de tenir la valeur — je vous remercie. Les versions libres il.Enfin, si vous savez sur le fonctionnement interne de votre système de
malloc
package, vous pouvez deviner qu'il pourrait bien revenir sur 16 octets données alignées (ou il pourrait être alignée 8 octets). Si elle était de 16 octets alignés, alors vous auriez pas besoin de dink avec les valeurs. Cependant, ce qui est douteux et non-portable — autresmalloc
de paquets qui ont minimum des alignements, et, par conséquent, en supposant une chose quand il fait quelque chose de différent conduirait à des core dumps. Dans de larges limites, cette solution est portable.Quelqu'un d'autre a mentionné
posix_memalign()
comme une autre façon d'obtenir l'alignement mémoire, qui n'est pas disponible partout, mais peut souvent être mise en œuvre à l'aide de cette base. Notez qu'il est commode que l'alignement est une puissance de 2; d'autres alignements sont messier.Un commentaire — ce code n'est pas de vérifier que la répartition réussi.
Amendement
Programmeur Informatique remarquer que vous ne pouvez pas faire de masque de bits des opérations sur les pointeurs, et, en effet, GCC (3.4.6 et 4.3.1 testé) ne se plaignent comme ça. Donc, une version modifiée de la base de code est converti en un programme principal, de ce qui suit. J'ai aussi pris la liberté d'ajouter 15 au lieu de 16, comme cela a été souligné. Je suis en utilisant
uintptr_t
depuis C99 a été autour assez longtemps pour être accessible sur la plupart des plateformes. Si ce n'était pas pour l'utilisation dePRIXPTR
dans leprintf()
déclarations, il serait suffisante pour#include <stdint.h>
au lieu d'utiliser#include <inttypes.h>
. [Ce code inclut le correctif a été souligné par C. R., qui a été réitérant un point en premier lieu par Le Projet De Loi K un certain nombre d'années, j'ai réussi à l'ignorer jusqu'à maintenant.]Et voici un peu plus de version généralisée, qui va travailler pour les tailles qui sont une puissance de 2:
Pour convertir
test_mask()
dans un objectif général de fonction d'allocation, la seule valeur de retour de l'allocateur aurait pour encoder la libération de l'adresse, comme plusieurs personnes nous ont indiqué dans leurs réponses.Problèmes avec les enquêteurs
Uri a commenté: Peut-être que je vais avoir [un] problème de compréhension de la lecture ce matin, mais si la question de l'entrevue spécifiquement dit: "Comment voulez-vous allouer 1024 octets de mémoire" et de toute évidence, vous affecter plus que ça. Ce ne serait pas un échec automatique de l'interviewer?
Ma réponse ne rentre pas dans l'un des 300 caractère de commentaire...
Ça dépend, je suppose. Je pense que la plupart des gens (moi y compris) ont pris la question à se dire "Comment voulez-vous allouer un espace dans lequel 1024 octets de données peuvent être stockées, et où l'adresse de base est un multiple de 16 octets". Si l'interviewer signifiait vraiment comment pouvez-vous attribuer 1024 octets (uniquement) et de l'avoir 16 octets alignés, les options sont plus limitées.
Cependant, si l'intervieweur devrait l'un de ces réponses, je m'attends à reconnaître que cette solution répond à un étroitement liés à la question, puis de recadrer leur question de point de la conversation dans la bonne direction. (De plus, si l'enquêteur a vraiment stroppy, alors je ne voudrais pas le travail; si la réponse à une insuffisamment précis exigence est abattu en flammes, sans correction, alors que l'interviewer n'est pas quelqu'un pour qui il est sécuritaire pour le travail.)
Le monde se déplace sur
Le titre de la question a récemment changé. Il a été Résoudre l'alignement de la mémoire en C les questions de l'entrevue que perplexe moi. Le titre révisé (Comment allouer aligné à la mémoire qu'à l'aide de la bibliothèque standard?) demandes a légèrement révisé à la réponse — le présent avenant prévoit.
C11 (ISO/IEC 9899:2011) ajout de la fonction
aligned_alloc()
:Et POSIX définit
posix_memalign()
:Un ou l'autre ou les deux de ces pourrait être utilisé pour répondre à la question maintenant, mais seulement la fonction POSIX était une option lorsque la question était à l'origine une réponse.
Derrière les coulisses, le nouveau aligné fonction de mémoire en faire de même de l'emploi comme indiqué dans la question, sauf qu'ils ont la capacité de forcer l'alignement de plus en plus facilement, et de garder trace de début de l'alignement de la mémoire interne de sorte que le code n'a pas à traiter spécialement juste que cela permet de libérer la mémoire retourné par la fonction d'allocation qui a été utilisé.
double
exige normalement de 8 octets d'alignement et les données renvoyées parmalloc()
est suffisamment harmonisées pourdouble
; et (b) les constantes d'ajouter serait de 3 ou 4 et le masque 0x03. Si vous avez d'autres sens pour 32 bits alignés adresse, veuillez préciser. (Je pourrais concevoir de vous dire un 4 GiB l'alignement, mais il semble un peu improbable, etmalloc()
peut ne pas fonctionner de manière fiable et que beaucoup de mémoire!).printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", mem, ptr);
n'est pas droit, est-il? Changer l'printf("%x PRIXPTR , %x PRIXPTR \n", mem, ptr);
rend compiler sur ma machine<inttypes.h>
de C99 disponibles (au moins pour la chaîne de format — sans doute, les valeurs doivent être transmises avec un cast:(uintptr_t)mem, (uintptr_t)ptr
). La chaîne de format s'appuie sur la concaténation de chaîne et le PRIXPTR macro est la bonneprintf()
longueur et spécificateur de type hex de sortie pour unuintptr_t
valeur. L'alternative est d'utiliser%p
mais la sortie de qui varie selon la plate-forme (certains ajoutent un0x
, la plupart ne le font pas) et est généralement écrit avec des majuscules chiffres hexadécimaux, dont je n'aime pas; ce que j'ai écrit est uniforme sur l'ensemble des plates-formes.uintptr_t
trop avant de le not au niveau du bit et ET le fonctionnement parce que certains compilateurs traiter un entier littéral 0x0F 32 bits.&
est OK; car d'un côté est déjà converti àuintptr_t
, le compilateur doit convertir l'autre àuintptr_t
trop, de sorte que0x0F
est gérée correctement. Cependant, je ne peux pas soustraire à la bit-à-bit~
problème si facilement; vous avez raison que devrait être lancé (il doit être un plâtre; il n'y a pas fiable suffixe notation commeULL
qui fonctionne pour toutes les variantes possibles deuintptr_t
) avant d'être au niveau du bit inversé. Merci — bien fait pour les taches il.15
et0xF
d'étendre ce code en toute sécurité des choses comme 32, 48 et 64 octets de l'alignement? Merci!uintptr_t
valeur correspondant à l'adresse. Changer les constantes de manipuler les puissances de deux (qui sont contraints d'être âgé d'au moins 16) est assurée par le dernier exemple de code. La manipulation d'un multiple de 48 nécessiterait général de l'arithmétique. Pour l'allocation de S octets alignée sur une frontière d'octet N, allouer (S + N - 1) octets, ce qui serait alloué à l'adresse de M, et de retour ((M + N - 1) / N) * N, avec un traitement adapté des moulages, etc.#define __STDC_FORMAT_MACROS
PRIXPTR
sont disponibles àg++
sans préciser__STDC_FORMAT_MACROS
. Je n'ai besoin de fixer les appels àprintf()
pour inclure le(uintptr_t)
jette. Le code compile correctement avecgcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror memalign2.c -o memalign2
etg++ -O3 -g -std=c++11 -Wall -Wextra -Werror memalign3.cpp -o memalign3
.#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS)
__STDC_FORMAT_MACROS
. (Note de bas de page 182: C++ implémentations devraient définir ces macros uniquement lorsque__STDC_FORMAT_MACROS
est défini avant<inttypes.h>
est inclus.) C11 ne contient pas que note de bas de page. Je suppose que l'environnement, il fonctionne en prend à cœur; il semble que ce soit GCC 4.9.1 (contre 4.8.1) ou Mac OS X 10.9.5 (par rapport à Windows 7) suit la norme C11 plutôt que le standard C99. Je suppose que c'est intéressant de souligner, bien que la question est en fait C plutôt qu'en C++.posix_memalign()
. Vous avez raison, mais je ne pense pas que c'est la matière. Il serait possible de concevoir une solution qui transforme le pointeur vers unchar *
, ajoute un nombre d'octets calculée d'une manière similaire à ce qui est fait maintenant, et puis la convertit enchar *
retour à unvoid *
, si vous souhaitez être plus sûr. Dans la pratique, je ne pense pas que vous trouverez un compilateur qui a des problèmes. Si il y en avait un, il serait quelque chose comme celui que j'ai appris le C, où octet pointeurs avaient des valeurs différentes du mot "pointeurs" pour la même adresse.& -16
depuis la promotion d'un type non signé doit obtenir la valeur de ce type qui, lorsqu'il est ajouté à 16, donnerait à zéro.char
type. Si vous entendez par "maximum" ou "plus strictes" l'alignement, il n'y a pas un moyen simple, mais vous pouvez généralement obtenir les informations d'une union à l'aide delong double
,double
,void *
,void (*)(void)
,long long
,long
— il est peu probable que vous aurez besoin d'autres types. Créer une union de ces types deunion Align { … };
; créer une structurestruct Alignment { char c; union Align u; }
. La valeur deoffsetof(struct Alignment, u)
vous donne le maximum d'alignement exigence sur votre plate-forme.uintptr_t
peut pas stocker un(void *)
dans la manière évidente - pensent que les premières machines RISC où tout était à 4 ou 8 octets aligné, etchar
/short
l'accès était synthétique...Trois des 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 Jonathan Leffler de solution, sauf que le round 16-alignés, vous n'avez besoin que de 15 octets supplémentaires, pas 16.
Un:
B:
2) Pour plus d'informations génériques fonction d'allocation mémoire, l'appelant ne voulez pas avoir à garder une trace de deux pointeurs (une et une seule de libre). Si vous stocker un pointeur vers le "vrai" tampon au-dessous de l'alignement de la mémoire tampon.
Un:
B:
Noter que, contrairement à (1), où seulement 15 octets ont été ajoutés à mem, ce code pourrait effectivement réduire l'alignement si votre mise en œuvre qui se passe pour la garantie de 32 octets de l'alignement de malloc (peu probable, mais, en théorie, un C mise en œuvre pourrait avoir un 32 octets aligné type). Ce n'est pas grave si tout ce que vous faire est d'appeler memset_16aligned, mais si vous utilisez la mémoire pour une structure (struct), alors il pourrait avoir son importance.
Je ne suis pas sûr de la main gauche ce qu'est un bon correctif est pour cela (autres que pour avertir l'utilisateur que le tampon de retour n'est pas forcément adapté pour arbitraire des structures) car il n'y a aucun moyen de déterminer par programmation à ce que la mise en œuvre spécifique de l'alignement de garantie. Je pense au démarrage, vous pourrait affecter deux ou plus de 1 octet, tampons, et à supposer que le pire de l'alignement que vous voyez est la garantie de l'alignement. Si vous vous trompez, vous perdez la mémoire. Quelqu'un à une meilleure idée, merci de le dire...
[Ajouté:
La 'norme' astuce consiste à créer une union devrait être au maximum de alignées types " pour déterminer la condition d'alignement. 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 delong long
(et, sur le Pouvoir 6 (AIX), les appareils deintmax_t
serait de vous donner un cryptage de 128 bits type entier). L'alignement des exigences pour que l'union peut être déterminé en l'intégrant dans une structure avec un seul char, suivi par l'union:Ensuite, vous devez utiliser la plus grande de demande d'alignement (dans l'exemple, 16) et le
align
valeur calculée ci-dessus.Sur (64-bit) Solaris 10, il semble que la base de l'alignement pour le résultat de
malloc()
est un multiple de 32 octets.]
Dans la pratique, aligné allocateurs souvent prendre un paramètre pour l'alignement plutôt que ce soit câblé. 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) Utiliser ce que votre plate-forme offre:
posix_memalign
de POSIX,_aligned_malloc
sur Windows.4) Si vous utilisez C11, puis le plus propre portable et concis - option consiste à utiliser la bibliothèque standard de la fonction
aligned_alloc
qui a été introduite dans cette version de la spécification du langage.ASSERT(mem);
pour vérifier les résultats de l'allocation;assert
est pour la capture des erreurs de programmation et non le manque de ressources en temps.assert
. Je ne vais pas écrire un décoché l'allocation, mais en général, il n'est pas possible de deviner combien de questionneurs " programmes pourra gérer l'échec d'allocation de mémoire. Dans ce cas cependant, depuis le deuxième cas est une affectation de la routine, je suppose que je peux deviner - retour d'un pointeur null est un moyen raisonnable pour indiquer l'échec.char *
et unsize_t
entraînera une erreur. Vous devez utiliser quelque chose commeuintptr_t
.Vous pouvez également essayer de
posix_memalign()
(sur les plate-formes POSIX, bien sûr).Voici une autre approche de la 'tour' de la partie. Pas le plus brillamment codé solution, mais il fait le travail, et ce type de syntaxe est un peu plus facile à retenir (plus serait de travailler pour l'alignement des valeurs qui ne sont pas une puissance de 2). Le
uintptr_t
plâtre a été nécessaire pour apaiser le compilateur; l'arithmétique des pointeurs n'est pas très friands de la division ou la multiplication.Malheureusement, en C99, il semble assez difficile pour garantir l'alignement de toute sorte de manière à être utilisable sur tout C mise en œuvre conforme à la C99. Pourquoi? Parce qu'un pointeur n'est pas garanti d'être le "adresse de l'octet", on peut imaginer qu'avec un modèle de mémoire fixe. Ni est la représentation de uintptr_t donc garanti, qui lui-même est une option de type de toute façon.
Nous sachions de certaines implémentations qui utilisent une représentation pour void * (et par définition, aussi char*), qui est une simple adresse de l'octet, mais en C99, il est opaque, à nous, les programmeurs. Une mise en œuvre pourrait représenter un pointeur par un ensemble {segment, décalage} où décalage pourrait avoir qui-sait-ce que l'alignement "dans la réalité." Pourquoi, un pointeur peut-être même une certaine forme de table de hachage valeur de recherche, ou même une liste liée valeur de recherche. Il pourrait coder les limites de l'information.
Dans un récent C1X projet pour une Norme C, nous voyons la _Alignas mot-clé. Qui pourrait aider un peu.
La seule garantie C99 nous donne est que les fonctions d'allocation de mémoire retournera un pointeur adapté pour l'affectation d'un pointeur pointant sur tout type d'objet. Puisque nous ne pouvons pas spécifier l'alignement des objets, nous ne pouvons pas mettre en œuvre nos propres fonctions d'allocation de la responsabilité de l'alignement dans un lieu bien défini, de manière portable.
Il serait bon de se tromper au sujet de cette allégation.
aligned_alloc()
. (C++11 / 14 / 1z ne comprends toujours pas)._Alignas()
et C++alignas()
ne font rien pour l'allocation dynamique, seulement pour automatique et statique de stockage (ou structure de mise en page).Sur les 16 contre 15 octet-comte rembourrage à l'avant, le nombre réel, vous devez ajouter à obtenir un alignement de N est max(0,N-M) où M est l'alignement naturel de l'allocateur de mémoire (et les deux sont des puissances de 2).
Depuis la mémoire minimale de l'alignement de l'allocation est de 1 octet, 15=max(0,16-1) est un conservateur de réponse. Toutefois, si vous savez que votre allocateur de mémoire va vous donner de 32 bits int adresses alignées (ce qui est assez fréquent), vous pourriez avoir utilisé 12 comme tampon.
Ce n'est pas important pour cet exemple, mais il peut être important sur un système embarqué avec 12K de RAM, où chaque int économisé compte.
La meilleure façon de mettre en œuvre si vous êtes réellement en train de tenter de sauver chaque octet est possible qu'une macro de sorte que vous pouvez nourrir votre propre alignement de la mémoire. Encore une fois, c'est probablement seulement utile pour les systèmes embarqués où vous avez besoin d'enregistrer chaque octet.
Dans l'exemple ci-dessous, sur la plupart des systèmes, la valeur 1 est tout simplement parfait pour
MEMORY_ALLOCATOR_NATIVE_ALIGNMENT
, cependant, pour notre théoriques système intégré avec 32 bits alignés les allocations, les activités suivantes pourraient économiser un tout petit peu de mémoire précieuse:Peut-être qu'ils auraient été satisfaits avec une connaissance de memalign? Et comme Jonathan Leffler, il y a deux nouveaux préférable fonctions à connaître.
Oups, florin me battre pour elle. Cependant, si vous lisez la page de manuel je lien, vous aurez plus de chances de comprendre l'exemple fourni par un précédent affiche.
memalign
fonction est obsolète etaligned_alloc
ouposix_memalign
devrait être utilisé à la place". Je ne sais pas ce qu'il a dit en octobre 2008 — mais il n'a probablement pas mentionneraligned_alloc()
comme cela a été ajoutée à C11.Nous faire ce genre de chose tout le temps pour Accélérer.cadre, un très vectorisé OS X /iOS bibliothèque, où nous devons prêter attention à l'alignement de tous les temps. Il existe assez peu d'options, un ou deux dont je n'ai pas vu mentionné ci-dessus.
La méthode la plus rapide pour un petit tableau comme ceci est juste le coller sur la pile. Avec GCC /clang:
Pas de free() nécessaire. C'est généralement de deux instructions: soustraire 1024 à partir du pointeur de pile, puis ET le pointeur de pile avec l'alignement. Sans doute que le demandeur a besoin de données sur le tas, car sa durée de vie de la matrice de dépassement de la pile ou de la récursivité est au travail ou à l'espace de pile est à une grave premium.
Sur OS X /iOS tous les appels à malloc/calloc/etc. sont toujours 16 octets aligné. Si vous avez besoin de 32 octets alignés pour AVX, par exemple, vous pouvez utiliser posix_memalign:
Certaines personnes ont mentionné l'interface C++ qui fonctionne de la même façon.
Il ne faut pas oublier que les pages sont alignés aux grandes puissances de deux, de sorte que la page aligné tampons sont également 16 octets aligné. Ainsi, mmap() et valloc() et d'autres interfaces sont également des options. mmap() a l'avantage de la mémoire tampon peut être alloué preinitialized avec quelque chose de non-zéro, si vous le souhaitez. Depuis ces pages alignées taille, vous ne serez pas obtenir l'allocation minimale de ceux-ci, et il pourrait être assujetti à une VM faute la première fois que vous la touchez.
De fromage: Tour de garde malloc ou similaire. Les tampons sont n*16 octets la taille comme celui-ci sera n*16 octets alignés, parce que la VM est utilisé pour attraper les dépassements et ses limites sont les limites de la page.
Certains Accélérer.cadre de prise de fonctions de l'utilisateur fourni temp tampon à utiliser comme espace de travail. Ici, nous devons supposer que la mémoire tampon passée pour nous est très mal alignées et l'utilisateur est activement en essayant de faire de notre vie dur de dépit. (Notre cas de test coller une page de garde avant et après la temp de la mémoire tampon pour souligner le dépit.) Ici, nous sommes de retour de la taille minimale, nous devons garantir un 16 octets aligné segment quelque part en elle, et puis à aligner manuellement la mémoire tampon par la suite. Cette taille est desired_size + alignement - 1. Donc, Dans ce cas c'est 1024 + 16 - 1 = 1039 octets. Alignez ensuite de la manière suivante:
L'ajout d'alignement-1 déplacez le pointeur passé la première adresse alignée et puis ANDing avec d'alignement (par exemple 0xfff...ff0 pour l'alignement=16) ramène à l'adresse alignée.
Comme décrit par d'autres commentaires, sur d'autres systèmes d'exploitation sans de 16 octets alignement des garanties, vous pouvez appeler malloc avec la plus grande taille, mettre de côté le pointeur pour free() plus tard, puis alignez comme décrit immédiatement au-dessus et utiliser le alignés pointeur, comme décrit pour le temp tampon cas.
Comme pour aligned_memset, c'est plutôt ridicule. Vous n'avez qu'à boucle jusqu'à 15 octets pour atteindre une adresse alignée, et ensuite alignées, les magasins après que, avec un possible code de nettoyage à la fin. Vous pouvez même faire le nettoyage de bits dans le vecteur de code, soit comme non alignés magasins qui chevauchent les alignés de région (en fournissant la longueur est au moins la longueur d'un vecteur) ou en utilisant quelque chose comme movmaskdqu. Quelqu'un vient juste d'être paresseux. Cependant, il est sans doute raisonnable de la question de l'entrevue si l'intervieweur veut savoir si vous êtes à l'aise avec stdint.h, opérateurs au niveau du bit et de la mémoire des fondamentaux, de sorte que l'exemple artificiel peut être pardonné.
Je suis surpris que personne n'a voté jusqu' Shao's réponse que, comme je le comprends, il est impossible de faire ce qui est demandé dans la norme C99, depuis la conversion d'un pointeur vers un type intégral officiellement est un comportement indéfini. (En dehors de la norme permettant la conversion de
uintptr_t
<->void*
, mais la norme ne semble pas permettre de faire toutes les manipulations de lauintptr_t
valeur et de le convertir ensuite en arrière.)unsigned char* myptr
; puis calculer `mptr += (16-(uintptr_t)my_ptr) & 0x0F, le comportement sera définie sur toutes les implémentations qui définissent my_ptr, mais si le pointeur résultant serait aligné dépend de la correspondance entre uintptr_t bits et des adresses.utilisation de memalign, Aligné À La Mémoire Des Blocs pourrait être une bonne solution pour le problème.
memalign
fonction est obsolète etaligned_alloc
ouposix_memalign
devrait être utilisé à la place". Je ne sais pas ce qu'il a déclaré en octobre 2010.La première chose qui surgit dans ma tête lors de la lecture de cette question était de définir l'alignement de structure, l'instancier, et puis point à elle.
Est là une raison fondamentale qui me manque, car personne d'autre ne l'a suggéré?
Au passage, depuis que j'ai utilisé un tableau de char (en supposant que le système du char est de 8 bits (c'est à dire 1 octet)), je ne vois pas la nécessité pour le attribut((emballé)) nécessairement (corrigez-moi si je me trompe), mais je l'ai mis dans de toute façon.
Cela fonctionne sur les deux systèmes, j'ai essayé sur, mais il est possible qu'il y est une optimisation du compilateur que je suis pas de me donner des faux positifs vis-a-vis de l'efficacité du code. J'ai utilisé gcc 4.9.2 sur OSX et gcc 5.2.1 sur Ubuntu.
MacOS X spécifique:
C11 est pris en charge, de sorte que vous pouvez les appeler aligned_malloc (16, taille).
MacOS X récupère le code est optimisé pour les différents processeurs au moment du démarrage pour memset, memcpy et memmove et que le code utilise des trucs que vous n'avez jamais entendu parler pour le faire rapidement. 99% de chance que memset court plus vite que tout écrit à la main memset16 qui rend l'ensemble de la question inutile.
Si vous voulez un 100% solution portable, avant C11 n'existe pas. Car il n'est pas portable moyen de tester l'alignement d'un pointeur. Si il n'a pas à être 100% portable, vous pouvez utiliser
Cela suppose que l'alignement d'un pointeur est stocké dans le bas de bits lors de la conversion d'un pointeur de type unsigned int. La conversion à unsigned int perd de l'information et de la mise en œuvre est définie, mais qui n'a pas d'importance parce que nous n'avons pas convertir le résultat un pointeur.
L'horrible partie est bien sûr qu'à l'origine le pointeur doit être enregistré quelque part pour appeler free () avec elle. Donc dans l'ensemble j'ai vraiment douter de la sagesse de cette conception.
aligned_malloc
dans OS X? Je suis en utilisant Xcode 6.1 et ce n'est pas définie n'importe où dans le SDK iOS, ni est-il déclaré n'importe où dans/usr/include/*
.aligned_alloc()
, mais qui n'est pas déclarée. De GCC 5.3.0, je reçois les messages intéressantsalig.c:7:15: error: incompatible implicit declaration of built-in function ‘aligned_alloc’ [-Werror]
etalig.c:7:15: note: include ‘<stdlib.h>’ or provide a declaration of ‘aligned_alloc’
. Le code n'a en effet inclure<stdlib.h>
, mais ni-std=c11
ni-std=gnu11
modifié les messages d'erreur.Suffit d'utiliser memalign? http://linux.die.net/man/3/memalign
memalign()
alloue size octets, et renvoie un pointeur vers la mémoire allouée". Vous devriez éviter d'utiliser des fonctions obsolètes dans le nouveau code, et devrait mettre à jour l'ancien code à utiliser un non-obsolète alternative (posix_memalign()
oualigned_alloc()
seraient de bons choix). Je ne sais pas ce qu'il a dit en septembre 2012.Vous pouvez également ajouter certains de 16 octets, et puis poussez l'original ptr 16 bits alignés par l'ajout du paragraphe (16 mod) comme ci-dessous le pointeur :
Si il y a des contraintes que vous ne pouvez pas gaspiller un seul octet, alors cette solution fonctionne:
Remarque: Il y a un cas où cela peut être exécutée à l'infini 😀
Pour la solution que j'ai utilisé un concept de rembourrage qui aligne la mémoire et ne pas gaspiller l'
la mémoire d'un seul octet .
Si il y a des contraintes que vous ne pouvez pas gaspiller un seul octet.
Tous les pointeurs alloué avec malloc de 16 octets alignés.
C11 est pris en charge, de sorte que vous pouvez les appeler aligned_malloc (16, taille).
malloc()
est en effet aligné sur 16 octets à la limite, mais rien dans la norme garantit — il va tout simplement être suffisamment bien alignés, pour toute utilisation, et sur de nombreux systèmes 32 bits alignant sur une limite de 8 octets est suffisant, et pour certains, une limite de 4 octets est suffisant.2 + (2%16) = 4