Warum sind Speicherarrays nicht veränderbar?

Ich kenne die erforderliche Länge meines Arrays vor der Ausführung der Funktion nicht. Warum kann ich die Größe meines Speicherarrays technisch nicht ändern?

Was ist der übliche Weg, um dieses Problem zu umgehen, wenn Sie auch damit konfrontiert sind.

Ich könnte ein Speicherarray erstellen und es am Ende der Funktion löschen, aber würde es mehr Gas benötigen? Ich kenne auch die Obergrenze der erforderlichen Länge, sodass ich ein Speicherarray mit der Obergrenzengröße zuweisen könnte, aber 95 % davon würden die meiste Zeit ungenutzt bleiben.

Um klarzustellen, dass ich einen Markt entwerfe, es gibt n Kauf- und m Verkaufsaufträge, ich möchte sie abgleichen, das Array matchedOrders kann bis zu der Größe von max(m,n) haben.

Ich habe die Antwort mit einem anderen Ansatz aktualisiert, der für Sie funktionieren könnte.

Antworten (3)

tl;dr Sie haben Recht, dass es derzeit nicht möglich ist, aber es gibt keinen grundsätzlichen Grund, warum Solidity das Feature nicht in Zukunft implementieren könnte.

Arrays mit vollständig dynamischer Größe passen nicht leicht zum Speichermodell des EVM. Ein Array beliebiger Größe könnte existieren, aber es müsste höher im Speicher sitzen als alles andere, damit es unbegrenzt nach oben wachsen kann. Per Definition könnten Sie also nur einen davon haben, und für den Compiler ist es ohnehin schwierig, dies allgemein zu arrangieren.

Tatsächlich sind Arrays mit dynamischer Größe für uns Old-School-Typen ein bisschen Luxus. C hat sie zum Beispiel nicht. Sie müssen den Speicher für das Array, das malloc()zur Laufzeit verwendet wird, speziell reservieren.

Grundsätzlich gibt es zwei Szenarien:

  1. Arrays beliebiger Größe, die bei Bedarf wachsen können. Solidity könnte diese wahrscheinlich intern mit einer Linked-List/Skip-List-Struktur implementieren, aber dies wurde nicht getan.

  2. Arrays mit dynamischer Größe, die on-the-fly erstellt werden können, aber nach der Erstellung eine feste Größe behalten. (Dies würde Ihre Anforderung erfüllen.) Ich sehe keinen besonderen Grund, warum Solidity dies nicht implementieren konnte, und es sollte ziemlich einfach sein. Auch hier wurde es einfach nicht getan . Siehe Update unten . In der LLL-Sprache von Ethereum ist dies übrigens über den allocAusdruck möglich, es gibt also keine grundsätzliche Einschränkung.

Speicherarrays unterscheiden sich grundlegend von Speicherarrays und werden durch einen Schlüssel=>Wert-Mechanismus indiziert, sodass es viel einfacher ist, sie dynamisch zu machen. Es ist eine interessante Idee, ein Speicher-Array zu verwenden und es beim Verlassen auf Null zu setzen, aber Sie erhalten immer noch nur etwa die Hälfte des Gases zurück; Jedes Element kostet ungefähr 10000 Gas, was enorm ist.

Zusammenfassend müssen Sie vorerst entweder mindestens die maximal vorstellbare erforderliche Größe zuweisen oder einen Algorithmus finden, der eine feste Speichergröße verwendet. Sie müssen wahrscheinlich eine Obergrenze festlegen nund mauf jeden Fall vermeiden, in einen Zustand zu geraten, in dem Ihr Vertrag nicht mehr ausgeführt werden kann, weil mehr Gas als das vorherrschende Blocklimit benötigt wird.


Aktualisieren

Es stellt sich heraus, dass der obige Ansatz 2 in Solidity möglich ist . Sie können die folgende Syntax verwenden, um spontan ein Array beliebiger Größe zu erstellen. Die Größe kann nicht geändert werden, sollte aber Ihren Anforderungen entsprechen.

function bar (uint n, uint m) returns (uint) {
    uint maxnm = n < m ? m : n;
    uint[] memory a = new uint[](maxnm);
    return a[maxnm-1];
}

Speicherarrays können normalerweise nicht vergrößert werden, da der Speicherbereich nach ihnen möglicherweise bereits für andere Werte zugewiesen wurde.

Sie können jedoch durch Inline-Montage verkleinert werden. Beispielsweise verringert diese Zeile die Größe des Speicherarrays testArrayum 1.

assembly { mstore(testArray, sub(mload(testArray), 1)) }

Diese Funktion gibt zurück 122:

function test() public pure returns (uint256)
{
    uint256[] memory testArray = new uint256[](123);
    assembly { mstore(testArray, sub(mload(testArray), 1)) }
    return testArray.length;
}

Es sollte immer darauf geachtet werden, dass die Array-Länge nicht unterläuft (unter 0 gehen). Außerdem funktioniert dies nur bei Speicherarrays mit dynamischer Größe wie ... memory[], aber nicht bei Speicherarrays mit statischer Größe wie... memory[10]

Auf das Array können verwendete Daten folgen. Daher könnte eine Erweiterung der Größe M auf N bedeuten: Zuweisung eines neuen Schlittens N Bytes + Kopieren von M Bytes Daten an diesen neuen Ort.

Das Erhöhen der Größe ist wahrscheinlich verboten, da die damit verbundenen Speicherlese- und Speicherschreibvorgänge möglicherweise kostspielig sind und der Programmierer genau wissen sollte, wann sie stattfinden. Ein Aufruf eines realloc(...)-Primitives würde diese Informationen maskieren.