programacion principales librerias lenguaje funciones dev descargar definicion clases biblioteca c memory-management

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



librerias en lenguaje c y sus funciones (17)

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 qué puede hacer el equipo de stackoverflow con esto:

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 con 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 si acaso. Dado que la memoria debe estar alineada a 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á lo suficientemente bien alineado para cualquier propósito. Sin embargo, el significado de ''cualquiera'' es principalmente para cosas como los tipos básicos: long , double , long double , long long y punteros a Objetos y punteros a funciones. Cuando hace cosas más especializadas, como jugar con sistemas de gráficos, pueden necesitar una alineación más estricta que el resto 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 se supone que haga aritmética de punteros en punteros nulos (y GCC tiene opciones de advertencia para avisarle cuando abusa de él). Luego agrega 16 al puntero de inicio. Supongamos que malloc() te devolvió un puntero increíblemente mal alineado: 0x800001. Sumando los 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 establecidos en uno excepto los últimos cuatro. Y eso con 0x800011 da 0x800010. Puede iterar sobre las otras compensaciones y ver que la misma aritmética funciona.

El último paso, free() , es fácil: siempre, y solo, regresa a free() un valor que uno de malloc() , calloc() o realloc() devolvió, todo lo demás es un desastre. Usted proporcionó correctamente mem para mantener ese valor - gracias. El libre lo libera.

Finalmente, si conoce los componentes internos del paquete malloc de su sistema, podría suponer que podría devolver datos alineados de 16 bytes (o podría estar alineado con 8 bytes). Si estuviera alineado a 16 bytes, no tendría que modificar los valores. Sin embargo, esto es poco fiable y no portátil: otros paquetes malloc tienen diferentes alineaciones mínimas y, por lo tanto, suponiendo que una cosa cuando hace algo diferente podría conducir a volcados de núcleo. 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 comprueba que la asignación se haya realizado correctamente.

Enmienda

El programador de Windows señaló que no se pueden realizar 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. Por lo tanto, una versión modificada del código básico - convertido en un programa principal, a continuación. También me he tomado la libertad de agregar solo 15 en lugar de 16, como se ha señalado. Estoy usando uintptr_t desde que C99 ha estado disponible el tiempo suficiente como para poder acceder a la mayoría de las plataformas. Si no fuera por el uso de PRIXPTR en las sentencias printf() , sería suficiente #include <stdint.h> lugar de usar #include <inttypes.h> . [Este código incluye la solución señalada por C.R. , que reiteraba un punto hecho por Bill K hace algunos años, que logré pasar por alto hasta ahora.]

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

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

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 lanzamiento, como varias personas han indicado en sus respuestas.

Problemas con los entrevistadores.

Uri comentó: Tal vez tenga [a] un problema de comprensión de lectura esta mañana, pero si la pregunta de la entrevista dice específicamente: "¿Cómo asignarías 1024 bytes de memoria" y claramente asignas más? ¿No sería eso un fracaso automático del entrevistador?

Mi respuesta no encaja en un comentario de 300 caracteres ...

Depende, supongo. Creo que la mayoría de las personas (incluyéndome a mí) entendieron la pregunta "¿Cómo asignaría 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 con 16 bytes, entonces las opciones son más limitadas.

  • Claramente, una posibilidad es asignar 1024 bytes y luego dar a esa dirección el ''tratamiento de alineación''; el problema con ese enfoque es que el espacio disponible real no está determinado correctamente (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 se asegure de que el bloque de 1024 bytes que devuelve esté alineado adecuadamente. Si ese es el caso, probablemente termine haciendo una operación bastante similar a la que hizo la solución propuesta, pero la ocultará dentro del asignador.

Sin embargo, si el entrevistador esperaba cualquiera de esas respuestas, esperaría que reconocieran que esta solución responde a una pregunta estrechamente relacionada y que luego replantea su pregunta para que la conversación apunte en la dirección correcta. (Además, si el entrevistador se ponía realmente malo, entonces no querría el trabajo; si la respuesta a un requisito insuficientemente preciso se incendia 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 Resolver 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 memoria alineada solo usando la biblioteca estándar? ) Exige una respuesta ligeramente revisada; este apéndice lo proporciona.

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

7.22.3.1 La función aligned_alloc

Sinopsis

#include <stdlib.h> 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 por alignment , cuyo tamaño se especifica por size y cuyo valor es indeterminado. El valor de la alignment será una alineación válida respaldada por la implementación y el valor del size será un múltiplo integral de la alignment .

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

Y POSIX define posix_memalign() :

#include <stdlib.h> 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 la alignment , y devolverá un puntero a la memoria asignada en memptr . El valor de la alignment será una potencia de dos múltiplos de sizeof(void *) .

Al completarse con éxito, el valor apuntado por memptr 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 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 completarse con éxito, posix_memalign() devolverá cero; de lo contrario, se devolverá un número de error para indicar el error.

Uno o ambos de estos 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 las escenas, la nueva función de memoria alineada realiza la misma tarea que se describe en la pregunta, excepto que tiene la capacidad de forzar la alineación con mayor facilidad y hace un seguimiento del inicio de la memoria alineada internamente para que el código no lo haga. Tengo que lidiar especialmente con esto: solo libera la memoria devuelta por la función de asignación que se usó.


¿Quizás se habrían satisfecho con un conocimiento de memalign ? Y como señala Jonathan Leffler, hay dos funciones preferibles más nuevas que conocer.

Ups, 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.



Aquí hay un enfoque alternativo a la parte ''redondear''. No es la solución con codificación más brillante, pero hace el trabajo, y este tipo de sintaxis es un poco más fácil de recordar (además, funcionaría para valores de alineación que no son una potencia de 2). El elenco uintptr_t fue necesario para apaciguar el compilador; aritmética de punteros no es muy aficionado 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 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. La representación de uintptr_t tampoco está tan garantizada, 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 opaca para nosotros, los programadores. Una implementación puede representar un puntero por un conjunto { segmento , desplazamiento } donde el desplazamiento podría tener quién sabe qué alineación "en realidad". Por qué, un puntero podría ser incluso 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 C1X reciente para un estándar C, vemos la palabra clave _Alignas . Eso podría ayudar un poco.

La única garantía que nos ofrece C99 es que las funciones de asignación de memoria devolverán un puntero adecuado para la asignación a un puntero que apunta a cualquier tipo de objeto. Dado que 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 y bien definida.

Sería bueno estar equivocado acerca de esta afirmación.


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

Dado que la alineación de memoria mínima de cualquier asignador es de 1 byte, 15 = máx (0,16-1) es una respuesta conservadora. Sin embargo, si sabe que su asignador de memoria le dará direcciones alineadas int 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 uno de los intentos guardados cuenta.

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

En el siguiente ejemplo, en la mayoría de los sistemas, el valor 1 está bien para MEMORY_ALLOCATOR_NATIVE_ALIGNMENT , sin embargo, para nuestro sistema teórico integrado con asignaciones alineadas de 32 bits, lo siguiente podría ahorrar un poco 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)


Específico de MacOS X:

  1. Todos los punteros asignados con malloc tienen 16 bytes alineados.
  2. C11 es compatible, por lo que solo puede llamar alineados_malloc (16, tamaño).

  3. MacOS X selecciona el código que está optimizado para procesadores individuales en el momento del arranque de memset, memcpy y memmove, y ese código utiliza trucos que nunca has escuchado para hacerlo más rápido. 99% de probabilidad de que memset se ejecute más rápido que cualquier memset escrito a mano16, lo que hace que 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 para probar la alineación de un puntero. Si no tiene que ser 100% portátil, puedes 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 int sin firmar pierde información y está definida por la implementación, pero eso no importa porque no convertimos el resultado de nuevo en 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.


Hacemos este tipo de cosas todo el tiempo para Accelerate.framework, una biblioteca OS X / iOS muy 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 pila. Con GCC / clang:

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

No se requiere gratis () Esto suele ser dos instrucciones: restar 1024 del puntero de pila, y luego el puntero de pila con -alineación. Presumiblemente, el solicitante necesitaba los datos en el montón porque su vida útil de la matriz excedía a la pila o la recursión está en funcionamiento o el espacio de pila es muy importante.

En OS X / iOS todas las llamadas a malloc / calloc / etc. Siempre están alineados 16 bytes. Si necesitaba 32 bytes alineados para AVX, por ejemplo, entonces 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 se debe olvidar que las páginas están alineadas a grandes potencias de dos, por lo que los búferes alineados a la página también se alinean a 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 se puede asignar preinicializado con algo distinto de cero, si lo desea. Dado que estos tienen el tamaño de la página alineada, no obtendrá la asignación mínima de estos, y es probable que esté sujeto a un error de VM la primera vez que lo toque.

Cheesy: Poner la guardia malloc o similar. Los buffers que tienen un tamaño de n * 16 bytes, como este, estarán alineados con n * 16 bytes, porque la VM se utiliza para detectar saturaciones y sus límites están en los límites de la página.

Algunas funciones de Accelerate.framework incorporan un búfer temporal provisto por el usuario para usar como espacio de trabajo. Aquí tenemos que asumir que el búfer que se nos pasa está muy desalineado y el usuario está intentando activamente hacer que nuestra vida sea difícil a pesar del rencor. (Nuestros casos de prueba pegan una página de protección justo antes y después del búfer temporal 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 posteriormente. Este tamaño es el tamaño deseado + alineación - 1. Entonces, en este caso es 1024 + 16 - 1 = 1039 bytes. Entonces alinea como tal:

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

Agregar la alineación-1 moverá el puntero más allá de la primera dirección alineada y luego ANDing con -alignment (por ejemplo, 0xfff ... ff0 para 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 de forma gratuita () más tarde, luego alinearlo como se describe anteriormente y usar el puntero alineado, tanto como Descrito para nuestro caso de buffer temporal.

En cuanto a align_memset, esto es bastante tonto. Solo tiene que hacer un bucle de hasta 15 bytes para llegar a una dirección alineada, y luego continuar con las tiendas alineadas con un posible código de limpieza al final. Incluso puede realizar los bits de limpieza en código vectorial, ya sea como almacenes no alineados que se superponen con la región alineada (siempre que la longitud sea al menos la longitud de un vector) o utilizando algo como movmaskdqu. Alguien está siendo perezoso. Sin embargo, es probable que sea una pregunta de entrevista razonable si el entrevistador quiere saber si se siente cómodo con stdint.h, operadores bitwise y fundamentales de memoria, por lo que se puede perdonar el ejemplo creado.


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

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

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

Esto funciona en dos sistemas en los que lo probé, pero es posible que haya una optimización del compilador que desconozco y que me proporcione falsos positivos con respecto a la eficacia del código. Utilicé gcc 4.9.2 en OSX y gcc 5.2.1 en 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; }


Me sorprende que nadie haya votado la share que, tal como lo entiendo, es imposible hacer lo que se pide en el estándar C99, ya que la conversión formal de un puntero a un tipo integral es un comportamiento indefinido. (Aparte del estándar que permite la conversión de uintptr_t <-> void* , pero el estándar no parece permitir realizar ninguna manipulación del valor de uintptr_t y luego volver a convertirlo).


También puede agregar unos 16 bytes y luego empujar el ptr original a 16 bits alineado 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); }



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

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

UNA:

/* 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 realizar un seguimiento de dos punteros (uno para usar y otro para liberar). Así que almacena un puntero al búfer ''real'' debajo del búfer alineado.

UNA:

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 se agregaron 15 bytes a mem, este código realmente podría reducir la alineación si su implementación garantiza una alineación de 32 bytes desde malloc (improbable, pero en teoría una implementación en C podría tener una de 32 bytes). tipo alineado). Eso no importa si todo lo que haces es llamar memset_16aligned, pero si usas la memoria para una estructura, entonces podría importar.

No estoy seguro de qué es una buena solución para esto (aparte de advertir al usuario que el búfer devuelto no es necesariamente adecuado para estructuras arbitrarias) ya que no hay manera de determinar programáticamente qué es la garantía de alineación específica de la implementación. Supongo que al inicio se podrían asignar dos o más buffers de 1 byte, y supongo que la peor alineación que se ve es la alineación garantizada. Si te equivocas, desperdicias la memoria. Cualquiera que tenga una mejor idea, por favor dígalo ...

[ Añadido : el truco ''estándar'' consiste en crear una unión de ''tipos probablemente alineados al máximo'' 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 <stdint.h> , probablemente podría usar '' intmax_t '' en lugar de máquinas long long (y, en Power 6 (AIX), intmax_t le daría un tipo de entero de 128 bits). Los requisitos de alineación para esa unión se pueden determinar incorporándolos en una estructura con un solo carácter seguido por 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;

Luego usaría la alineación más grande de la 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. Por lo tanto, el usuario aprobará el tamaño de la estructura que le interesa (o la menor potencia de 2 mayor o igual que esa) y todo estará bien.

3) Use lo que su plataforma proporciona: posix_memalign para POSIX, _aligned_malloc en Windows.

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



Para la solución utilicé un concepto de relleno que alinea la memoria y no desperdicia la memoria de un solo byte.

Si hay restricciones, no puede desperdiciar un solo byte. Todos los punteros asignados con malloc tienen 16 bytes alineados.

C11 es compatible, por lo que solo puede llamar alineado_malloc (16, tamaño).

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


Si hay restricciones que no pueden desperdiciar un solo byte, entonces esta solución funciona: Nota: Hay un caso en el que 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);


long add; mem = (void*)malloc(1024 +15); add = (long)mem; add = add - (add % 16);//align to 16 byte boundary ptr = (whatever*)(add);