2012-01-23 13 views
5

Devo implementare una versione ottimizzata di malloc/realloc/free (adattata per la mia particolare applicazione). Al momento il codice gira su una piattaforma particolare, ma mi piacerebbe scriverlo in modo portabile, se possibile (la piattaforma potrebbe cambiare in futuro), o almeno vorrei concentrare le possibili differenze di piattaforma in un singolo punto (probabilmente a .h). Sono a conoscenza di alcuni dei problemi:Come gestire gli allineamenti della memoria e l'aritmetica dei puntatori generici in modo portatile in C?

  • differenze di allineamento memoria
  • differenze di dimensioni minime blocchi di memoria adatto per l'assegnazione "generica" ​​
  • differenze di dimensioni puntatore

(I' Ignoreremo le differenze nei servizi di sistema di base per l'allocazione di memoria qui, poiché su alcuni sistemi embedded potrebbero non essere affatto disponibili Immaginiamo di lavorare su un grande blocco di memoria preallocato da utilizzare come "heap").

La domanda (s):

  • Esistono macro standard o funzioni in C per questo tipo di scopo?
  • Quali altri problemi posso affrontare in questo lavoro?

risposta

1

caratteristiche di allineamento sono curati solo nel nuovo standard C, C11. ha parole chiave _Alignof, _Alignas e una funzione aligned_alloc. Tesi caratteristiche non sono molto difficili da emulare con la maggior parte dei compilatori moderni (come indicato in altre risposte) , quindi ti suggerisco di scrivere piccoli macro o wrapper che useresti a seconda di __STDC_VERSION__.

+0

Grazie! L'uso di _Alignof & friends (e l'implementazione in un file specifico della piattaforma se l'SDK non lo fa) è il modo più pulito. –

+0

L'OP chiede codice portatile e la tua risposta è C11 Per quanto ne so, non c'è ancora alcun supporto per il compilatore in questo campo, anzi, molti compilatori non supportano nemmeno il C99. probabilmente un certo modo per rendere il codice non portatile per i prossimi 5-10 anni o così. – Lundin

+0

@Lundin, non dico che dovrebbe usarlo, dico che dovrebbe emularlo, queste sono le interfacce con cui andare Il supporto per C11 arriverà molto più rapidamente rispetto a C99, aggiungendo fondamentalmente funzionalità già presenti (come questo, allineamento) o altre facoltative (come VLA, thread o atomics). –

0

Se si osserva #pragma pack, questo può essere d'aiuto in quanto consente di definire il packing della struttura ed è implementato sulla maggior parte dei compilatori.

+0

Ma ... I puntatori prodotti da malloc devono preservare l'allineamento e i vincoli di imballaggio caratteristici della piattaforma. Quindi dovrei incontrarci invece di modificarli. "#pragma pack" può aiutarmi nello sviluppo del motore di allocazione principale, e qui hai ragione. Ma la mia domanda era più su come conoscere (e soddisfare) i vincoli del sistema in un modo il più standard possibile. Ciao! –

+0

'#pragma pack' è decisamente totalmente NONPORTAbile. –

+0

@JanHudec: funziona su MSVC, GCC, ICC, WATCOM e pochi altri, mi sembra abbastanza portatile (potrebbe non essere * standardizzato * tuttavia). – Necrolis

1

memoria allineati differisce da compilatore a compilatore purtroppo (questo è un problema), il MSVC, avete aligned_malloc, hai anche POSIX memalign per Linux, e poi c'è anche _mm_alloc che lavora sotto ICC, MSVC e GCC, IIRC , che dovrebbe essere il più portatile.

Il secondo problema è lo spreco di memoria dall'allineamento, non sarebbe importante, ma su sistemi embedded, è qualcosa da prendere in considerazione.

se si impilano gli oggetti che richiedono l'allineamento (come i tipi SIMD), si desidera anche esaminare __attribute__((__aligned__(x))) e __declspec(align(x)).

in termini di portabilità dei puntatori, è possibile utilizzare i tipi da stdint.h/pstdint.h a farlo, ma gli standard può dire qualcosa su UB durante il lancio tra il uintptr_t e un puntatore (purtroppo gli standard non sono il mio punto di forza:().

+0

Grazie. Purtroppo gli standard sembrano essere molto deboli in questo tipo di problemi :(... –

1

Il problema principale è che si forniscono solo le dimensioni totali del blocco di memoria a malloc() e amici, senza alcuna informazione sulla granularità dell'oggetto. Se si visualizza un'allocazione come una matrice di oggetti, allora si ha una dimensione che è la dimensione dell'oggetto base e un numero n che è il numero di oggetti nell'array, ad es.:

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

Se avete solo la dimensione totale, quindi non so se s = 4 e n = 10, o se s = 2 e n = 20, o s = 1 e n = 40, perché tutti si moltiplicano per la dimensione totale di 40 byte.

Quindi la domanda di base è, vuoi un sostituto diretto per le funzioni originali, ad es. quando hai lanciato chiamate native su tutto il tuo codice base, o disponi di una modularità centralizzata e asciutta con funzioni wrapper. Lì potresti usare le funzioni che forniscono s e n.

void *my_malloc (size_t s, size_t n) 

La maggior parte delle volte dovrebbe essere una scommessa sicura quando l'indirizzo di memoria assoluta restituito è un multiplo di s per garantire l'allineamento corretto.

In alternativa, quando si esegue il porting della propria implementazione, è sufficiente osservare l'allineamento utilizzato dalla piattaforma di destinazione (ad esempio multipli di 16) nella versione originale malloc() e utilizzarlo per la propria implementazione.

+0

Sfortunatamente, per molte ragioni, ho bisogno di una sostituzione "malloc" malloc. Hai perfettamente ragione: se potessi fare una supposizione sugli oggetti da allocare, il mio lavoro sarebbe più semplice. Ma c'è troppo codice da toccare. –

3

Il classico modo di garantire il mantenimento di allineamento adatto a tutti i tipi di base è quello di definire un'unione:

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

... quindi assicurarsi che gli indirizzi che mano sono sempre compensati da un multiplo di sizeof (union alloc_align) dagli indirizzi allineati ricevuti dall'unità di allocazione della memoria di sistema.

Credo che un metodo simile a questo è descritto in K & R.

+0

Vecchio stile, ma potrebbe funzionare. Aggiungerei "intptr_t" e un puntatore alla funzione. Grazie –

0

C dice malloc restituisce un puntatore alla memoria allineati per qualsiasi scopo. Non esiste un modo portabile in C per ottenerlo con le caratteristiche di C. Ciò ha come conseguenza che malloc è una funzione che se scritta in C non può essere scritta in modo portabile.

(C99, 7.20.3p1) "Il puntatore restituito se l'allocazione riesce viene opportunamente allineato in modo che esso può essere assegnato ad un puntatore a qualsiasi tipo di oggetto e quindi utilizzato per accedere ad un oggetto o un array di tali oggetti nello spazio allocato (fino a quando lo spazio non viene esplicitamente disallocato). "

+0

Per renderlo portatile: per quanto riguarda il casting del puntatore su (char *) e l'aggiunta dell'offset corretto. Questo sarebbe definito. – 2501

Problemi correlati