¿Cómo asignar memoria alineada solo usando la biblioteca estándar?

Acabo de terminar una prueba como parte de una entrevista de trabajo, y una pregunta me dejó perplejo, incluso usando google como referencia. Me gustaría ver lo que la tripulación stackoverflow puede hacer con él:

La función “memset_16aligned” requiere que se le pase un puntero alineado de 16 bytes, o se bloqueará.

a) ¿Cómo asignaría 1024 bytes de memoria y lo alinearía a un límite de 16 bytes?
b) Libere la memoria después de que se haya ejecutado memset_16aligned.

{ void *mem; void *ptr; // answer a) here memset_16aligned(ptr, 0, 1024); // answer b) here } 

Respuesta original

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

Respuesta fija

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

Explicación según lo solicitado

El primer paso es asignar suficiente espacio libre, por las dudas. Como la memoria debe estar alineada en 16 bytes (lo que significa que la dirección del byte principal debe ser un múltiplo de 16), agregar 16 bytes adicionales garantiza que tengamos suficiente espacio. En algún lugar de los primeros 16 bytes, hay un puntero alineado de 16 bytes. (Tenga en cuenta que se supone que malloc() devuelve un puntero que está suficientemente bien alineado para cualquier propósito. Sin embargo, el significado de ‘cualquiera’ es principalmente para cosas como tipos básicos: long , double , long double , long long y punteros a objetos y sugerencias para las funciones. Cuando realiza tareas más especializadas, como jugar con sistemas gráficos, puede necesitar una alineación más estricta que el rest del sistema, por lo tanto, preguntas y respuestas como esta).

El siguiente paso es convertir el puntero vacío en un puntero char; A pesar de GCC, no debe hacer aritmética de punteros en punteros vacíos (y GCC tiene opciones de advertencia para avisarle cuando lo abuse). Luego agrega 16 al puntero de inicio. Supongamos que malloc() devuelve un puntero imposiblemente mal alineado: 0x800001. Agregar el 16 da 0x800011. Ahora quiero redondear al límite de 16 bytes, por lo que quiero restablecer los últimos 4 bits a 0. 0x0F tiene los últimos 4 bits establecidos en uno; por lo tanto, ~0x0F tiene todos los bits configurados en uno excepto los últimos cuatro. Y eso con 0x800011 da 0x800010. Puedes iterar sobre las otras compensaciones y ver que funciona la misma aritmética.

El último paso, free() , es fácil: siempre, y solo, regresa a free() un valor que uno de malloc() , calloc() o realloc() devolvió, cualquier otra cosa es un desastre. Has proporcionado correctamente mem para mantener ese valor, gracias. El gratis lo libera.

Finalmente, si conoce las partes internas del paquete malloc de su sistema, podría adivinar que bien podría devolver datos alineados de 16 bytes (o podría estar alineado con 8 bytes). Si fuera alineado 16-byte, entonces no necesitarías soñar con los valores. Sin embargo, esto es dudoso y no portátil: otros paquetes malloc tienen diferentes alineaciones mínimas, y por lo tanto, asumir una cosa cuando hace algo diferente daría lugar a volcados centrales. Dentro de amplios límites, esta solución es portátil.

Alguien más mencionó posix_memalign() como otra forma de obtener la memoria alineada; eso no está disponible en todas partes, pero a menudo podría implementarse usando esto como base. Tenga en cuenta que era conveniente que la alineación fuera una potencia de 2; otras alineaciones son más desordenadas.

Un comentario más: este código no verifica que la asignación tuvo éxito.

Enmienda

El Progtwigdor de Windows señaló que no se pueden hacer operaciones de máscara de bits en los punteros, y, de hecho, GCC (3.4.6 y 4.3.1 probado) se queja de esa manera. Entonces, sigue una versión enmendada del código básico, convertida en un progtwig principal. También me he tomado la libertad de agregar solo 15 en vez de 16, como se ha señalado. Estoy usando uintptr_t ya que C99 ha estado disponible el tiempo suficiente para ser accesible en la mayoría de las plataformas. Si no fuera por el uso de PRIXPTR en las instrucciones printf() , sería suficiente #include lugar de usar #include . [Este código incluye la solución señalada por CR , que reiteraba un punto hecho por primera vez por Bill K hace algunos años, que pude pasar por alto hasta ahora.]

 #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); } 

Y aquí hay una versión marginalmente más generalizada, que funcionará para tamaños que son un poder 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); } 

Para convertir test_mask() en una función de asignación de propósito general, el único valor de retorno del asignador tendría que codificar la dirección de liberación, como varias personas han indicado en sus respuestas.

Problemas con los entrevistadores

Uri comentó: Tal vez estoy teniendo [un] problema de comprensión de lectura esta mañana, pero si la pregunta de la entrevista dice específicamente: “¿Cómo asignaría 1024 bytes de memoria?” Y claramente asigna más que eso. ¿No sería eso una falla automática del entrevistador?

Mi respuesta no cabe en un comentario de 300 caracteres …

Depende, supongo. Creo que la mayoría de la gente (incluyéndome a mí) tomó la pregunta para decir “¿Cómo asignarías un espacio en el que se pueden almacenar 1024 bytes de datos y donde la dirección base es un múltiplo de 16 bytes?”. Si el entrevistador realmente quiso decir cómo puede asignar 1024 bytes (solo) y alinearlo a 16 bytes, entonces las opciones son más limitadas.

  • Claramente, una posibilidad es asignar 1024 bytes y luego darle a esa dirección el ‘tratamiento de alineación’; el problema con ese enfoque es que el espacio disponible real no está determinado adecuadamente (el espacio utilizable está entre 1008 y 1024 bytes, pero no había un mecanismo disponible para especificar qué tamaño), lo que lo hace menos útil.
  • Otra posibilidad es que se espera que escriba un asignador de memoria completa y asegúrese de que el bloque de 1024 bytes que devuelve esté alineado correctamente. Si ese es el caso, probablemente termines haciendo una operación bastante similar a la que hizo la solución propuesta, pero la escondes dentro del asignador.

Sin embargo, si el entrevistador esperaba cualquiera de esas respuestas, esperaría que reconocieran que esta solución responde una pregunta estrechamente relacionada, y luego replanteen su pregunta para dirigir la conversación en la dirección correcta. (Además, si el entrevistador se pusiera realmente malhumorado, no me gustaría el trabajo, si la respuesta a un requisito insuficientemente preciso se derriba en llamas sin corrección, entonces el entrevistador no es alguien para quien sea seguro trabajar).

El mundo avanza

El título de la pregunta ha cambiado recientemente. Fue Solve la alineación de la memoria en la pregunta de la entrevista C lo que me dejó perplejo . El título revisado ( ¿Cómo asignar la memoria alineada solo utilizando la biblioteca estándar? ) Exige una respuesta ligeramente revisada, esta adición lo proporciona.

C11 (ISO / IEC 9899: 2011) agregó la función aligned_alloc() :

7.22.3.1 La función aligned_alloc

Sinopsis

 #include  void *aligned_alloc(size_t alignment, size_t size); 

Descripción
La función aligned_alloc asigna espacio para un objeto cuya alineación se especifica mediante alignment , cuyo tamaño se especifica por size y cuyo valor es indeterminado. El valor de alignment debe ser una alineación válida soportada por la implementación y el valor de size debe ser un múltiplo integral de alignment .

Devoluciones
La función aligned_alloc devuelve un puntero nulo o un puntero al espacio asignado.

Y POSIX define posix_memalign() :

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

DESCRIPCIÓN

La función posix_memalign() asignará bytes de size alineados en un límite especificado por alignment , y devolverá un puntero a la memoria asignada en memptr . El valor de alignment debe ser una potencia de dos múltiplos de sizeof(void *) .

Al completar con éxito, el valor apuntado por memptr debe ser un múltiplo de alignment .

Si el tamaño del espacio solicitado es 0, el comportamiento está definido por la implementación; el valor devuelto en memptr debe ser un puntero nulo o un puntero único.

La función free() desasignará la memoria que previamente ha sido asignada por posix_memalign() .

VALOR DEVUELTO

Al completar con éxito, posix_memalign() devolverá cero; de lo contrario, se devolverá un número de error para indicar el error.

Cualquiera de estos o ambos podrían usarse para responder la pregunta ahora, pero solo la función POSIX era una opción cuando la pregunta fue respondida originalmente.

Detrás de escena, la nueva función de memoria alineada hace el mismo trabajo que se describe en la pregunta, excepto que tienen la capacidad de forzar la alineación más fácilmente, y realizar un seguimiento interno del inicio de la memoria alineada para que el código no lo haga tiene que tratar especialmente: simplemente libera la memoria devuelta por la función de asignación que se utilizó.

Tres respuestas ligeramente diferentes dependiendo de cómo veas la pregunta:

1) Lo suficientemente bueno para que la pregunta exacta sea la solución de Jonathan Leffler, excepto que para redondear a 16 alineados, solo necesitas 15 bytes adicionales, no 16.

UN:

 /* 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; 

SEGUNDO:

 free(mem); 

2) Para una función de asignación de memoria más genérica, la persona que llama no quiere tener que hacer un seguimiento de dos punteros (uno para usar y otro para liberar). Así que almacena un puntero al buffer ‘real’ debajo del buffer alineado.

UN:

 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; 

SEGUNDO:

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

Tenga en cuenta que a diferencia de (1), donde solo 15 bytes fueron agregados a la memoria, este código realmente podría reducir la alineación si su implementación garantiza la alineación de 32 bytes desde malloc (improbable, pero en teoría una implementación C podría tener un byte de 32 bytes) tipo alineado). Eso no importa si todo lo que haces es llamar a memset_16aligned, pero si usas la memoria para una estructura, entonces podría importar.

No estoy seguro de lo que es una buena solución para esto (que no sea advertir al usuario que el búfer devuelto no es necesariamente adecuado para estructuras arbitrarias) ya que no hay manera de determinar mediante progtwigción cuál es la garantía de alineación específica de la implementación. Supongo que al inicio podría asignar dos o más búferes de 1 byte, y asumir que la peor alineación que ve es la alineación garantizada. Si estás equivocado, desperdicias memoria. Alguien con una idea mejor, por favor dígalo …

[ Agregado : El truco ‘estándar’ es crear una unión de ‘tipos que probablemente sean los más alineados’ para determinar la alineación requerida. Es probable que los tipos alineados al máximo sean (en C99) ‘ long long ‘, ‘ long double ‘, ‘ void * ‘ o ‘ void (*)(void) ‘; si incluye , podría presumiblemente usar ‘ intmax_t ‘ en lugar de long long (y, en máquinas Power 6 (AIX), intmax_t le daría un tipo entero de 128 bits). Los requisitos de alineación para esa unión se pueden determinar incrustándolos en una estructura con un solo carácter seguido de la unión:

 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; 

A continuación, utilizaría el mayor de la alineación solicitada (en el ejemplo, 16) y el valor de align calculado anteriormente.

En Solaris 10 (64 bits), parece que la alineación básica para el resultado de malloc() es un múltiplo de 32 bytes.
]

En la práctica, los asignadores alineados a menudo toman un parámetro para la alineación en lugar de estar cableados. Entonces, el usuario pasará el tamaño de la estructura que le importa (o la menor potencia de 2 mayor o igual a eso) y todo estará bien.

3) Usa lo que proporciona tu plataforma: posix_memalign para POSIX, _aligned_malloc en Windows.

4) Si usa C11, la opción más limpia, portátil y concisa es usar la función de biblioteca estándar aligned_alloc que se introdujo en esta versión de la especificación del lenguaje.

También posix_memalign() probar posix_memalign() (en plataformas POSIX, por supuesto).

Aquí hay un enfoque alternativo a la parte de ‘redondear hacia arriba’. No es la solución más shinymente codificada, pero hace el trabajo, y este tipo de syntax es un poco más fácil de recordar (además funcionaría para valores de alineación que no son un poder de 2). El uintptr_t fue necesario para apaciguar al comstackdor; La aritmética del puntero no es muy aficionada a la división o multiplicación.

 void *mem = malloc(1024 + 15); void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16; memset_16aligned(ptr, 0, 1024); free(mem); 

Desafortunadamente, en C99 parece bastante difícil garantizar la alineación de cualquier tipo de una manera que sea portátil en cualquier implementación de C conforme a C99. ¿Por qué? Debido a que no se garantiza que un puntero sea la “dirección de byte” que uno podría imaginar con un modelo de memoria plana. Tampoco está garantizada la representación de uintptr_t , que de todos modos es un tipo opcional.

Podríamos conocer algunas implementaciones que usan una representación para void * (y por definición, también char * ) que es una dirección de byte simple, pero para C99 es opaco para nosotros, los progtwigdores. Una implementación podría representar un puntero mediante un conjunto { segmento , desplazamiento } donde el desplazamiento podría tener una alineación de quién sabe qué “en realidad”. Por qué, un puntero podría ser una forma de valor de búsqueda de tabla hash, o incluso un valor de búsqueda de lista enlazada. Podría codificar información de límites.

En un borrador reciente de C1X para un estándar C, vemos la palabra clave _Alignas . Eso podría ayudar un poco.

La única garantía que nos brinda C99 es que las funciones de asignación de memoria devolverán un puntero adecuado para asignarlo a un puntero apuntando a cualquier tipo de objeto. Como no podemos especificar la alineación de los objetos, no podemos implementar nuestras propias funciones de asignación con la responsabilidad de la alineación de una manera portátil bien definida.

Sería bueno estar equivocado acerca de este reclamo.

En el frente de relleno de conteo de 16 contra 15 bytes, el número real que necesita agregar para obtener una alineación de N es max (0, NM) donde M es la alineación natural del asignador de memoria (y ambos son potencias de 2).

Como la alineación de memoria mínima de cualquier asignador es de 1 byte, 15 = max (0,16-1) es una respuesta conservadora. Sin embargo, si sabe que su asignador de memoria le dará direcciones alineadas de 32 bits (lo cual es bastante común), podría haber usado 12 como pad.

Esto no es importante para este ejemplo, pero podría ser importante en un sistema integrado con 12K de RAM donde cada guardado de un int cuenta.

La mejor manera de implementarlo si realmente va a intentar guardar cada byte posible es como una macro para que pueda alimentarlo con su alineación de memoria nativa. De nuevo, esto probablemente solo sea útil para sistemas integrados en los que debe guardar cada byte.

En el ejemplo siguiente, en la mayoría de los sistemas, el valor 1 está bien para MEMORY_ALLOCATOR_NATIVE_ALIGNMENT , sin embargo, para nuestro sistema embebido teórico con asignaciones alineadas de 32 bits, lo siguiente podría ahorrar un poquito de memoria preciosa:

 #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) 

Tal vez se habrían satisfecho con el conocimiento de memalign ? Y, como señala Jonathan Leffler, hay dos funciones más nuevas preferibles para conocer.

Vaya, Florin me ganó. Sin embargo, si lee la página de manual a la que me he vinculado, lo más probable es que comprenda el ejemplo proporcionado por un póster anterior.

Hacemos este tipo de cosas todo el tiempo para Accelerate.framework, una biblioteca OS X / iOS fuertemente vectorizada, donde tenemos que prestar atención a la alineación todo el tiempo. Hay bastantes opciones, una o dos de las cuales no vi mencionadas anteriormente.

El método más rápido para una matriz pequeña como esta es simplemente pegarlo en la stack. Con GCC / clang:

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

No es necesario (). Esto suele ser dos instrucciones: reste 1024 del puntero de la stack, luego Y y el puntero de la stack con -alineación. Es de suponer que el solicitante necesitaba los datos en el montón porque su vida útil de la matriz excedió la stack o la recursión está en el trabajo o el espacio de la stack tiene una prima grave.

En OS X / iOS todas las llamadas a malloc / calloc / etc. siempre están alineados a 16 bytes. Si necesita 32 bytes alineados para AVX, por ejemplo, puede usar posix_memalign:

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

Algunas personas han mencionado la interfaz C ++ que funciona de manera similar.

No debe olvidarse que las páginas están alineadas con grandes potencias de dos, por lo que los búferes alineados con la página también están alineados en 16 bytes. Por lo tanto, mmap () y valloc () y otras interfaces similares también son opciones. mmap () tiene la ventaja de que el búfer puede asignarse preinicializado con algo que no sea cero, si lo desea. Dado que estos tienen el tamaño alineado de la página, no obtendrá la asignación mínima de estos, y es probable que esté sujeto a un error VM la primera vez que lo toque.

Cheesy: enciende el protector malloc o similar. Los búferes que tienen un tamaño de n * 16 bytes, como este, estarán n * 16 bytes alineados, ya que la máquina virtual se utiliza para detectar sobrecargas y sus límites están en los límites de la página.

Algunas funciones de Accelerate.framework toman un buffer temporal provisto por el usuario para usar como espacio cero. Aquí tenemos que suponer que el búfer que se nos ha pasado está muy mal alineado y el usuario está intentando activamente hacer nuestra vida difícil por despecho. (Nuestros casos de prueba adhieren una página de guardia justo antes y después del búfer de temperatura para subrayar el rencor). Aquí, devolvemos el tamaño mínimo que necesitamos para garantizar un segmento alineado de 16 bytes en algún lugar y luego alineamos manualmente el búfer. Este tamaño es desired_size + alignment – 1. Entonces, en este caso, es 1024 + 16 – 1 = 1039 bytes. Luego alinea como sigue:

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

Agregar alineación-1 moverá el puntero más allá de la primera dirección alineada y luego ANDing -alinear (por ejemplo, 0xfff … ff0 para la alineación = 16) lo regresa a la dirección alineada.

Como se describe en otras publicaciones, en otros sistemas operativos sin garantías de alineación de 16 bytes, puede llamar a malloc con el tamaño más grande, dejar de lado el puntero para free () más tarde, luego alinearlo como se describe arriba y usar el puntero alineado, tanto como descrito para nuestro caso de buffer de temperatura.

En cuanto a alignment_memset, esto es bastante tonto. Solo tiene que ingresar hasta 15 bytes para llegar a una dirección alineada, y luego continuar con las tiendas alineadas después de eso con algún posible código de limpieza al final. Incluso puedes hacer los bits de limpieza en código vectorial, ya sea como tiendas desalineadas que se superponen a la región alineada (siempre que la longitud sea al menos la longitud de un vector) o usando algo como movmaskdqu. Alguien solo está siendo flojo. Sin embargo, probablemente sea una pregunta razonable para la entrevista si el entrevistador quiere saber si usted se siente cómodo con los operadores estándar, bitdings.th y los fundamentos de la memoria, por lo que el ejemplo artificial puede ser perdonado.

Me sorprende que nadie haya votado la respuesta de Shao que, según tengo entendido, es imposible hacer lo que se pide en el C99 estándar, ya que convertir un puntero a un tipo integral formalmente es un comportamiento indefinido. (Aparte del estándar que permite la conversión de uintptr_t <-> void* , pero el estándar no parece permitir hacer ninguna manipulación del valor uintptr_t y luego convertirlo de nuevo).

el uso de memalign, Aligned-Memory-Blocks podría ser una buena solución para el problema.

Lo primero que me vino a la cabeza al leer esta pregunta fue definir una estructura alineada, instanciarla y luego señalarla.

¿Hay alguna razón fundamental por la que me pierdo porque nadie más sugirió esto?

Como nota al margen, dado que utilicé una matriz de caracteres (suponiendo que el carácter del sistema es de 8 bits (es decir, 1 byte)), no veo la necesidad del atributo ((empaquetado)) necesariamente (corríjanme si me equivoco ), pero lo puse de todos modos.

Esto funciona en dos sistemas en los que lo probé, pero es posible que exista una optimización del comstackdor que desconozco y que me da falsos positivos frente a la eficacia del código. Usé gcc 4.9.2 en OSX y gcc 5.2.1 en Ubuntu.

 #include  #include  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; } 

MacOS X específico:

  1. Todos los punteros asignados con malloc están alineados en 16 bytes.
  2. C11 es compatible, por lo que solo puede llamar al alignment_malloc (16, tamaño).

  3. MacOS X escoge el código que está optimizado para procesadores individuales en el momento del arranque para memset, memcpy y memmove y ese código usa trucos de los que nunca has oído hablar para hacerlo rápido. 99% de posibilidades de que memset funcione más rápido que cualquier memset16 escrito a mano, lo que hace que toda la pregunta no tenga sentido.

Si desea una solución 100% portátil, antes de C11 no hay ninguna. Porque no hay una forma portátil de probar la alineación de un puntero. Si no tiene que ser 100% portátil, puede usar

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

Esto supone que la alineación de un puntero se almacena en los bits más bajos al convertir un puntero a int sin signo. La conversión a unsigned int pierde información y se define la implementación, pero eso no importa porque no convertimos el resultado a un puntero.

La parte horrible es, por supuesto, que el puntero original debe guardarse en algún lugar para llamar a free () con él. Así que, en general, realmente dudaría de la sabiduría de este diseño.

También puede agregar unos 16 bytes y luego empujar el ptr original a 16 bits alineados agregando el (16-mod) como debajo del puntero:

 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); } 

Si hay restricciones de que no se puede perder un solo byte, entonces esta solución funciona: Nota: Hay un caso donde esto puede ejecutarse infinitamente: 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);