Ich verwende den folgenden Code in meiner Anwendung, und es funktioniert gut. Aber ich frage mich, ob es besser ist, es mit Malloc zu machen oder es so zu lassen, wie es ist?
function (int len)
{
char result [len] = some chars;
send result over network
}
Antworten:
Der Hauptunterschied besteht darin, dass VLAs (Variable Length Arrays) keinen Mechanismus zum Erkennen von Zuordnungsfehlern bieten.
Wenn Sie erklären
und
len
übersteigt die Menge an verfügbarem Stapelspeicher, Ihr Programm das Verhalten nicht definiert ist. Es gibt keinen Sprachmechanismus, um im Voraus zu bestimmen, ob die Zuweisung erfolgreich sein wird, oder um nach der Tatsache zu bestimmen, ob sie erfolgreich ist.Auf der anderen Seite, wenn Sie schreiben:
Dann können Sie Fehler ordnungsgemäß behandeln oder zumindest sicherstellen, dass Ihr Programm nach einem Fehler nicht versucht, die Ausführung fortzusetzen.
(Nun, meistens. Auf Linux-Systemen
malloc()
kann ein Teil des Adressraums zugewiesen werden, auch wenn kein entsprechender Speicher verfügbar ist. Spätere Versuche, diesen Speicherplatz zu verwenden, können den OOM Killer auslösen . Die Überprüfung aufmalloc()
Fehler ist jedoch nach wie vor empfehlenswert .)Ein weiteres Problem ist, dass auf vielen Systemen mehr Platz (möglicherweise viel mehr) zur Verfügung steht
malloc()
als für automatische Objekte wie VLAs.Und wie Philipps Antwort bereits erwähnte, wurden VLAs in C99 hinzugefügt (Microsoft unterstützt sie insbesondere nicht).
Und VLAs wurden in C11 optional gemacht. Wahrscheinlich werden die meisten C11-Compiler sie unterstützen, aber Sie können sich nicht darauf verlassen.
quelle
Automatische Arrays mit variabler Länge wurden in C99 in C eingeführt.
Sofern Sie keine Bedenken hinsichtlich der Abwärtsvergleichbarkeit mit älteren Standards haben, ist dies in Ordnung.
Wenn es funktioniert, berühren Sie es im Allgemeinen nicht. Optimieren Sie nicht im Voraus. Machen Sie sich keine Sorgen, wenn Sie spezielle Funktionen oder clevere Methoden hinzufügen, da Sie diese häufig nicht verwenden werden. Halte es einfach.
quelle
Wenn Ihr Compiler Arrays mit variabler Länge unterstützt, besteht die einzige Gefahr darin, den Stapel auf einigen Systemen zu überlaufen, wenn der Stapel
len
lächerlich groß ist. Wenn Sie sicher sind, dass dieserlen
Wert nicht größer als eine bestimmte Zahl sein wird, und Sie wissen, dass Ihr Stapel auch bei maximaler Länge nicht überläuft, lassen Sie den Code unverändert. Ansonsten schreiben Sie es mitmalloc
und umfree
.quelle
char result [sizeof(char)]
ist ein Array mit einer Größe1
(dasizeof(char)
gleich eins), sodass die Zuweisung abgeschnitten wirdsome chars
.str
zu einem Zeiger , sosizeof
dass es vier oder acht sein wird, abhängig von der Zeigergröße auf Ihrem System.char* result = alloca(len);
die Zuordnung auf dem Stapel vornehmen. Es hat den gleichen grundlegenden Effekt (und die gleichen grundlegenden Probleme)Ich mag die Idee, dass Sie ein zur Laufzeit zugewiesenes Array ohne Speicherfragmentierung, baumelnde Zeiger usw. haben können. Andere haben jedoch darauf hingewiesen, dass diese Zuweisung zur Laufzeit unbemerkt fehlschlagen kann. Also habe ich das mit gcc 4.5.3 in einer Cygwin-Bash-Umgebung versucht:
Die Ausgabe war:
Die zu große Länge, die beim zweiten Aufruf übergeben wurde, verursachte eindeutig den Fehler (Überlaufen in marker []). Dies bedeutet nicht, dass diese Art der Überprüfung narrensicher ist (Dummköpfe können schlau sein!) Oder dass sie den Standards von C99 entspricht, aber es könnte hilfreich sein, wenn Sie diese Bedenken haben.
Wie immer YMMV.
quelle
Im Allgemeinen ist der Stapel der einfachste und beste Ort, um Ihre Daten zu speichern.
Ich würde die Probleme von VLAs vermeiden, indem ich einfach das größte Array zuteile, das Sie erwarten.
Es gibt jedoch Fälle, in denen der Haufen am besten ist und sich die Mühe lohnt, mit Malloc herumzuspielen.
quelle
In der eingebetteten Programmierung verwenden wir immer statische Arrays anstelle von malloc, wenn die malloc- und freien Operationen häufig sind. Aufgrund des Mangels an Speicherverwaltung im eingebetteten System verursachen die häufigen Zuweisungs- und Freigabevorgänge Speicherfragmente. Wir sollten jedoch einige knifflige Methoden anwenden, z. B. das Definieren der maximalen Größe eines Arrays und das Verwenden eines statischen lokalen Arrays.
Wenn Ihre Anwendung unter Linux oder Windows ausgeführt wird, spielt es keine Rolle, Array oder Malloc zu verwenden. Der entscheidende Punkt liegt darin, wo Sie Ihre Datumsstruktur und Ihre Codelogik verwenden.
quelle
Was bisher noch niemand erwähnt hat, ist, dass die Array-Option mit variabler Länge wahrscheinlich wesentlich schneller als malloc / free sein wird, da das Zuweisen einer VLA nur ein Fall ist, in dem der Stapelzeiger angepasst wird (zumindest in GCC).
Wenn diese Funktion häufig aufgerufen wird (was Sie natürlich durch die Profilerstellung bestimmen), ist die VLA eine gute Optimierungsoption.
quelle
Dies ist eine sehr häufige C-Lösung, die ich für das Problem verwende und die möglicherweise hilfreich ist. Im Gegensatz zu VLAs besteht in pathologischen Fällen kein praktisches Risiko eines Stapelüberlaufs.
So verwenden Sie es in Ihrem Fall:
In diesem Fall wird der Stapel verwendet, wenn die Zeichenfolge in maximal 512 Byte passt. Andernfalls wird eine Heap-Zuordnung verwendet. Dies kann nützlich sein, wenn die Zeichenfolge beispielsweise in 99% der Fälle in maximal 512 Byte passt. Nehmen wir jedoch an, es gibt einen verrückten exotischen Fall, den Sie gelegentlich behandeln müssen, wenn die Zeichenfolge 32 Kilobyte lang ist und der Benutzer auf seiner Tastatur eingeschlafen ist oder so. Dadurch können beide Situationen problemlos bewältigt werden.
Die aktuelle Version, die ich in der Produktion verwende, hat auch eine eigene Version von
realloc
undcalloc
so weiter sowie standardkonforme C ++ - Datenstrukturen, die auf demselben Konzept basieren, aber ich habe das zur Veranschaulichung des Konzepts erforderliche Minimum extrahiert.Es hat den Vorbehalt, dass das Kopieren gefährlich ist, und Sie sollten keine Zeiger zurückgeben, die durch das Kopieren zugewiesen wurden (sie könnten ungültig werden, da die
FastMem
Instanz zerstört wird). Es ist für einfache Fälle innerhalb des Bereichs einer lokalen Funktion gedacht, in denen Sie versucht wären, immer die Stapel- / VLAs zu verwenden, da in seltenen Fällen Puffer- / Stapelüberläufe auftreten können. Es ist kein Allokator für allgemeine Zwecke und sollte nicht als solcher verwendet werden.Ich habe es tatsächlich vor langer Zeit als Antwort auf eine Situation in einer alten Codebasis mit C89 erstellt, in der ein ehemaliges Team dachte, dass es niemals passieren würde, wenn ein Benutzer einen Gegenstand mit einem Namen benennt, der mehr als 2047 Zeichen lang ist (vielleicht ist er auf seiner Tastatur eingeschlafen) ). Meine Kollegen haben tatsächlich versucht, die Größe der an verschiedenen Stellen zugewiesenen Arrays auf 16.384 zu erhöhen. Zu diesem Zeitpunkt hielt ich es für lächerlich und tauschte lediglich ein höheres Risiko eines Stapelüberlaufs gegen ein geringeres Risiko eines Pufferüberlaufs aus. Dies stellte eine Lösung dar, die sehr einfach zu installieren war, um diese Fälle zu beheben, indem nur ein paar Codezeilen hinzugefügt wurden. Dies ermöglichte es, den allgemeinen Fall sehr effizient zu handhaben und den Stapel dennoch zu nutzen, ohne die verrückten seltenen Fälle, bei denen der Haufen die Software zum Absturz brachte. Wie auch immer, ich' Wir fanden es seitdem auch nach C99 nützlich, da VLAs uns immer noch nicht vor Stapelüberläufen schützen können. Dieser kann aber noch Pools aus dem Stack für kleine Zuweisungsanfragen bilden.
quelle
Der Call-Stack ist immer begrenzt. Unter Mainstream-Betriebssystemen wie Linux oder Windows liegt die Grenze bei einem oder wenigen Megabyte (und Sie könnten Möglichkeiten finden, dies zu ändern). Bei einigen Multithread-Anwendungen ist der Wert möglicherweise niedriger (da die Threads mit einem kleineren Stapel erstellt werden könnten). Auf eingebetteten Systemen können es nur wenige Kilobyte sein. Eine gute Faustregel ist, Call-Frames zu vermeiden, die größer als ein paar Kilobyte sind.
Die Verwendung einer VLA ist nur dann sinnvoll, wenn Sie sicher sind, dass Ihre VLA
len
klein genug ist (höchstens einige Dutzendtausende). Ansonsten haben Sie einen Stapelüberlauf und das ist ein Fall von undefiniertem Verhalten , sehr beängstigend Situation.Die Verwendung der manuellen dynamischen C-Speicherzuweisung (z. B.
calloc
odermalloc
&free
) hat jedoch auch Nachteile:es kann fehlschlagen und Sie sollten immer auf Fehler prüfen (z . B.
calloc
odermalloc
zurückNULL
).Es ist langsamer: Eine erfolgreiche VLA-Zuweisung dauert einige Nanosekunden, eine erfolgreiche
malloc
kann einige Mikrosekunden (in guten Fällen nur einen Bruchteil einer Mikrosekunde) oder sogar mehr (in pathologischen Fällen, in denen es zu einem Thrashing kommt , viel mehr) benötigen .Es ist viel schwieriger zu codieren: Sie können
free
nur, wenn Sie sicher sind, dass die spitze Zone nicht mehr verwendet wird. In Ihrem Fall können Sie beidecalloc
undfree
dieselbe Routine aufrufen .Wenn Sie wissen, dass Ihr
result
Name (ein sehr schlechter Name, Sie sollten niemals die Adresse einer automatischen Variablen- VLA zurückgeben; ich verwende ihnbuf
stattresult
unten) meistens klein ist, können Sie ihn in Sonderfällen verwenden, zDer obige Code ist jedoch weniger lesbar und ist wahrscheinlich eine vorzeitige Optimierung. Es ist jedoch robuster als eine reine VLA-Lösung.
PS. Einige Systeme (z. B. einige Linux-Distributionen sind standardmäßig aktiviert) haben eine Überbelegung des Arbeitsspeichers (was dazu führt,
malloc
dass ein Zeiger angegeben wird, auch wenn nicht genügend Arbeitsspeicher vorhanden ist). Dies ist eine Funktion, die ich auf meinen Linux-Computern nicht mag und normalerweise deaktiviere.quelle