Revised as previous answer wasn't completely true as it is possible in memory, but it can only be done in assembly and is quite involved.
I've been looking at this quite a lot recently and your issue is primarily down to a memory pointer.
There is two reasons why the above won't work in direct memory; the first is the msize opcode should point to the largest accessed memory index, but it doesn't which is being overwritten.
The second is that we need to discard the data type size as this will automatically be allocated by the EVM.
As you can see from your example mload is using 0x40 (in memory this will reference bytes 64-96) which is the currently allocated memory size (msize) and is making the msize point to 0x20 (byte 32).
Layout in memory
Solidity reserves three 256-bit slots:
0 - 64: scratch space for hashing methods
64 - 96: currently allocated memory size (aka. free memory pointer)
Scratch space can be used between statements (ie. within inline assembly).
Solidity always places new objects at the free memory pointer and
memory is never freed (this might change in the future).
Source: http://solidity.readthedocs.io/en/develop/miscellaneous.html#layout-in-memory
msize
"size of memory, i.e. largest accessed memory index"
Source : http://solidity.readthedocs.io/en/develop/assembly.html#opcodes
From the source code:
msize (note that msize also depends on memory read access)
Source: https://github.com/ethereum/solidity/blob/18a72dbe4668e23aaf38404183b978fbbb1824d1/libevmasm/SemanticInformation.cpp#L63
Example of a test resetting the msize:
(mstore 0x40 0) ; Set initial MSIZE to 0x60
Source: https://github.com/ethereum/solidity/blob/6cbb726fb8970c6cb98e9b6a2928ef612ad9d760/test/liblll/EndToEndTest.cpp#L641
Because the code uses a return, it halts execution and returns the value stored in memory which is being referenced.
"end execution, return data mem[p..(p+s))"
Source : http://solidity.readthedocs.io/en/develop/assembly.html#opcodes
This will freeze the state of the memory and return the value of the memory which you reference.
In memory solution
Let's look at returning a broken value by altering the code to something like:
contract C {
function c() public returns (uint[]) {
return f(1,2);
}
function f(uint a, uint b) private returns (uint[] memory memOffset) {
assembly {
// Create an dynamic sized array manually.
// Don't need to define the data type here as the EVM will prefix it
mstore(add(memOffset, 0x00), 2) // Set size to 2
mstore(add(memOffset, 0x20), a) // array[0] = a
mstore(add(memOffset, 0x40), b) // array[1] = b
}
}
}
We now get an array with invalid values, this is due to additional operations being executed by the contract which is overwriting the memory you allocated. To fix this we need to move where we are writing memory to and let the evm know where the msize should be pointing to.
contract C {
function c() public returns (uint[]) {
return f(1,2);
}
function f(uint a, uint b) private returns (uint[] memory memOffset) {
assembly {
// Create an dynamic sized array manually.
// Don't need to define the data type here as the EVM will prefix it
memOffset := msize() // Get the highest available block of memory
mstore(add(memOffset, 0x00), 2) // Set size to 2
mstore(add(memOffset, 0x20), a) // array[0] = a
mstore(add(memOffset, 0x40), b) // array[1] = b
mstore(0x40, add(memOffset, 0x60)) // Update the msize offset to be our memory reference plus the amount of bytes we're using
}
}
}
The above should now return the correct value.
Previous answer:
Dynamic arrays cannot be stored in memory due to their
nondeterministic size. Basically, if you have a dynamic array which
has a incremental growing and shrinking storage you would have to
either constantly keep shuffling around the memory, risk overwriting
existing memory or reserve a large block of memory which is incredibly
inefficient and gas heavy. Other programming languages resolved this
issue using dictionaries and linked list structures where you
reference various memory locations instead of a sequence of memory.
The reason why its possible to do in the stack/storage is because it
optimizes the storage similar to a linked list, but this hasn't been
applied to the use within memory. In short arrays in Solidity are far
from complete and need some work yet, so I would consider using other
techniques such as storing data as bytes.
According to the Solidity documentation, starting from version 0.5.0 it is possible to access the return data from a call:
address.call(bytes memory) returns (bool, bytes memory)
issue low-level CALL
with the given payload, returns success condition and return data, forwards all available gas, adjustable
The data is given as a single bytes
array, so you will likely want to decode it using abi.decode
in order to make use of it.
Prior to version 0.5.0, you can count on the fact that the return values remains on the stack when the call()
returns. They can be accessed, but you will have to use assembly.
Best Answer
When none of the returned values is an integer (such as in your case), you can use this:
In other cases (that you may possibly have), for any integer value, add
.toFixed()
.For example, suppose that the first returned-value is
uint256
, then use this: