Hier ist ein Code aus der Solidity-Dokumentation:
function sumAsm(uint[] _data) public returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i) {
assembly {
o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
}
}
}
Kann jemand diesen Code erklären. _data ist ein Array von uint. Wie können wir 0x20 zu einem Array hinzufügen? Wenn der Array-Name die Adresse des ersten Elements darstellt, warum versetzen wir es? Wie gibt diese Zeile (add(add(_data, 0x20), mul(i, 0x20)))
die Adresse des ersten und der nachfolgenden Elemente des Arrays an.
Innerhalb des Assembler-Codes _data
ist die Speicheradresse der Anfang der Array-Daten.
Das erste Speicherwort (32 Bytes = 0x20 Bytes) ist jedoch für die Länge des Arrays reserviert, also müssen wir darüber hinweggehen. Somit _data[0]
befindet sich an der Speicheradresse _data + 0x20
. Im Code sieht das so aus add(_data, 0x20)
.
Die Array-Elemente folgen fortlaufend, beginnend bei 0, und belegen jeweils ein 32-Byte-Wort (0x20-Byte). Daher ist Element [N] N * 0x20 vom Anfang der Array-Daten versetzt, die wir oben gefunden haben. Im Code ist dieser Offset mul(i, 0x20)
.
Alles zusammengenommen _data[i]
findet man bei (_data + 0x20) + (i * 0x20)
, was der Ausdruck ist (add(add(_data, 0x20), mul(i, 0x20)))
.
Der Nettoeffekt der Schleife besteht darin, alle Array-Elemente zu addieren o_sum
. Dies wird wahrscheinlich in der Assemblierung durchgeführt, um den Overhead der Überprüfung von Array-Grenzen zu vermeiden, die Solidity immer einfügt.
Die Antwort von @benjaminion ist fantastisch, aber ich habe seinen Assembler-Code verständlicher gemacht:
assembly {
let ithpos := mul(add(i, 0x01), 0x20)
let ithnumber := mload(add(_data, ithpos))
o_sum := add(o_sum, ithnumber)
}
Was ich getan habe:
_data
ich einfach i
um add(i, 0x01)
. Dies wird in die Multiplikatorfunktion geleitet, mul
die den richtigen Wortindex erhält (denken Sie daran, dass Assembly nur mit 32-Byte-Variablen funktioniert).ith
Position aus dem Speicherithnumber
, o_sum
indem Sie eine einfache Addition ausführenBeachten Sie auch, wenn Sie begonnen haben, solidity ^0.5.0 zu verwenden, dass Ihr _data
Parameter jetzt wie folgt definiert werden muss:
function sumAsm(uint[] memory _data) public returns (uint o_sum) {
...
}
Es gibt ein zusätzliches memory
Schlüsselwort zwischen dem Typ und dem Namen.
für pauls antwort:
o_sum := add(o_sum, mload(add(_data, mul(add(i, 0x01), 0x20))))
ist tatsächlich billiger zu verwenden, da es keinen Speicher für 2 Variablen zuweist und einen Wert für beide speichert. es muss auch kein mload für jede dieser Variablen verwendet werden
Nicolas Massart
Bobdabär