Ich habe gerade einen Test im Rahmen eines Vorstellungsgesprächs abgeschlossen, und eine Frage hat mich verblüfft, selbst wenn ich Google als Referenz verwendet habe. Ich würde gerne sehen, was die StackOverflow-Crew damit machen kann:
Die
memset_16aligned
Funktion erfordert einen 16-Byte-ausgerichteten Zeiger, der an sie übergeben wird. Andernfalls stürzt sie ab.a) Wie würden Sie 1024 Byte Speicher zuweisen und ihn an einer 16-Byte-Grenze ausrichten?
b) Geben Sie den Speicher frei, nachdem dermemset_16aligned
ausgeführt wurde.
{
void *mem;
void *ptr;
// answer a) here
memset_16aligned(ptr, 0, 1024);
// answer b) here
}
c
memory-management
JimDaniel
quelle
quelle
Antworten:
Ursprüngliche Antwort
Feste Antwort
Erklärung wie gewünscht
Der erste Schritt besteht darin, für alle Fälle genügend freien Speicherplatz zuzuweisen. Da der Speicher 16-Byte-ausgerichtet sein muss (was bedeutet, dass die Adresse des führenden Bytes ein Vielfaches von 16 sein muss), garantiert das Hinzufügen von 16 zusätzlichen Bytes, dass wir über genügend Speicherplatz verfügen. Irgendwo in den ersten 16 Bytes befindet sich ein auf 16 Bytes ausgerichteter Zeiger. (Beachten Sie, dass
malloc()
angeblich einen Zeiger zurückzugeben , die gut genug für ausgerichtet ist jeder . Zweck jedoch die Bedeutung von ‚any‘ ist in erster Linie für Dinge wie Grundtypen -long
,double
,long double
,long long
., Und Zeiger auf Objekte und Zeiger auf Funktionen Wenn Sie sind Wenn sie speziellere Dinge tun, wie das Spielen mit Grafiksystemen, müssen sie möglicherweise strenger ausgerichtet werden als der Rest des Systems - daher Fragen und Antworten wie diese.)Der nächste Schritt besteht darin, den void-Zeiger in einen char-Zeiger umzuwandeln. Ungeachtet des GCC sollten Sie keine Zeigerarithmetik für leere Zeiger durchführen (und GCC verfügt über Warnoptionen, die Sie darüber informieren, wenn Sie es missbrauchen). Fügen Sie dann 16 zum Startzeiger hinzu. Angenommen,
malloc()
Sie haben einen unglaublich schlecht ausgerichteten Zeiger zurückgegeben: 0x800001. Das Hinzufügen der 16 ergibt 0x800011. Jetzt möchte ich auf die 16-Byte-Grenze abrunden - also möchte ich die letzten 4 Bits auf 0 zurücksetzen. Bei 0x0F sind die letzten 4 Bits auf eins gesetzt. Daher sind~0x0F
alle Bits mit Ausnahme der letzten vier auf eins gesetzt. Und das mit 0x800011 ergibt 0x800010. Sie können die anderen Offsets durchlaufen und sehen, dass dieselbe Arithmetik funktioniert.Der letzte Schritt,
free()
ist einfach: Sie immer, und nur, Rückkehr zufree()
einem Wert , dass einermalloc()
,calloc()
oderrealloc()
an Sie zurückgeschickt - alles andere ist eine Katastrophe. Sie haben richtig angegebenmem
, um diesen Wert zu halten - danke. Das kostenlose veröffentlicht es.Wenn Sie die Interna des Systems kennen
malloc
, können Sie davon ausgehen, dass es möglicherweise 16-Byte-ausgerichtete Daten zurückgibt (oder 8-Byte-ausgerichtet ist). Wenn es 16-Byte-ausgerichtet wäre, müssten Sie nicht mit den Werten dink. Dies ist jedoch zweifelhaft und nicht portabel - anderemalloc
Pakete haben unterschiedliche Mindestausrichtungen, und daher würde die Annahme einer Sache, wenn sie etwas anderes tun, zu Core-Dumps führen. In weiten Grenzen ist diese Lösung portabel.Jemand anderes erwähnte
posix_memalign()
als einen anderen Weg, um das ausgerichtete Gedächtnis zu erhalten; das ist nicht überall verfügbar, könnte aber oft auf dieser Basis implementiert werden. Beachten Sie, dass es praktisch war, dass die Ausrichtung eine Potenz von 2 war; andere Ausrichtungen sind unordentlicher.Noch ein Kommentar - dieser Code überprüft nicht, ob die Zuordnung erfolgreich war.
Änderung
Windows Programmer wies darauf hin, dass Sie keine Bitmaskenoperationen für Zeiger ausführen können, und tatsächlich beschwert sich GCC (3.4.6 und 4.3.1 getestet) so. Es folgt also eine geänderte Version des Basiscodes, der in ein Hauptprogramm konvertiert wurde. Ich habe mir auch erlaubt, nur 15 statt 16 hinzuzufügen, wie bereits erwähnt wurde. Ich verwende
uintptr_t
C99 seit langem, um auf den meisten Plattformen verfügbar zu sein. Wenn es nicht für die VerwendungPRIXPTR
in denprintf()
Anweisungen wäre, würde es ausreichen,#include <stdint.h>
anstatt zu verwenden#include <inttypes.h>
. [Dieser Code enthält die Korrektur, auf die CR hingewiesen hat und die einen Punkt wiederholte, den Bill K vor einigen Jahren zum ersten Mal gemacht hatte und den ich bisher übersehen habe.]Und hier ist eine geringfügig allgemeinere Version, die für Größen mit einer Potenz von 2 funktioniert:
Um
test_mask()
in eine Allzweckzuweisungsfunktion umzuwandeln , müsste der einzelne Rückgabewert des Zuweisers die Freigabeadresse codieren, wie mehrere Personen in ihren Antworten angegeben haben.Probleme mit Interviewern
Uri kommentierte: Vielleicht habe ich heute Morgen ein Problem mit dem Leseverständnis, aber wenn die Interviewfrage speziell sagt: "Wie würden Sie 1024 Bytes Speicher zuweisen?" Und Sie weisen eindeutig mehr als das zu. Wäre das nicht ein automatischer Fehler des Interviewers?
Meine Antwort passt nicht in einen Kommentar mit 300 Zeichen ...
Es kommt darauf an, nehme ich an. Ich denke, die meisten Leute (einschließlich mir) haben die Frage so verstanden: "Wie würden Sie einen Speicherplatz zuweisen, in dem 1024 Byte Daten gespeichert werden können und in dem die Basisadresse ein Vielfaches von 16 Byte ist?". Wenn der Interviewer wirklich gemeint hat, wie Sie 1024 Bytes (nur) zuweisen und 16 Bytes ausrichten können, sind die Optionen eingeschränkter.
Wenn der Interviewer jedoch eine dieser Antworten erwartet, würde ich erwarten, dass er erkennt, dass diese Lösung eine eng verwandte Frage beantwortet, und dann seine Frage neu formuliert, um das Gespräch in die richtige Richtung zu lenken. (Wenn der Interviewer wirklich schlampig geworden wäre, würde ich den Job nicht wollen. Wenn die Antwort auf eine unzureichend genaue Anforderung ohne Korrektur in Flammen niedergeschossen wird, ist der Interviewer nicht jemand, für den es sicher ist zu arbeiten.)
Die Welt bewegt sich weiter
Der Titel der Frage hat sich kürzlich geändert. Es war die Lösung der Gedächtnisausrichtung in der C-Interview-Frage, die mich verblüffte . Der überarbeitete Titel ( Wie ordne ich ausgerichteten Speicher nur mit der Standardbibliothek zu? ) Erfordert eine leicht überarbeitete Antwort - dieses Addendum enthält sie.
C11 (ISO / IEC 9899: 2011) hinzugefügte Funktion
aligned_alloc()
:Und POSIX definiert
posix_memalign()
:Eine oder beide könnten verwendet werden, um die Frage jetzt zu beantworten, aber nur die POSIX-Funktion war eine Option, als die Frage ursprünglich beantwortet wurde.
Hinter den Kulissen erledigt die neue Funktion für ausgerichteten Speicher fast die gleiche Aufgabe wie in der Frage beschrieben, außer dass sie die Ausrichtung einfacher erzwingen und den Start des ausgerichteten Speichers intern verfolgen kann, damit der Code dies nicht tut müssen sich speziell damit befassen - es gibt nur den Speicher frei, der von der verwendeten Zuordnungsfunktion zurückgegeben wird.
quelle
<inttypes.h>
ab C99 verfügbar sind (zumindest für die Formatzeichenfolge - die Werte sollten wohl mit einer Umwandlung übergeben werden :)(uintptr_t)mem, (uintptr_t)ptr
. Die Formatzeichenfolge basiert auf der Verkettung von Zeichenfolgen, und das PRIXPTR-Makro ist der richtigeprintf()
Längen- und Typbezeichner für die Hex-Ausgabe für einenuintptr_t
Wert. Die Alternative ist die Verwendung,%p
aber die Ausgabe davon variiert je nach Plattform (einige fügen eine führende hinzu0x
, die meisten nicht) und wird normalerweise mit Hex-Ziffern in Kleinbuchstaben geschrieben, was mir nicht gefällt. Was ich geschrieben habe, ist plattformübergreifend einheitlich.Drei leicht unterschiedliche Antworten, je nachdem, wie Sie die Frage betrachten:
1) Gut genug für die genaue gestellte Frage ist Jonathan Lefflers Lösung, außer dass Sie zum Aufrunden auf 16 ausgerichtet nur 15 zusätzliche Bytes benötigen, nicht 16.
EIN:
B:
2) Für eine allgemeinere Speicherzuweisungsfunktion möchte der Anrufer nicht zwei Zeiger verfolgen müssen (einen zum Verwenden und einen zum Freigeben). Sie speichern also einen Zeiger auf den 'echten' Puffer unter dem ausgerichteten Puffer.
EIN:
B:
Beachten Sie, dass dieser Code im Gegensatz zu (1), bei dem nur 15 Bytes zu mem hinzugefügt wurden , die Ausrichtung tatsächlich reduzieren kann, wenn Ihre Implementierung eine 32-Byte-Ausrichtung von malloc garantiert (unwahrscheinlich, aber theoretisch könnte eine C-Implementierung 32 Byte haben ausgerichteter Typ). Das spielt keine Rolle, wenn Sie nur memset_16aligned aufrufen. Wenn Sie jedoch den Speicher für eine Struktur verwenden, kann dies von Bedeutung sein.
Ich bin mir nicht sicher, was eine gute Lösung dafür ist (außer den Benutzer zu warnen, dass der zurückgegebene Puffer nicht unbedingt für beliebige Strukturen geeignet ist), da es keine Möglichkeit gibt, programmgesteuert zu bestimmen, wie die implementierungsspezifische Ausrichtungsgarantie lautet. Ich denke, Sie könnten beim Start zwei oder mehr 1-Byte-Puffer zuweisen und davon ausgehen, dass die schlechteste Ausrichtung, die Sie sehen, die garantierte Ausrichtung ist. Wenn Sie sich irren, verschwenden Sie Speicher. Wer eine bessere Idee hat, sagt es bitte ...
[ Hinzugefügt : Der 'Standard'-Trick besteht darin, eine Vereinigung von' wahrscheinlich maximal ausgerichteten Typen 'zu erstellen, um die erforderliche Ausrichtung zu bestimmen. Die maximal ausgerichteten Typen sind wahrscheinlich (in C99) '
long long
', 'long double
', 'void *
' oder 'void (*)(void)
'; Wenn Sie einschließen<stdint.h>
, könnten Sie vermutlich 'intmax_t
' anstelle vonlong long
(und auf Power 6 (AIX) -Maschinenintmax_t
einen 128-Bit-Integer-Typ verwenden) verwenden. Die Ausrichtungsanforderungen für diese Vereinigung können bestimmt werden, indem sie in eine Struktur mit einem einzelnen Zeichen gefolgt von der Vereinigung eingebettet wird:Sie würden dann die größere der angeforderten Ausrichtung (im Beispiel 16) und den
align
oben berechneten Wert verwenden.Unter (64-Bit) Solaris 10 scheint die grundlegende Ausrichtung für das Ergebnis
malloc()
ein Vielfaches von 32 Byte zu sein.]]
In der Praxis verwenden ausgerichtete Allokatoren häufig einen Parameter für die Ausrichtung, anstatt dass sie fest verdrahtet sind. Der Benutzer wird also die Größe der Struktur übergeben, die ihm wichtig ist (oder die kleinste Potenz von 2 größer oder gleich dieser), und alles wird gut.
3) Verwenden Sie das, was Ihre Plattform bietet:
posix_memalign
für POSIX_aligned_malloc
unter Windows.4) Wenn Sie C11 verwenden, besteht die sauberste - tragbare und prägnante - Option darin, die Standardbibliotheksfunktion zu verwenden
aligned_alloc
, die in dieser Version der Sprachspezifikation eingeführt wurde.quelle
ASSERT(mem);
, die Zuordnungsergebnisse zu überprüfen.assert
dient zum Abfangen von Programmierfehlern und nicht zum Mangel an Laufzeitressourcen.char *
und asize_t
führt zu einem Fehler. Sie müssten so etwas wie verwendenuintptr_t
.Sie können es auch versuchen
posix_memalign()
(natürlich auf POSIX-Plattformen).quelle
Hier ist eine alternative Herangehensweise an den Teil "Aufrunden". Nicht die brillanteste codierte Lösung, aber sie erledigt den Job, und diese Art von Syntax ist etwas einfacher zu merken (plus würde für Ausrichtungswerte funktionieren, die keine Zweierpotenz sind). Das
uintptr_t
Besetzung war notwendig, um den Compiler zu beschwichtigen; Zeigerarithmetik ist nicht sehr angetan von Division oder Multiplikation.quelle
Leider scheint es in C99 ziemlich schwierig zu sein, eine Ausrichtung jeglicher Art auf eine Weise zu gewährleisten, die für jede C99-Implementierung, die C99 entspricht, portabel wäre. Warum? Da ein Zeiger nicht garantiert die "Byteadresse" ist, die man sich bei einem Flat-Memory-Modell vorstellen kann. Auch die Darstellung von uintptr_t ist nicht so garantiert, was selbst ohnehin ein optionaler Typ ist.
Wir kennen vielleicht einige Implementierungen, die eine Darstellung für void * (und per Definition auch char * ) verwenden, die eine einfache Byteadresse ist, aber nach C99 ist sie für uns Programmierer undurchsichtig. Eine Implementierung könnte einen Zeiger durch eine Menge { Segment , Offset } darstellen, wobei Offset "in der Realität" wer-weiß-was-Ausrichtung haben könnte. Ein Zeiger kann sogar eine Form von Hash-Tabellen-Suchwert oder sogar ein Suchwert für verknüpfte Listen sein. Es könnte Grenzinformationen codieren.
In einem aktuellen C1X-Entwurf für einen C-Standard sehen wir das Schlüsselwort _Alignas . Das könnte ein bisschen helfen.
Die einzige Garantie, die C99 uns gibt, besteht darin, dass die Speicherzuweisungsfunktionen einen Zeiger zurückgeben, der für die Zuordnung zu einem Zeiger geeignet ist, der auf einen beliebigen Objekttyp zeigt. Da wir die Ausrichtung von Objekten nicht spezifizieren können, können wir unsere eigenen Zuordnungsfunktionen, die für die Ausrichtung verantwortlich sind, nicht auf eine genau definierte, tragbare Weise implementieren.
Es wäre gut, sich in dieser Behauptung zu irren.
quelle
aligned_alloc()
. (C ++ 11/14 / 1z hat es immer noch nicht)._Alignas()
und C ++alignas()
tun nichts für die dynamische Zuordnung, nur für die automatische und statische Speicherung (oder das Strukturlayout).Auf der 16-gegen-15-Byte-Auffüllfront ist die tatsächliche Zahl, die Sie hinzufügen müssen, um eine Ausrichtung von N zu erhalten, max (0, NM), wobei M die natürliche Ausrichtung des Speicherzuweisers ist (und beide Potenzen von 2 sind).
Da die minimale Speicherausrichtung eines Allokators 1 Byte beträgt, ist 15 = max (0,16-1) eine konservative Antwort. Wenn Sie jedoch wissen, dass Ihr Speicherzuweiser Ihnen 32-Bit-Adressen mit int-Ausrichtung gibt (was ziemlich häufig vorkommt), hätten Sie 12 als Pad verwenden können.
Dies ist für dieses Beispiel nicht wichtig, kann jedoch auf einem eingebetteten System mit 12 KB RAM wichtig sein, bei dem jeder einzelne gespeicherte int zählt.
Der beste Weg, um es zu implementieren, wenn Sie tatsächlich versuchen, jedes mögliche Byte zu speichern, ist als Makro, damit Sie ihm Ihre native Speicherausrichtung zuführen können. Auch dies ist wahrscheinlich nur für eingebettete Systeme nützlich, bei denen Sie jedes Byte speichern müssen.
Im folgenden Beispiel ist auf den meisten Systemen der Wert 1 in Ordnung. Für
MEMORY_ALLOCATOR_NATIVE_ALIGNMENT
unser theoretisches eingebettetes System mit 32-Bit-ausgerichteten Zuordnungen kann jedoch Folgendes ein wenig wertvollen Speicherplatz sparen:quelle
Vielleicht wären sie mit einem Wissen über Memalign zufrieden gewesen ? Und wie Jonathan Leffler betont, gibt es zwei neuere bevorzugte Funktionen, über die man Bescheid wissen muss.
Ups, Florin hat mich geschlagen. Wenn Sie jedoch die Manpage lesen, auf die ich verlinkt habe, werden Sie höchstwahrscheinlich das Beispiel eines früheren Posters verstehen.
quelle
memalign
Funktion ist veraltet undaligned_alloc
oderposix_memalign
sollte stattdessen verwendet werden“. Ich weiß nicht, was es im Oktober 2008 gesagt hat - aber es wurde wahrscheinlich nicht erwähnt,aligned_alloc()
da dies zu C11 hinzugefügt wurde.Wir machen so etwas die ganze Zeit für Accelerate.framework, eine stark vektorisierte OS X / iOS-Bibliothek, in der wir ständig auf die Ausrichtung achten müssen. Es gibt einige Optionen, von denen ich eine oder zwei oben nicht gesehen habe.
Die schnellste Methode für ein kleines Array wie dieses besteht darin, es einfach auf den Stapel zu kleben. Mit GCC / Clang:
Kein free () erforderlich. Dies sind normalerweise zwei Anweisungen: Subtrahieren Sie 1024 vom Stapelzeiger und dann UND den Stapelzeiger mit Ausrichtung. Vermutlich benötigte der Anforderer die Daten auf dem Heap, da seine Lebensdauer des Arrays den Stapel überschritt oder die Rekursion am Werk ist oder der Stapelspeicher einen hohen Stellenwert hat.
Unter OS X / iOS werden alle Aufrufe von malloc / calloc / etc. sind immer 16 Byte ausgerichtet. Wenn Sie beispielsweise 32 Byte für AVX benötigen, können Sie posix_memalign verwenden:
Einige Leute haben die C ++ - Schnittstelle erwähnt, die ähnlich funktioniert.
Es sollte nicht vergessen werden, dass Seiten auf große Zweierpotenzen ausgerichtet sind, sodass seitenausgerichtete Puffer ebenfalls 16 Byte ausgerichtet sind. Daher sind auch mmap () und valloc () sowie andere ähnliche Schnittstellen Optionen. mmap () hat den Vorteil, dass der Puffer auf Wunsch vorinitialisiert mit etwas ungleich Null zugewiesen werden kann. Da diese eine seitenausgerichtete Größe haben, erhalten Sie nicht die Mindestzuordnung von diesen und es wird wahrscheinlich ein VM-Fehler auftreten, wenn Sie sie zum ersten Mal berühren.
Cheesy: Schalten Sie Guard Malloc oder ähnliches ein. Puffer mit einer Größe von n * 16 Byte wie dieser werden n * 16 Byte ausgerichtet, da VM zum Abfangen von Überläufen verwendet wird und ihre Grenzen an Seitengrenzen liegen.
Einige Accelerate.framework-Funktionen verwenden einen vom Benutzer bereitgestellten temporären Puffer, um ihn als Arbeitsbereich zu verwenden. Hier müssen wir davon ausgehen, dass der an uns übergebene Puffer völlig falsch ausgerichtet ist und der Benutzer aktiv versucht, unser Leben trotz allem schwer zu machen. (Unsere Testfälle kleben eine Schutzseite direkt vor und nach dem temporären Puffer, um den Trotz zu unterstreichen.) Hier geben wir die Mindestgröße zurück, die erforderlich ist, um ein 16-Byte-ausgerichtetes Segment irgendwo darin zu gewährleisten, und richten den Puffer anschließend manuell aus. Diese Größe ist erwünscht_Größe + Ausrichtung - 1. In diesem Fall sind das also 1024 + 16 - 1 = 1039 Bytes. Dann so ausrichten:
Durch Hinzufügen von Ausrichtung-1 wird der Zeiger an der ersten ausgerichteten Adresse vorbei bewegt, und durch UND-Verknüpfung mit -ausrichtung (z. B. 0xfff ... ff0 für Ausrichtung = 16) wird er zur ausgerichteten Adresse zurückgebracht.
Wie in anderen Beiträgen beschrieben, können Sie auf anderen Betriebssystemen ohne 16-Byte-Ausrichtungsgarantie malloc mit der größeren Größe aufrufen, den Zeiger später kostenlos () beiseite legen, dann wie unmittelbar oben beschrieben ausrichten und den ausgerichteten Zeiger verwenden beschrieben für unseren temporären Pufferfall.
Aligned_memset ist ziemlich dumm. Sie müssen nur bis zu 15 Bytes durchlaufen, um eine ausgerichtete Adresse zu erreichen, und anschließend mit ausgerichteten Speichern fortfahren, wobei am Ende möglicherweise ein Bereinigungscode angezeigt wird. Sie können die Bereinigungsbits sogar im Vektorcode ausführen, entweder als nicht ausgerichtete Speicher, die den ausgerichteten Bereich überlappen (vorausgesetzt, die Länge entspricht mindestens der Länge eines Vektors), oder Sie verwenden etwas wie movmaskdqu. Jemand ist nur faul. Es ist jedoch wahrscheinlich eine vernünftige Interviewfrage, wenn der Interviewer wissen möchte, ob Sie mit stdint.h, bitweisen Operatoren und Speichergrundlagen vertraut sind, damit das erfundene Beispiel vergeben werden kann.
quelle
Ich bin überrascht, dass niemand Shao gewählt hat ‚s Antwort , dass, wie ich es verstehe, ist es unmöglich, zu tun , was in Standard C99 gefragt ist, da formal nicht definiertes Verhalten einen Zeiger auf einen integralen Typ Umwandlung ist. (Abgesehen von dem Standard, der die Konvertierung von
uintptr_t
<->void*
zulässt, scheint der Standard jedoch keine Manipulationen desuintptr_t
Werts und anschließende Konvertierung zuzulassen .)quelle
unsigned char* myptr
; und dann `mptr + = (16- (uintptr_t) my_ptr) & 0x0F berechnen, würde das Verhalten bei allen Implementierungen definiert, die my_ptr definieren, aber ob der resultierende Zeiger ausgerichtet wäre, würde von der Zuordnung zwischen uintptr_t Bits und Adressen abhängen.Die Verwendung von memalign, Aligned-Memory-Blöcken könnte eine gute Lösung für das Problem sein.
quelle
memalign
Funktion ist veraltet undaligned_alloc
oderposix_memalign
sollte stattdessen verwendet werden“. Ich weiß nicht, was es im Oktober 2010 gesagt hat.Das erste, was mir beim Lesen dieser Frage in den Sinn kam, war, eine ausgerichtete Struktur zu definieren, sie zu instanziieren und dann darauf zu zeigen.
Gibt es einen fundamentalen Grund, warum ich vermisse, da dies sonst niemand vorgeschlagen hat?
Als Randnotiz sehe ich keine Notwendigkeit für das, da ich ein Array von Zeichen verwendet habe (vorausgesetzt, das Zeichen des Systems ist 8 Bit (dh 1 Byte))
__attribute__((packed))
Notwendige (korrigieren Sie mich, wenn ich falsch liege), aber ich sage es in sowieso.Dies funktioniert auf zwei Systemen, auf denen ich es ausprobiert habe, aber es ist möglich, dass es eine Compiler-Optimierung gibt, von der ich nicht weiß, dass sie mir hinsichtlich der Wirksamkeit des Codes falsch positive Ergebnisse liefert. ich benutzte
gcc 4.9.2
unter OSX undgcc 5.2.1
Ubuntu verwendet.quelle
MacOS X-spezifisch:
C11 wird unterstützt, Sie können also einfach align_malloc (16, Größe) aufrufen.
MacOS X wählt Code aus, der beim Booten für einzelne Prozessoren für memset, memcpy und memmove optimiert ist, und dieser Code verwendet Tricks, von denen Sie noch nie gehört haben, um ihn schnell zu machen. 99% ige Wahrscheinlichkeit, dass Memset schneller ausgeführt wird als jedes handgeschriebene Memset16, wodurch die gesamte Frage sinnlos wird.
Wenn Sie eine 100% tragbare Lösung wünschen, gibt es vor C11 keine. Weil es keine tragbare Möglichkeit gibt, die Ausrichtung eines Zeigers zu testen. Wenn es nicht 100% portabel sein muss, können Sie verwenden
Dies setzt voraus, dass die Ausrichtung eines Zeigers in den niedrigsten Bits gespeichert wird, wenn ein Zeiger in vorzeichenloses int konvertiert wird. Die Konvertierung in unsigned int verliert Informationen und ist in der Implementierung definiert. Dies spielt jedoch keine Rolle, da das Ergebnis nicht zurück in einen Zeiger konvertiert wird.
Das Schreckliche ist natürlich, dass der ursprüngliche Zeiger irgendwo gespeichert werden muss, um damit free () aufzurufen. Alles in allem würde ich die Weisheit dieses Entwurfs wirklich bezweifeln.
quelle
aligned_malloc
in OS X? Ich verwende Xcode 6.1 und es ist nirgendwo im iOS SDK definiert oder irgendwo in deklariert/usr/include/*
.aligned_alloc()
, aber das wird auch nicht deklariert. Von GCC 5.3.0 bekomme ich die interessanten Nachrichtenalig.c:7:15: error: incompatible implicit declaration of built-in function ‘aligned_alloc’ [-Werror]
undalig.c:7:15: note: include ‘<stdlib.h>’ or provide a declaration of ‘aligned_alloc’
. Der Code enthielt zwar die Fehlermeldungen, änderte sie<stdlib.h>
jedoch weder-std=c11
noch-std=gnu11
.Sie können auch 16 Bytes hinzufügen und dann den ursprünglichen ptr auf 16 Bit ausrichten, indem Sie (16-mod) wie unter dem Zeiger hinzufügen:
quelle
Wenn es Einschränkungen gibt, bei denen Sie kein einzelnes Byte verschwenden können, funktioniert diese Lösung: Hinweis: Es gibt einen Fall, in dem dies unendlich ausgeführt werden kann: D.
quelle
%
Operatorvoid*
auf sinnvolle Weise definiert ist?Für die Lösung habe ich ein Konzept des Auffüllens verwendet, das den Speicher ausrichtet und nicht den Speicher eines einzelnen Bytes verschwendet.
Wenn es Einschränkungen gibt, können Sie kein einzelnes Byte verschwenden. Alle mit malloc zugewiesenen Zeiger sind 16 Byte ausgerichtet.
C11 wird unterstützt, Sie können also einfach anrufen
aligned_alloc (16, size)
.quelle
malloc()
zwar an einer 16-Byte-Grenze ausgerichtet, aber nichts in einem Standard garantiert dies - er ist einfach für jede Verwendung ausreichend gut ausgerichtet, und auf vielen 32-Bit-Systemen, die auf einer ausgerichtet sind Eine 8-Byte-Grenze ist ausreichend, und für einige ist eine 4-Byte-Grenze ausreichend.Hoffe, dies ist die einfachste Implementierung, lassen Sie mich Ihre Kommentare wissen.
quelle
quelle
add += 16 - (add % 16)
.(2 - (2 % 16)) == 0
.