Gibt es * irgendeine * Möglichkeit, die Länge eines C-artigen Arrays in C ++ / G ++ zu erhalten?

8

Ich habe versucht, eine Länge von (T * v) -Funktion für eine ganze Weile zu implementieren, bisher ohne Erfolg.

Es gibt zwei grundlegende, wohlbekannte Lösungen für T v [n] -Arrays, die beide nutzlos oder sogar gefährlich sind, sobald das Array in einen T * v -Zeiger zerfallen ist.

%Vor%

Es gibt Workarounds mit Wrapper-Klassen und Containern wie STLSoft array_proxy, boost :: array, std :: vector, etc. Alle haben Nachteile und die Einfachheit, syntaktischen Zucker und die weit verbreitete Verwendung von Arrays fehlt.

Es gibt Mythen über Lösungen mit Compiler-spezifischen Aufrufen, die normalerweise vom Compiler verwendet werden, wenn delete [] die Länge des Arrays kennen muss. Nach dem C ++ - FAQ Lite 16.14 gibt es zwei Techniken, die von Compilern verwendet werden, um zu wissen, wie viel Speicher freigegeben werden soll: überzuordnende und assoziative Arrays. Bei Überzuordnung weist es eine weitere Wortsumme zu und legt die Länge des Arrays vor das erste Objekt. Die andere Methode speichert offensichtlich die Längen in einem assoziativen Array. Ist es möglich, zu wissen, welche Methode G ++ verwendet, und die entsprechende Array-Länge zu extrahieren? Was ist mit Overheads und Paddings? Irgendeine Hoffnung für nicht Compiler-spezifischen Code? Oder sogar nicht plattformspezifische G ++ Builtins?

Es gibt auch Lösungen, die das Überladen von operator new [] und operator delete [] beinhalten, die ich implementiert habe:

%Vor%

Es funktionierte gut, bis ich einen seltsamen Fehler bekam: länge von konnte kein Array finden. Wie sich herausstellte, hat G ++ am Anfang dieses spezifischen Arrays 8 weitere Bytes zugewiesen, als es sein sollte. Obwohl der Operator new [] den Anfang des gesamten Arrays zurückgeben sollte, rufen Sie ihn ptr, der aufrufende Code stattdessen ptr + 8 auf, so dass die Länge von (ptr + 8) offensichtlich mit der Ausnahme fehlgeschlagen ist (selbst wenn dies nicht der Fall wäre) möglicherweise eine falsche Array-Größe zurückgegeben). Sind diese 8 Bytes eine Art Overhead oder Padding? Kann die oben erwähnte Überbelegung nicht sein, funktionierte die Funktion für viele Arrays korrekt. Was ist es und wie man es deaktiviert oder umgeht, vorausgesetzt, es ist möglich, G ++ spezifische Aufrufe oder Tricks zu verwenden?

Bearbeiten: Aufgrund der zahlreichen Möglichkeiten, C-artige Arrays zuzuordnen, ist es im Allgemeinen nicht möglich, die Länge eines beliebigen Arrays anhand seines Zeigers zu bestimmen, genau wie Oli Charlesworth vorgeschlagen hat. Aber es ist möglich, für nicht-verfallene statische Arrays (siehe die Template-Funktion oben), und Arrays mit einem benutzerdefinierten Operator new [] (size_t, size_t), basierend auf einer Idee von Ben Voigt:

%Vor%

Bedauerlicherweise gibt es bisher noch keine zuverlässigen Methoden, um die Länge eines dynamischen Arrays in einem benutzerdefinierten Operator new [] (size_t) zu bestimmen, da wir davon ausgehen, dass das Padding kleiner ist als das Größe eines der Elemente des Arrays.

Es gibt jedoch auch andere Arten von Arrays, für die eine Längenberechnung möglich ist, wie Ben Voigt vorgeschlagen hat. Daher sollte es möglich und wünschenswert sein, eine Wrapper-Klasse zu konstruieren, die mehrere Arten von Arrays (und ihre Längen) akzeptieren kann seine Konstruktoren und ist implizit oder explizit in andere Wrapper-Klassen und Array-Typen konvertierbar. Unterschiedliche Lebensdauern von verschiedenen Arten von Arrays könnten ein Problem sein, aber es könnte mit Garbage Collection gelöst werden.

    
Frigo 16.06.2011, 14:21
quelle

3 Antworten

8

Um das zu beantworten:

  

Irgendwelche Hoffnung auf nicht Compiler-spezifischen Code?

Nein.

Wenn Sie sich dazu gezwungen sehen, müssen Sie wahrscheinlich Ihr Design überdenken. Verwenden Sie zum Beispiel ein std::vector .

    
Oliver Charlesworth 16.06.2011, 14:25
quelle
4

Ihre Analyse ist größtenteils korrekt, aber ich denke, Sie haben die Tatsache ignoriert, dass Typen mit trivialen Destruktoren die Länge nicht speichern müssen, und daher kann die Überlagerung für verschiedene Typen unterschiedlich sein.

Der Standard erlaubt operator new[] , ein paar Bytes für seinen eigenen Gebrauch zu stehlen, also müssen Sie eine Bereichsüberprüfung des Zeigers anstelle einer genauen Übereinstimmung durchführen. std::map wird wahrscheinlich nicht effizient sein, aber ein sortierter Vektor sollte (kann binär gesucht werden). Ein ausgewogener Baum sollte auch wirklich gut funktionieren.

    
Ben Voigt 16.06.2011 14:25
quelle
1

Vor einiger Zeit habe ich eine ähnliche Funktion zur Überwachung von Speicherlecks verwendet:

Wenn Sie aufgefordert werden, Größe Datenbytes zuzuweisen, würde ich Größe + 4 Bytes zuweisen und die Länge der Zuweisung in den ersten 4 Bytes speichern:

%Vor%

In Ihrem Fall ist das Abrufen der zugewiesenen Größe eine Sache der Verschiebung der angegebenen Adresse um 4 und Lesen des Wertes.

Beachten Sie, dass Sie bei ungültigen Speicherparametern ungültige Ergebnisse erhalten. Beachten Sie auch, dass malloc oft intern arbeitet: die Größe der Zuweisung auf ein verstecktes Feld setzen vor die zurückgegebene Adresse. Auf einigen Architekturen muss ich nicht noch mehr zuweisen, das System malloc ist ausreichend.

Das ist eine invasive Art, es zu tun ... aber es funktioniert (vorausgesetzt, Sie ordnen alles mit diesen modifizierten Zuweisungsroutinen zu UND, dass Sie die Startadresse Ihres Arrays kennen).

    
Gui13 06.09.2013 06:52
quelle

Tags und Links