Solidity智能合约体系,数组介绍

怎么办呢咱利用数组和byte字节整一个类,的数组,你应该对以太坊、智能合约有所了解

88bifa必发唯一官网 3

有时候需要把最新的N条记录暂时存起来供需要的时候调用,C#有没有指针,怎么办呢咱利用数组和byte字节整一个类:

数组可以声明时指定大小,也可以是动态变长。对于storage存储的数组来说,元素类型可以是任意的(可以是数组,map,结构体)。对于memory存储的数组,如果作为public函数的参数,它不能是map类型的数组,只能是支持ABI的类型。

Solidity 教程系列第5篇 – Solidity 数组介绍。

    public class Buffer<T>
    {
        private T[] _TS;
        private byte _Index = 0;
        private int _Capcity;
        public Buffer(int capcity)
        {
            //将数组的大小设置成2的n次方
            while ((capcity & capcity - 1) != 0)
                capcity++;
            _TS = new T[capcity];
            _Capcity = capcity;
        }
        public void Write(T t)
        {
            _TS[_Index % _Capcity] = t;
            _Index++;
        }

        public IEnumerable<T> Read()
        {
            byte index = _Index;
            for (int i = 0; i < _TS.Count(); i++)
            {
                index--;
                yield return _TS[index % _Capcity];
            }
        }
    }

一个数组,固定大小为k,元素类型为T,可以声明为T[k],如果数组大小是动态的,可以声明为T[]。如声明一个类型为uint的数组长度为5的变长数组(5个元素都是变长数组),可以声明为uint[][5]。(注意,相比其他语言,solidity多维数组的长度声明是反的。)要访问第三个动态数组的第二个元素,使用x[2][1](数组的索引是从0开始的,访问数组元素的顺序与声明时相反)。

写在前面

 解释一下:

bytesstring是特殊的数组。bytesbyte[]是类似的,但在外部函数作为参数调用中,bytes会进行压缩打包。stringbytes类似,但是不允许长度索引的方式访问。

Solidity
是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解,

1.byte _Index=0;

因此,优先使用bytes而不是byte[],因为bytes更节省内存。

如果你还不了解,建议你先看以太坊是什么

_Index–; //=0xff

注意:如果你想访问字符串的某一个字节,你可以通过bytes.length /
bytes[7] =
‘x’;
的方式来访问。记住,你访问的是UTF-8表示的低级别的字节,而不是独立的字符。这句话有点绕,我尝试用自己的理解解释一下。可以将字符串s通过bytes转为一个bytes,可以通过bytes.length获取长度,bytes[n]获取对应的UTF-8编码。通过下标访问获取到的不是对应字符,而是UTF-8编码,比如中文编码是多字节,变长的,所以下标访问到的只是其中的一个编码。

本文前半部分是参考Solidity官方文档(当前最新版本:0.4.20)进行翻译,后半部分对官方文档中没有提供代码的知识点补充代码说明(订阅专栏阅读)。

_Index++;//=0

类型为数组的状态变量,可以标记为public,从而让Solidity创建一个访问器getter,可以通过索引的方式来访问数组的某个元素。

数组(Arrays)

利用byte的这个特点,写的时候++,读的时候–,不用考虑边界,是不是比较方便。

可使用new关键字创建一个memory的数组。与stroage数组不同的是,你不能通过.length来修改数组大小。

数组可以声明时指定长度,也可以是动态变长。对storage存储的数组来说,元素类型可以是任意的,类型可以是数组,映射类型,结构体等。但对于memory的数组来说。如果作为public函数的参数,它不能是映射类型的数组,只能是支持ABI的类型。

2.数组大小为什么要自动调整为2的n次方?

pragma solidity ^0.4.16;contract C { function f public pure { uint[] memory a = new uint[]; bytes memory b = new bytes; // Here we have a.length == 7 and b.length == len a[6] = 8; //错误,编译不通过 // a.length = 10; }}

一个元素类型为T,固定长度为k的数组,可以声明为T[k],而一个动态大小(变长)的数组则声明为T[]

那是因为只有2的n次方的时候,(0xFF %
_Capcity)==_Capcity-1,才能实现数组的遍历。

数组常量,是一个还没有赋值到变量的数组表达式。

还可以声明一个多维数组,如声明一个类型为uint的数组长度为5的变长数组(5个元素都是变长数组),可以声明为uint[][5]。(注意,相比非区块链语言,多维数组的长度声明是反的。)

 

pragma solidity ^0.4.16;contract C { function f() public pure { //编译不通过 //g([1, 2, 3]); g, 2, 3]); } function g(uint[3] _data) public pure { // ... }}

要访问第三个动态数组的第二个元素,使用x[2][1]。数组的序号是从0开始的,序号顺序与定义相反。

测试一下,调用的代码如下:

数组常量是一个固定大小的memory数组,元素类型则是使用刚好能存储的元素的能用类型,比如[1,
2,
3]
,只需要uint8即可存储,因为这几个元素的类型都是uint8。它的类型是uint8[3]
memory。

bytesstring是一种特殊的数组。bytes类似byte[],但在外部函数作为参数调用中,bytes会进行压缩打包。string类似bytes,但不提供长度和按序号的访问方式(目前)。

        static void Main(string[] args)
        {
            Buffer<int> buffer = new Buffer<int>(10);
            var taskWrite = Task.Factory.StartNew(() =>
            {

                for (int i = 0; i < 20; i++)
                {
                    buffer.Write(i);
                    Console.WriteLine($"Write:i={i}");
                    Task.Delay(300).Wait();
                }
            });
            var taskRead = Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < 7; i++)
                {
                    Console.WriteLine("Read:" + string.Join(",", buffer.Read()));
                    Task.Delay(1000).Wait();
                }
            });
            Task.WaitAll(taskWrite, taskRead);
        }
    }

由于g()方法的参数需要的是uint(uint表示的是uint256),所以需要对第一个元素进行类型转换,使用uint来进行这个转换。注意:定长的memory数组是不能和变长的memory数组之间相互赋值的。

所以应该尽量使用bytes而不是byte[]

结果:

// 不能编译通过pragma solidity ^0.4.0;contract C { function f() public { // The next line creates a type error because uint[3] memory // cannot be converted to uint[] memory. uint[] memory x = [uint, 3, 4]; }}

可以将字符串s通过bytes(s)转为一个bytes,可以通过bytes(s).length获取长度,bytes(s)[n]获取对应的UTF-8编码。通过下标访问获取到的不是对应字符,而是UTF-8编码,比如中文编码是多字节,变长的,所以下标访问到的只是其中的一个编码。

88bifa必发唯一官网 1

已经计划在未来移除这样的限制。当前因为ABI传递数组还有些问题。

类型为数组的状态变量,可以标记为public,从而让Solidity创建一个访问器,如果要访问数组的某个元素,指定数字下标就好了。(稍后代码事例)

 

length:数组有一个.length属性,表示当前的数组长度。storage的变长数组(memory不可以),可以通过给.length赋值调整数组长度。memory的变长数组不支持。不能通过访问超出当前数组的长度的方式,来自动实现改变数组长度。memory数组虽然可以通过参数,灵活指定大小,但一旦创建,大小不可调整。push:变长的storage数组和bytes都有一个成员方法push(),用于在数组的末尾添加一个新元素,返回值为新的长度。pop:变长的storage数组和bytes都有一个成员方法push(),用于移除数组末尾的元素。

创建内存数组

适用于:

警告:在external函数中,不能使用多维数组

可使用new关键字创建一个memory的数组。与stroage数组不同的是,你不能通过.length的长度来修改数组大小属性。我们来看看下面的例子:

1.小缓存,<=255

基于EVM的限制,不能通过external函数返回动态的内容。在下面这个的例子中,如果通过web3.js调用能返回数据,但从Solidity中调用不能返回数据。

pragma solidity ^0.4.16;

当然可以>255,只要把_Index的类型改为uint16,uint32或者uint64,那大小分别是0xFFFF,0xFFFFFFFF和0xFFFFFFFFFFFFFFFF,但是这么大有用吗?还叫缓存吗?

目前唯一的解决办法是使用一个非常大的静态数组。

contract C {

2.单线程读单线程写操作

contract C { function f() returns  { ... } }

pragma solidity ^0.4.16;contract ArrayContract { uint[2**20] m_aLotOfIntegers; // 这里不是两个动态数组的数组,而是一个动态数组里,每个元素是长度为二的数组。 bool[2][] m_pairsOfFlags; // newPairs 存在 memory里,因为是函数参数 function setAllFlagPairs(bool[2][] newPairs) public { m_pairsOfFlags = newPairs; } function setFlagPair(uint index, bool flagA, bool flagB) public { // 访问不存在的index会抛出异常 m_pairsOfFlags[index][0] = flagA; m_pairsOfFlags[index][1] = flagB; } function changeFlagArraySize(uint newSize) public { // 如果新size更小, 移除的元素会被销毁 m_pairsOfFlags.length = newSize; } function clear() public { // 销毁 delete m_pairsOfFlags; delete m_aLotOfIntegers; // 同销毁一样的效果 m_pairsOfFlags.length = 0; } bytes m_byteData; function byteArrays(bytes data) public { // byte arrays  are different as they are stored without padding, // but can be treated identical to "uint8[]" m_byteData = data; m_byteData.length += 7; m_byteData[3] = byte; delete m_byteData[2]; } function addFlag(bool[2] flag) public returns  { return m_pairsOfFlags.push; } function createMemoryArray(uint size) public pure returns  { // Dynamic memory arrays are created using `new`: uint[2][] memory arrayOfPairs = new uint[2][]; // Create a dynamic byte array: bytes memory b = new bytes; for (uint i = 0; i < b.length; i++) b[i] = byte; return b; }}

functionf(uint len)publicpure {

 

参考:

uint[] memory a =new uint[](7);

//a.length = 100;  // 错误

bytes memory b =new bytes(len);

// Here we have a.length == 7 and b.length == len

a[6] =8;

}

}

数组常量及内联数组

数组常量,是一个数组表达式(还没有赋值到变量)。下面是一个简单的例子:

pragma solidity ^0.4.16;

contract C {

functionf()publicpure {

g([uint(1),2,3]);

}

functiong(uint[3] _data)publicpure {

// …

}

}

通过数组常量,创建的数组是memory的,同时还是定长的。元素类型则是使用刚好能存储的元素的能用类型,比如[1,
2, 3],只需要uint8即可存储,它的类型是uint8[3] memory

由于g()方法的参数需要的是uint(默认的uint表示的其实是uint256),所以需要对第一个元素进行类型转换,使用uint(1)来进行这个转换。

还需注意的一点是,定长数组,不能与变长数组相互赋值,我们来看下面的代码:

//  无法编译

pragma solidity ^0.4.0;

contract C {

functionf()public {

// The next line creates a type error because uint[3]88bifa必发唯一官网, memory

// cannot be converted to uint[] memory.

uint[] x = [uint(1),3,4];

}

}

已经计划在未来移除这样的限制。当前因为ABI传递数组还有些问题。

成员

length属性

数组有一个.length属性,表示当前的数组长度。storage的变长数组,可以通过给.length赋值调整数组长度。memory的变长数组不支持。

不能通过访问超出当前数组的长度的方式,来自动实现改变数组长度。memory数组虽然可以通过参数,灵活指定大小,但一旦创建,大小不可调整。

push方法

storage的变长数组和bytes都有一个push方法(string没有),用于附加新元素到数据末端,返回值为新的长度。

限制情况

当前在external函数中,不能使用多维数组。

另外,基于EVM的限制,不能通过外部函数返回动态的内容。

contract C {

functionf()returns (uint[]) { … }

}

在这个的例子中,如果通过web.js调用能返回数据,但从Solidity中调用不能返回数据。一种绕过这个问题的办法是使用一个非常大的静态数组。

pragma solidity ^0.4.16;

contract ArrayContract {

uint[2**20] m_aLotOfIntegers;

//
这里不是两个动态数组的数组,而是一个动态数组里,每个元素是长度为二的数组。

bool[2][] m_pairsOfFlags;

// newPairs 存在 memory里,因为是函数参数

functionsetAllFlagPairs(bool[2][] newPairs)public {

m_pairsOfFlags = newPairs;

}

functionsetFlagPair(uint index, bool flagA, bool flagB)public {

// 访问不存在的index会抛出异常

m_pairsOfFlags[index][0] = flagA;

m_pairsOfFlags[index][1] = flagB;

}

functionchangeFlagArraySize(uint newSize)public {

// 如果新size更小, 移除的元素会被销毁

m_pairsOfFlags.length = newSize;

}

functionclear()public {

// 销毁

delete m_pairsOfFlags;

delete m_aLotOfIntegers;

// 同销毁一样的效果

m_pairsOfFlags.length =0;

}

bytes m_byteData;

functionbyteArrays(bytes data)public {

// byte arrays (“bytes”) are different as they are stored without
padding,

// but can be treated identical to “uint8[]”

m_byteData = data;

m_byteData.length +=7;

m_byteData[3] = byte(8);

delete m_byteData[2];

}

functionaddFlag(bool[2] flag)publicreturns (uint) {

return m_pairsOfFlags.push(flag);

}

functioncreateMemoryArray(uint size)publicpurereturns (bytes) {

// Dynamic memory arrays are created using `new`:

uint[2][] memory arrayOfPairs =new uint[2][](size);

// Create a dynamic byte array:

bytes memory b =new bytes(200);

for (uint i =0; i < b.length; i++)

b[i] = byte(i);

return b;

}

}

补充事例说明

事例代码及讲解,请订阅区块链技术查看。

参考文档

Solidity官方文档-数组

圆方圆区块链汇集大批区块链名师,采取导师值班制,为学员实时解决技术疑难。请关注圆方圆区块链知识星球与导师。(培训咨询请联系船长13826054890微信手机同号) 

88bifa必发唯一官网 2

作者Tiny熊 ,专注于
区块链底层技术研究,更多Tiny熊老师的文章和视频请关注圆方圆链圈公众号。

88bifa必发唯一官网 3