2012-01-23 10 views
5

Tengo que implementar una versión optimizada de malloc/realloc/free (adaptada para mi aplicación particular). En este momento, el código se ejecuta en una plataforma en particular, pero me gustaría escribirlo de manera portátil, si es posible (la plataforma puede cambiar en el futuro), o al menos me gustaría concentrar las posibles diferencias de plataforma en un único punto (probablemente a .h). Soy consciente de algunos de los problemas:¿Cómo administrar alineaciones de memoria y aritmética de puntero genérico de forma portátil en C?

  • diferencias de alineación de memoria
  • diferencias en el tamaño más pequeño bloques de memoria adecuada para la asignación de "genérico"
  • diferencias en el tamaño del puntero

(I' Ignore las diferencias en los servicios básicos del sistema para la asignación de memoria aquí, ya que en algunos sistemas integrados pueden no estar disponibles. Imaginemos que trabajamos en un gran bloque de memoria preasignado para usarlo como "montón".

La pregunta (s):

  • ¿Hay macros estándar o funciones en C para este tipo de propósito?
  • ¿Qué otros problemas puedo enfrentar en este trabajo?

Respuesta

1

características de alineación sólo son manejados en el nuevo estándar de C, C11. tiene palabras clave _Alignof, _Alignas y una función aligned_alloc. Tesis características no son muy difíciles de emular con la mayoría de los compiladores modernos (como se indica en otras respuestas) , por lo tanto, le sugiero que se escriba pequeñas macros o envoltorios que usaría según __STDC_VERSION__.

+0

Gracias! Usar _Alignof & friends (e implementarlos en un archivo específico de plataforma si el SDK no lo hace) es la manera más limpia. –

+0

El OP solicita un código portable y su respuesta es C11. Hasta donde yo sé, todavía no hay ningún soporte para compiladores en este lugar. De hecho, muchos compiladores ni siquiera son compatibles con C99. El uso de características específicas de C11 es probablemente una cierta manera de hacer el código no portátil durante los próximos 5-10 años más o menos. – Lundin

+0

@Lundin, no digo que deba usarlo, digo que debería emularlo, estas son las interfaces para ir con El soporte para C11 será mucho más rápido que para C99. Básicamente agrega características que ya están allí (como esta, alineación), o hace que otras sean opcionales (como VLA, hilos o átomos). –

0

Si echa un vistazo a #pragma pack, esto puede ser útil ya que le permite definir el empaque de la estructura y se implementa en la mayoría de los compiladores.

+0

Pero ... Los punteros producidos por malloc deben preservar las restricciones de alineación y empaque características de la plataforma. Entonces debería reunirme en lugar de modificarlos. "#pragma pack" puede ayudarme a desarrollar el motor de asignación central, y aquí tiene razón. Pero mi pregunta era más acerca de cómo conocer (y cumplir) las restricciones del sistema de la manera más estándar posible. ¡Adiós! –

+0

'#pragma pack' definitivamente no es totalmente seguro. –

+0

@JanHudec: funciona en MSVC, GCC, ICC, WATCOM y algunos otros, me parece bastante portátil (sin embargo, podría no ser * estandarizado *). – Necrolis

1

memoria alineado difiere del compilador compilador desgracia (esta es una cuestión), en MSVC, que tienen aligned_malloc, también tiene POSIX memalign para Linux, y luego también hay _mm_alloc que funciona bajo la CPI, MSVC y GCC, IIRC , que debería ser el más portátil.

El segundo problema es el desperdicio de memoria al alinearlo, no sería importante, pero en los sistemas integrados, es algo a tener en cuenta.

si está asignando elementos que requieren alineación (como los tipos SIMD), también debe consultar __attribute__((__aligned__(x))) y __declspec(align(x)).

en términos de portabilidad de la aritmética de punteros, puede utilizar los tipos de stdint.h/pstdint.h para hacerlo, pero las normas puede decir algo sobre UB cuando se cuelan entre uintptr_t y un puntero (por desgracia, las normas no son mi punto fuerte:().

+0

Gracias. Lamentablemente, los estándares parecen ser muy débiles en este tipo de problemas :(... –

1

El problema principal es que solo proporciona el tamaño total del bloque de memoria a malloc() y sus amigos, sin ninguna información sobre la granularidad del objeto. Si ve una asignación como una matriz de objetos, entonces tiene un tamaño que es el tamaño del objeto básico, y un número n que es el número de objetos en la matriz, p.:

p = malloc(sizeof(*p) * n); 

Si sólo tiene el tamaño total, entonces usted no sabe si s = 4 yn = 10, o si s = 2 y n = 20, o s = 1 yn = 40, porque todos se multiplican al tamaño total de 40 bytes.

Por lo tanto, la pregunta básica es si desea un sustituto directo de las funciones originales, p. cuando ha lanzado llamadas nativas en todo su código base, o tiene una modularidad centralizada y DRY con funciones de contenedor. Allí podría usar funciones que proporcionan s y n.

void *my_malloc (size_t s, size_t n) 

mayor parte del tiempo que debería ser una apuesta segura cuando la dirección de memoria absoluta devuelto es un múltiplo de s para garantizar la alineación correcta.

De forma alternativa, cuando transfiera su implementación, simplemente observe la alineación que utiliza el malloc() nativo para la plataforma de destino (por ejemplo, múltiplos de 16) y úsela para su propia implementación.

+0

Desafortunadamente, por muchas razones, necesito un reemplazo "simple" de malloc. Tiene toda la razón: si pudiera hacer una suposición sobre los objetos que asignar, mi trabajo sería más simple. Pero hay demasiado código para tocar. –

3

La forma clásica de asegurarse de que mantener la alineación adecuada para todos los tipos básicos es definir una unión:

union alloc_align { 
    void *dummy1; 
    long long dummy2; 
    long double dummy3; 
}; 

... a continuación, asegúrese de que las direcciones usted reparte siempre se ve compensado por un múltiplo de sizeof (union alloc_align) desde las direcciones alineadas que recibe desde el asignador de memoria del sistema.

creo un método similar al esto se describe en K & R.

+0

Pasado de moda, pero puede funcionar. Agregaría "intptr_t" y un puntero a la función. Gracias –

0

C dice malloc devuelve un puntero a la memoria alineada para cualquier propósito. No hay una forma portátil en C para lograr eso con las características C. Esto tiene la consecuencia de que malloc es una función que si se escribe en C no se puede escribir de forma portátil.

(C99, 7.20.3p1) "El puntero devuelto si la asignación tiene éxito está adecuadamente alineado de manera que puede ser asignado a un puntero a cualquier tipo de objeto y luego se usa para acceder a un objeto tal o una matriz de tales objetos en el espacio asignado (hasta que el espacio sea desasignado explícitamente) ".

+0

Para que sea portátil: ¿Qué hay de lanzar el puntero a (char *) y agregar el desplazamiento correcto. Eso sería definido. – 2501

Cuestiones relacionadas