Réponse originale à cette question
{
void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
Fixe répondre
{
void *mem = malloc(1024+15);
void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
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. (À noter qu' 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 de long, double, long double, long de long. 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 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 à l' free()
d'une valeur que l'un d' malloc()
, calloc()
ou realloc()
retourné pour vous - tout le reste est une catastrophe. Vous avez correctement fournies mem
à juger que la valeur - je vous remercie. Les versions libres.
Enfin, si vous savez sur le fonctionnement interne de votre système 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 -- autres malloc
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.
Encore 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 à l'aide d' 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 de l' PRIXPTR
dans la printf()
des déclarations, il serait suffisant d' #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.]
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
}
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", mem, ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
return(0);
}
Et voici un peu plus de version généralisée, qui va travailler pour les tailles qui sont une puissance de 2:
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
}
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", mem, 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()
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.
- Clairement, il est possible d'allouer de 1024 octets et ensuite donner l'adresse de l'alignement de traitement"; le problème avec cette approche est que l'espace disponible n'est pas bien déterminée (l'espace utilisable est entre 1008 et 1024 octets, mais il n'y avait pas un mécanisme disponible pour spécifier la taille), ce qui la rend moins utile.
- Une autre possibilité est que vous êtes censé écrire un plein allocateur de mémoire et de s'assurer que le bloc de 1 024 octets de votre retour est correctement aligné. Si c'est le cas, vous probablement jusqu'à la fin de faire une opération assez similaire à ce que la solution proposée n', mais vous cacher à l'intérieur de l'allocateur.
Toutefois, 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.)