Ich habe diesen Artikel gelesen , der ein Beispiel für einen Vertrag gibt, der nicht funktioniert. Obwohl sie es in dem Artikel behoben haben, wollte ich wissen, warum es nicht funktioniert. Ich habe eine vereinfachte Version unten kopiert:
pragma solidity ^0.4.18;
contract StructArrayInitWrong {
struct Room {
address[] players;
}
Room[] rooms;
function createRoom() public {
address[] adr;
adr.push(msg.sender);
Room memory room = Room(adr);
rooms.push(room);
}
function getRoomsLength() view returns (uint) {
return rooms.length;
}
}
Das Seltsame ist, dass die rooms
Array-Länge jedes Mal um zwei zunimmt, wenn createRoom
dieser Vertrag aufgerufen wird. Können Sie sich dieses Verhalten erklären? Vielen Dank.
Das Problem liegt in dieser Zeile
address[] adr;
Es gibt eine "Warnung" vor nicht initialisiertem Speicher. Hier geht mehr vor, als eine sanfte Warnung vermuten lässt. Ich verstehe wirklich nicht, warum dies kein schwerer Fehler ist, sodass der Entwickler ihn nicht akzeptiert.
Da der Speicher nicht initialisiert war, wusste der Compiler nicht, wo er das dynamische Array ablegen sollte, adr[]
also legte er es in den ersten Steckplatz . Sie haben richtig gelesen. Es stampft weiter rooms[]
. Autsch!
Da der Besetzer dieses Slots zufällig auch ein dynamisches Array ist, verwendet es auch das erste Wort, um die Array-Länge zu beschreiben. So erhalten wir das seltsame beobachtete Verhalten ... gehen Sie zu einem Array über, dann weiter zu dem anderen und schwups! - beide Arrays haben die Länge 2.
Dies ist nicht der einzige Fall, in dem Speicher, der innerhalb von Funktionen statt an der üblichen Stelle (außerhalb) deklariert wird, potenziell katastrophale Datenüberschreibungen verursacht. Siehe hier für ein weiteres Beispiel: Solc Compiler oversight? Ungeeignete Mapping-Deklaration überschreibt Speicher
Wenn man bedenkt, dass Smart Contracts klar und fehlerfrei sein sollen, bin ich kein großer Fan von solchen unbeabsichtigten Ergebnissen. Das und Referenzgrößen summieren sich für meinen Geschmack zu etwas zu viel Voodoo.
Vielleicht meldet sich jemand anderes mit besseren Heuristiken. Ich könnte vorschlagen, zwei Gewohnheiten zu bilden
Hier ist der Code mit einer Änderung und funktioniert wie erwartet:
pragma solidity ^0.4.18;
contract StructArrayInitWrong {
struct Room {
address[] players;
}
Room[] rooms;
address[] adr; // <=== if we're going to store this, then let's store this.
function createRoom() public {
// <=== note gaping hole
adr.push(msg.sender);
Room memory room = Room(adr);
rooms.push(room);
}
function getRoomsLength() public view returns (uint) {
return rooms.length;
}
}
Ich hoffe es hilft.
lunr
memory
wie folgt geändert:address[] memory adr = new address[](1); adr[0] = msg.sender;
. Dies scheint auch zu funktionieren.