<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77877841" target="_blank">【Solidity】1.一个Solidity源文件的布局</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77892188" target="_blank">【Solidity】2.合约的结构体</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77930835" target="_blank">【Solidity】3.类型</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77942459" target="_blank">【Solidity】4.单位和全局可变量</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77964409" target="_blank">【Solidity】5.表达式和控制结构</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77981249" target="_blank">【Solidity】6. 合约</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77989384" target="_blank">【Solidity】7. 部件</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/78016231" target="_blank">【Solidity】8. 杂项</a>
Solidity是一种静态类型的语言,这意味着每个变量(州和地方)的类型需要被指定的(或至少已知的 - 见下文型扣)在编译时。 Solidity提供了几种可以组合形成复杂类型的基本类型。
另外,类型可以在含有运算符的表达式与彼此交互。 对于操作的快速参考,请参阅运算符的优先顺序。
以下类型也称为值类型,因为这些类型的变量将始终按值传递,即当它们用作函数参数或分配时,它们始终被复制。
<code>bool</code>:可能的值是常量<code>true</code>和<code>false</code>。
操作:
<code>!</code> (逻辑否定)
<code>&&</code>(逻辑连接,“和”)
<code>||</code> (逻辑分离,“或”)
<code>==</code>(相等)
<code>!=</code>(不等式)
运算符<code>||</code> 和<code>&&</code>应用常见的短路规则。 这意味着在表达式<code>f(x) || g(y)</code>,如果<code>f(x)</code>评估为真,即使可能有副作用,也不会评估<code>g(y)</code>。
<code>int/uint</code>:各种大小的有符号和无符号整数。 关键字<code>uint8</code>到<code>uint256</code>的步幅是8(无符号的8到256位)和<code>int8</code>到<code>int256</code>。 <code>uint</code>和<code>int</code>分别是<code>uint256</code>和<code>int256</code>的别名。
比较:<code><=</code>,<code><</code>,<code>==</code>,<code>!=</code>,<code>>=</code>,<code>></code>(评估为bool)
位操作符:<code>&</code>,<code>|</code>,<code>^</code>(按位异或),<code>〜</code>(按位取反)
算术运算符:<code>+</code>, <code>-</code> ,<code>一元 -</code> ,<code>一元 +</code>,<code>*</code>,<code>/</code>,<code>%</code>(其余),<code>**</code>(幂),<code><<</code>(左移),<code>>></code>(右移)
除法总是截断(它只是编译为EVM的DIV操作码),但如果两个运算符都是文字(或文字表达式),则它不会截断。
除零和模量具有零引发运行时异常。
移位操作的结果是左操作数的类型。 表达式<code>x << y</code>等价于<code>x * 2 ** y</code>,<code>x >> y</code>等价于<code>x / 2 ** y</code>。 这意味着移位负数符号延伸。 移位操作使用负数会引发运行时异常。
警告:
由符号整数类型的负值的右移位所产生的结果是从那些其他的编程语言产生的不同。 在“Solidity”中,将右图转换为除法,所以偏移的负值将向零舍入(截断)。 在其他编程语言中,负值的转移权就像划分为舍入(朝向负无穷大)。
地址:保存一个20字节值(Ethereum地址的大小)。 地址类型也有成员,并作为所有合约的基础。
操作
<code><=</code>, <code><</code>, <code>==</code>, <code>!=</code>, <code>>=</code> 和 <code>></code>
<code>balance</code> 和 <code>transfer</code>
可以使用财产余额查询地址的余额,并使用传输函数将以太网(以wei为单位)发送到地址:
如果x是合同地址,则其代码(更具体地说:其回退函数(如果存在))将与传送调用一起执行(这是EVM的限制,不能防止)。 如果执行任务无法运行,或以任何方式失败,则以太网传输将被恢复,并且当前的合同将以异常方式停止。
<code>send</code>
send是低级对等的转账。 如果执行失败,当前合同将不会以异常方式停止,但发送将返回false。
<code>call</code>, <code>callcode</code> and <code>delegatecall</code>
此外,为了与不遵守ABI的合同进行接口,提供了任意数量的任意类型参数的函数调用。 这些参数被填充到32字节并连接。 一个例外是第一个参数被编码到正好四个字节的情况。 在这种情况下,这里没有填充以允许使用功能签名。
<code>call</code>返回一个布尔值,指示调用的函数是否终止(<code>true</code>)或引起EVM异常(<code>false</code>)。 不可能访问返回的实际数据(为此,我们需要提前知道编码和大小)。
以类似的方式,可以使用函数<code>delegatecall</code>:区别在于仅使用给定地址的代码,所有其他方面(存储,余额,…)均取自当前的合同。 委托人的目的是使用存储在另一个合同中的库代码。 用户必须确保两个合同中的存储布局适合委托使用。 在homestead之前,只有一个名为<code>callcode</code>的有限变体才可用,不能访问原始的<code>msg.sender</code>和<code>msg.value</code>值。
所有三个功能<code>call</code>, <code>callcode</code>和<code>delegatecall</code>都是非常低级的功能,只能作为最后的手段,因为它们打破了Solidity的类型安全性。
<code>.gas()</code>选项可用于所有三种方法,而<code>delegatecall</code>不支持<code>.value()</code>选项。
所有的合约都继承地址的成员,所以它是可以查询使用<code>this.balance</code>当前合约的余额。
不鼓励使用<code>callcode</code>,将来会被删除。
所有这些功能都是低级别的功能,应小心使用。 具体而言,任何未知的合约可能是恶意的,如果你调用它,你交出控制权,以该合同可能又回调到你的合约,所以要改变你的状态变量,当调用返回准备。
<code>bytes1</code>,<code>bytes2</code>,<code>bytes3</code>,…,<code>bytes32</code>。 <code>byte</code>是<code>bytes1</code>的别名。
位运算符:<code>&</code>,<code>|</code>,<code>^</code>(按位异或),<code>〜</code>(逐位否定),<code><<</code>(左移),<code>>></code>(右移)
索引访问:如果x的类型为<code>bytesI</code>,则<code>0 <= k <I</code>的<code>x [k]</code>返回第<code>k</code>个字节(只读)。
移位操作符以任何整数类型作为右操作数(但将返回左操作数的类型),表示要移位的位数。 移动负数将导致运行时异常。
成员:
<code>.length</code>产生字节数组的固定长度(只读)。
bytes:
动态大小的字节数组,请参见数组。 不是价值型!
string:
动态尺寸的UTF-8编码字符串,请参见数组。 不是价值型!
作为一个经验法则,用于任意长度的原始字节数据和字符串的任意长度的字符串(UTF-8)数据字节。 如果可以将长度限制在一定数量的字节,那么请务必使用bytes1至bytes32之一,因为它们便宜得多。
固体点数目前尚未完全支持。 它们可以被声明,但不能被分配到或来自。
通过地址校验和测试的十六进制文字,例如0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF是地址类型。 长度在39到41位之间的十六进制文字不通过校验和测试会产生警告,并被视为常规有理数字文字。
整数文字由0-9范围内的数字序列组成。 它们被解释为小数。 例如,69意味着六十九。 八进制文字不存在于粘性和前导零无效。
小数部分文字由一个<code>.</code>形成。 在一侧至少有一个数字。 例子包括<code>1.</code>,<code>.1</code>和<code>1.3</code>。
也支持科学符号,其中基数可以分数,而指数不能。 实例包括<code>2e10</code>,<code>-2e10</code>,<code>2e-10</code>,<code>2.5e1</code>。
数字字面表达式保持任意精度,直到它们转换为非文字类型(即通过将它们与非文字表达式一起使用)。 这意味着计算不会溢出,而在数字文字表达式中,分割不会截断。
例如,<code>(2**800 + 1) - 2**800</code>导致常数<code>1</code>(类型<code>uint8</code>),尽管中间结果甚至不适合机器字大小。 此外,<code>.5 * 8</code>导致整数<code>4</code>(尽管在其间使用非整数)。
只要操作数为整数,任何可应用于整数的运算符也可应用于数字文字表达式。 如果两者中的任何一个是分数的,则不允许位操作,如果指数是分数(因为可能导致非有理数),则不允许求幂)。
Solidity有一些文本类型为每个有理数。 整数文字和理性数字字面值属于数字文字类型。 此外,所有数字字面值表达式(即仅包含数字文字和运算符的表达式)都属于数字字面值类型。 所以数字文字表达式<code>1 + 2</code>和<code>2 + 1</code>都属于理性数字3的相同数字字面值类型。
用于在早期版本中截断的整数文字的分割,但现在将转换为有理数,即<code>5/2</code>不等于<code>2</code>,但为<code>2.5</code>
因为它们与非文字表达式中使用的数字面表达式尽快转换成非文字类型。 尽管我们知道,在下面的例子中分配给b中的表达式的值的计算结果为一个整数,但局部表达<code>2.5 + a</code>不类型检查所以代码不编译.
字符串文字用双引号或单引号(<code>"foo"</code>或<code>'bar'</code>)编写。 它们并不意味着像C中那样为零; <code>"foo"</code>代表三个不是四个字节。 与整数文字一样,它们的类型可以有所不同,但它们可以隐式转换为<code>bytes1</code>,…,<code>bytes32</code>(如果它们适合)到字节和字符串。
字符串文字支持转义字符,例如<code>\n</code>,<code>\xNN</code>和<code>\uNNNN</code>。 <code>\xNN</code>取十六进制值并插入相应的字节,而<code>\ uNNNN</code>则使用Unicode代码点并插入UTF-8序列。
Hexademical Literals以关键字hex为前缀,以双引号或单引号(<code>hex"001122FF"</code>)括起来。 它们的内容必须是十六进制字符串,它们的值将是这些值的二进制表示。
Hexademical Literals像字符串文字,具有相同的可转换性限制。
枚举是在Solidity中创建用户定义类型的一种方式。 它们可以显式转换为所有整数类型,也可以转换为隐式转换。 显式转换在运行时检查值范围,失败会导致异常。 枚举需要至少一个成员。
函数类型是函数的类型。 函数类型的变量可以从函数分配,函数类型的函数参数可以用于将函数传递给函数并从函数调用返回函数。 功能类型有两种功能 - 内部和外部功能:
内部函数只能在当前合约内部更详细地调用(更具体地说是在当前代码单元内部,也包括内部函数库和继承函数),因为它们不能在当前契约的上下文之外执行。 调用内部函数是通过跳转到其入口标签来实现的,就像在内部调用当前合同的函数一样。
外部功能由一个地址和一个函数签名,他们可以通过传递和外部函数调用返回。
功能类型记为如下:
与参数类型相反,返回类型不能为空 - 如果函数类型不返回任何东西,则<code>returns (<return types>)</code>部分必须被省略。
缺省情况下,函数类型为<code>internal</code>,内部关键字可以省略。 与此相反,合同函数本身默认为公用,只能作为类型的名称中使用时,默认为内部。
在当前合约中有两种访问函数的方法:直接以其名称,<code>f</code>或使用<code>this.f</code>. 前者将导致内部功能,后者在外部功能中。
如果函数类型变量未初始化,则调用它将导致异常。 如果在使用<code>delete</code>之后调用函数也会发生这种情况。
如果在Solidity的上下文中使用外部函数类型,则将它们视为函数类型,它以单个字节24类型将该地址后跟功能标识符编码在一起。
请注意,现行合约的公共函数既可以作为内部函数也可以用作外部函数。 要使用<code>f</code>作为内部函数,只需使用<code>f</code>,如果要使用其外部形式,请使用<code>this.f</code>.
显示如何使用内部函数类型的示例:
另一个使用外部函数类型的例子:
Lambda或内联函数已计划但尚未支持。
复杂类型,即不总是适合256位的类型,必须比我们已经看到的值类型更仔细地处理。 由于复制它们可能相当昂贵,我们必须考虑我们是否希望将它们存储在内存中(不是持久存储的)或存储(保存状态变量的位置)。
每个复杂类型,即数组和结构体,都有一个额外的注释,即“数据位置”,关于它是存储在内存中还是存储器中。 根据上下文,总是默认,但可以通过将存储或内存附加到类型来覆盖。 函数参数(包括返回参数)默认值是内存,局部变量默认是存储和位置被迫储存状态变量(显然)。
还有第三个数据位置calldata,它是一个不可修改的非持久性区域,其中存储了函数参数。 外部函数的函数参数(不返回参数)被强制调用数据,并且主要表现为内存。
因为他们改变了分配的行为数据的位置是很重要的:存储和内存,并以一个状态变量(甚至是从其他状态变量)之间的分配总是创建一个独立的副本。 本地存储变量的分配只能分配一个引用,而且该引用总是指向状态变量,即使后者在此同时被更改。 另一方面,从存储的存储引用类型到另一存储器引用类型的分配不会创建副本。
总结:
强制数据位置:
- 外部函数的参数(不返回):calldata
- 状态变量:存储
默认数据位置:
- 函数的参数(也返回):内存
- 所有其他局部变量:存储
数组可以具有编译时固定大小,也可以是动态的。 对于存储阵列,元素类型可以是任意的(也就是其他数组,映射或结构)。 对于存储器阵列,它不能是映射,如果它是公共可见函数的参数,则必须是ABI类型。
固定大小k和元素类型T的数组被写为<code>T [k]</code>,动态大小的数组为<code>T []</code>。 例如,uint的5个动态数组的数组是<code>uint [] [5]</code>(注意,与其他语言相比,符号是相反的)。 要访问第三个动态数组中的第二个uint,您使用<code>x [2] [1]</code>(索引为零,访问以相反的方式工作,即x [2]将类型中的一个级别 正确的)。
类型字节和字符串的变量是特殊的数组。 一个字节类似于byte [],但是它被紧密地打包在calldata中。 字符串等于字节,但不允许长度或索引访问(现在)。
因此,字节应该始终优先于byte [],因为它成本更低。
如果要访问字符串<code>s</code>的字节表示,请使用<code>bytes(s).length</code> / <code>bytes(s)[7] = 'x'</code>;. 请记住,您正在访问UTF-8表示的低级字节,而不是单个字符!
在内存中创建可变长度的数组可以使用<code>new</code>关键字来完成。 与存储阵列相反,不能通过分配给<code>.length</code>成员来调整存储器阵列的大小。
数组字面值是写入表达式的数组,不会立即分配给变量。
数组文字的类型是固定大小的内存数组,其基类型是给定元素的常见类型。 [1,2,3]的类型是uint8 [3]内存,因为这些常量的类型是uint8。 因此,有必要将上述示例中的第一个元素转换为uint。 请注意,目前,固定大小的存储阵列不能分配给动态大小的存储器阵列,即不可能实现以下功能:
计划在将来删除这个限制,但由于在ABI中如何传递数组,因此目前还会产生一些并发症。
length:
数组有一个长度成员来保存它们的元素数量。 可以通过更改.length成员来调整动态数组的大小(不在内存中)。 尝试访问当前长度之外的元素时,不会自动发生。 一旦创建了存储器阵列的大小是固定的(但是动态的,即它可以依赖于运行时参数)。
push:
动态存储阵列和字节(不是字符串)具有称为push的成员函数,可用于在数组的末尾追加元素。 该函数返回新的长度。
在外部函数中不可能使用阵列数组。
由于EVM的限制,不可能从外部函数调用返回动态内容。 <code>contract C { function f() returns (uint[]) { ... }</code>将返回一个如果从web3.js调用的东西,但是如果从Solidity调用则返回。
现在唯一的解决方法是使用大型静态大小的数组。
Solidity提供了一种以结构体形式定义新类型的方法,如下例所示:
合约没有提供众筹合约的全部功能,但它包含理解结构所必需的基本概念。 结构类型可以在映射和数组中使用,它们本身可以包含映射和数组。
结构体不可能包含自己类型的成员,尽管struct本身可以是映射成员的值类型。 这个限制是必要的,因为结构的大小必须是有限的。
请注意,在所有函数中,将struct类型分配给局部变量(默认存储数据位置)。 这不会复制结构体,而只存储一个引用,以便对局部变量的成员的赋值实际写入状态。
当然,您也可以直接访问结构的成员,而不必将其分配给本地变量,如广告系列[campaignID] .amount = 0。
映射类型被声明为<code>mapping(_KeyType => _ValueType)</code>。 这里<code>_KeyType</code>可以是几乎任何类型,除了映射,动态大小的数组,契约,枚举和结构体。 <code>_ValueType</code>实际上可以是任何类型,包括映射。
因此,映射没有长度或概念的键或值被设置。
映射只允许用于状态变量(或内部函数中的存储引用类型)。
有可能<code>public</code>标记映射,并具有Solidity创建一个getter。 _KeyType将成为getter的必需参数,它将返回_ValueType。
_ValueType也可以是映射。 getter将递归地为每个_KeyType设置一个参数。
如果<code>a</code>是一个LValue(即,可以分配给一个变量或东西),下面的运算符可作为简写:
<code>a += e</code>等价于<code>a = a + e</code>。 相应地定义运算符 <code>-=</code>,<code>*=</code>,<code>/=</code>,<code>%=</code>,<code>a |=</code>,<code>&=</code>和<code>^=</code>。 <code>a++</code>和<code>a--</code>等价于<code>a += 1</code> / <code>a -= 1</code> ,但表达式本身仍然具有以前的值<code>a</code>。 相反,<code>--a</code>和<code>++ a</code>对于a而言具有相同的效果,但在更改后返回值。
<code>delete a</code>将类型的初始值分配给a。即 对于整数,它等效于a = 0,但它也可以用于数组,其中分配长度为零的动态数组或与所有元素重置的长度相同的静态数组。 对于结构体,它会为所有成员重新分配一个结构体。
删除对整个映射没有影响(因为映射的关键字可能是任意的,并且通常是未知的)。 所以,如果你删除一个结构体,它将会重置所有不是映射的成员,也可以递归到成员中,除非是映射。 但是,可以删除单个键及其映射到的内容。
如果运算符应用于不同类型,编译器将尝试将其中一个操作数隐式转换为其他类型(对于赋值也是如此)。 一般来说,值类型之间的隐式转换是有可能的,如果它在语义上有意义且没有信息丢失:uint8可以转换为uint16和int128到int256,但int8不能转换为uint256(因为uint256不能保持为-1)。 此外,无符号整数可以转换为相同或更大尺寸的字节,但反之亦然。 任何可以转换为uint160的类型也可以转换为地址。
如果编译器不允许隐式转换,但是你知道你正在做什么,那么有时可以使用显式类型转换。 请注意,这可能会给您一些意想不到的行为,所以一定要测试,以确保结果是你想要的! 以下示例将负的int8转换为uint:
在这段代码片段末尾,x将具有0xfffff..fd(64个十六进制字符)的值,在256位的二进制补码表示中为-3。
如果一个类型被明确转换为较小的类型,则高阶位被切断:
为方便起见,并不总是需要明确指定变量的类型,编译器会根据分配给该变量的第一个表达式的类型自动推断:
这里,y的类型将是uint24。 对于函数参数或返回参数,不能使用var。
该类型仅从第一个赋值中推导出来,所以以下代码段中的循环是无限的,因为我将具有类型uint8,并且此类型的任何值都小于2000.对于<code>for (var i = 0; i < 2000; i++) { ... }</code>