Я читал эту статью , в которой приводится пример контракта, который не будет работать. Хотя они исправили это в статье, я хотел знать, почему это не работает. Я скопировал упрощенную версию ниже:
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;
}
}
Странно то, что rooms
длина массива увеличивается на два каждый раз , когда createRoom
вызывается в этом контракте. Можете ли вы объяснить это поведение? Спасибо.
Проблема в этой строке
address[] adr;
Есть "предупреждение" о неинициализированном хранилище. Здесь происходит нечто большее, чем можно предположить из мягкого предупреждения. Я действительно не понимаю, почему это не серьезная ошибка, поэтому разработчик ее не принимает.
Из-за того, что хранилище не было инициализировано, компилятор не знал, куда поместить динамический массив adr[]
, поэтому поместил его в первый слот . Вы правильно прочитали. Это топает rooms[]
. Ой!
Так как место, занятое этим слотом, также является динамическим массивом, оно также использует первое слово для описания длины массива. Таким образом, мы получаем наблюдаемое странное поведение... нажимаем на один массив, потом на другой и пуф! - оба массива имеют длину 2.
Это не единственный случай, когда хранение, объявленное внутри функций, а не в обычном месте (снаружи), приводит к потенциально катастрофическим перезаписям данных. См. здесь другой пример: Оплошность компилятора Solc? Неверное объявление сопоставления перезаписывает хранилище
Учитывая, что смарт-контракты должны быть четкими и свободными от дефектов, я не большой поклонник такого рода непреднамеренного результата. Это и ссылочные переменные создают, на мой взгляд, слишком много вуду.
Может быть, кто-то еще вмешается с лучшей эвристикой. Я мог бы предложить сформировать две привычки
Вот код с одним изменением и работает как положено:
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;
}
}
Надеюсь, это поможет.
лунр
memory
так:address[] memory adr = new address[](1); adr[0] = msg.sender;
. Кажется, это тоже работает.