Each storage variable has its own storage slot whose address is a sequential number of this variable within contract:
contract Foo {
uint internal x; // Storage slot #0
mapping (uint => uint) internal y; // Storage slot #1
uint [] internal z; // Storage slot #2
}
For atomic variables, such as x
, this slot holds variable value.
For mapping variables, such as y
, this slot is not used, but still allocated.
For dynamic array variables, such as z
, this slot contains array length.
Array data is stored separately in consequent storage slots starting from the slot with address keccak256 (n)
where n
is variable sequential number.
So, to access array z
declared above, you may use the following assembly:
function zLength () public view returns (uint r) {
assembly {
r := sload (2)
}
}
function zElement (uint i) public view returns (uint r) {
assembly {
mstore (0, 2)
r := sload (add (keccak256 (0, 32), i))
}
}
Of cause, keccak256 (2)
should not be calculated every time, such should be substituted as compile-time constant.
See documentation for more details.