solidity重点总结

solidity的constant修饰符代表不能用函数修改任何数据.

external只能外部调用,internal只能内部调用,如果函数中写了this....则表明这个函数必须从外部调用,但是这种写法不常用,按照大佬的回答做就行了

As for best practices, you should use external if you expect that the function will only ever be called externally, and use public if you need to call the function internally. It almost never makes sense to use the this.f() pattern, as this requires a real CALL to be executed, which is expensive. Also, passing arrays via this method would be far more expensive than passing them internally.

引用类型的变量类型

引用类型为:数组,字符串,结构体等

当引用类型作为函数参数的时候,其类型默认为memory,memory类型的变量会临时拷贝一份值存储到内存中。函数参数用memory类型的时候相当于值传递,而storage类型则是指针传递。storage会改变区块的内容,而storage并不会

一个编程实例:

memory------无法改变合约中的值

pragma solidity ^0.4.4;

contract Person {

    string public  _name;
    
    function Person() {
        _name = "liyuechun";
    }

    function f() {
        
        modifyName(_name);
    }

    function modifyName(string storage name)  {
    
        var name1 = name;
        bytes(name1)[0] = 'L';
    }
}

storage-------可以改变合约中的值

pragma solidity ^0.4.4;

contract Person {

    string public  _name;
    
    function Person() {
        _name = "liyuechun";
    }

    function f() {
        
        modifyName(_name);
    }

    function modifyName(string storage name) internal {
    
        var name1 = name;
        bytes(name1)[0] = 'L';
    }
}

地址Address

以太坊中的地址的长度为20字节,一字节等于8位,一共160位,所以address其实亦可以用uint160来声明。

我的钱包地址为0xC0667451aB87fc828D1C1540750D7d352eF3e5Fd

下面看一段Address的示例代码:

pragma solidity ^0.4.4;


// 0x903ad08970c70d10e5fb5b3c26f7b714830afcf6
// 0x62e40877f4747e06197aa1a2b9ac06dd9bb244a3

// 0xf055775ebd516e7419ae486c1d50c682d4170645

// 0xe7795e05d15f7406baf411cafe766fc28eccf35f
// 0xe7795e05d15f7406baf411cafe766fc28eccf35f

contract Test {
    
    address public _owner;
    
    uint public _number;

    function Test() {
        _owner = msg.sender;
        _number = 100;
    }
    
    function msgSenderAddress() constant returns (address) {
        return msg.sender;
    }
    
    function setNumberAdd1() {
        _number = _number + 5;
    }
    
    function setNumberAdd2() {
        if (_owner == msg.sender) {
            _number = _number + 10;
        }
    }
    
    
    function returnContractAddress() constant returns (address) {
        return this;
    }
    
    
    
}

几个常识

  • msg.sender就是当前调用方法的用户地址
  • this指的是当前合约的地址
  • address支持各种算数运算符

address的成员变量和函数

1.balance

pragma solidity ^0.4.4;

contract addressBalance{
    
    function getBalance(address addr) constant returns (uint){
        return addr.balance;
    }

}

这段代码返回了addr的余额

2.transfer-----向指定的地址转入一定的以太币,单位为wei,转钱的函数要声明payable

pragma solidity ^0.4.4;

contract PayableKeyword{ 
    
    
    // 从合约发起方向 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c 地址转入 msg.value 个以太币,单位是 wei
    function deposit() payable{
        
        address Account2 = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
        Account2.transfer(msg.value);
    }
  
  
    // 读取 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c 地址的余额
    function getAccount2Balance() constant returns (uint) {
        
        address Account2 = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;

        return Account2.balance;
    }  
    
    // 读取合约发起方的余额
    function getOwnerBalance() constant returns (uint) {
        
        address Owner = msg.sender;
        return Owner.balance;
    } 
    
}

3.send,和transfer一样,但是send在失败的时候不会报错,所以用send会有风险

字符串

没啥说的,要强调的一点是solidity中的字符串并不想C中用/0结尾

字节数组和string,看一个例子。固定字节数组不能直接转换为string

pragma solidity ^0.4.4;

contract C {
    
    function bytes32ToString(bytes32 x) constant returns (string) {
        bytes memory bytesString = new bytes(32);
        for (uint j = 0; j < 32; j++) {
            bytesString[j]=x[j];
        }
        /*
        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }*/
        bytes memory bytesStringTrimmed = new bytes(32);
        for (j = 0; j < 32; j++) {
            bytesStringTrimmed[j] = bytesString[j];
        }
        return string(bytesStringTrimmed);
    }

    function bytes32ArrayToString(bytes32[] data) constant returns (string) {
        bytes memory bytesString = new bytes(data.length * 32);
        uint urlLength;
        for (uint i = 0; i< data.length; i++) {
            for (uint j = 0; j < 32; j++) {
                byte char = byte(bytes32(uint(data[i]) * 2 ** (8 * j)));
                if (char != 0) {
                    bytesString[urlLength] = char;
                    urlLength += 1;
                }
            }
        }
        bytes memory bytesStringTrimmed = new bytes(urlLength);
        for (i = 0; i < urlLength; i++) {
            bytesStringTrimmed[i] = bytesString[i];
        }
        return string(bytesStringTrimmed);
    }    
}

数组

pragma solidity ^0.4.4;

contract C {
    
    // 数组的长度为5,数组里面的存储的值的类型为uint类型
    uint [5] T = [1,2,3,4,5];
    
    
    // 通过for循环计算数组内部的值的总和
    function numbers() constant public returns (uint) {
        uint num = 0;
        for(uint i = 0; i < T.length; i++) {
            num = num + T[i];
        }
        return num;
    }

}

可变长度数组可以用uint [] T=[1,2,3,4,5]来声明,可以用push添加值。

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

结构体

pragma solidity ^0.4.4;

contract Students {
    
    struct Person {
        uint age;
        uint stuID;
        string name;
    }

    Person _person = Person(18,101,"liyuechun");

}
pragma solidity ^0.4.4;

contract Students {
    
    struct Person {
        uint age;
        uint stuID;
        string name;
    }

    Person _person = Person({age:18,stuID:101,name:"liyuechun"});

}
pragma solidity ^0.4.4;

contract Students {
    
    struct Person {
        uint age;
        uint stuID;
        string name;
    }
    
    function personInit() {
        
        Person memory person = Person({age:18,stuID:101,name:"liyuechun"});
    }
}

mapping 

pragma solidity ^0.4.4;

contract MappingExample {
    
    // 测试账号
    
    // 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
    
    // 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
    
    // 0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db
    
    mapping(address => uint)  balances;

    function update(address a,uint newBalance) public {
        balances[a] = newBalance;
    }
    
    // {0xca35b7d915458ef540ade6068dfe2f44e8fa733c: 100,0x14723a09acff6d2a60dcdf7aa4aff308fddc160c: 200,0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db: 300 }
    
    function searchBalance(address a) constant public returns (uint) {
        
        return balances[a];
    }
}

pragma solidity ^0.4.4;

contract CrowdFunding {
    // Defines a new type with two fields.
    struct Funder {
        address addr;
        uint amount;
    }

    struct Campaign {
        address beneficiary;
        uint fundingGoal;
        uint numFunders;
        uint amount;
        mapping (uint => Funder) funders;
    }

    uint numCampaigns;
    mapping (uint => Campaign) campaigns;

    function newCampaign(address beneficiary, uint goal) public returns (uint campaignID) {
        campaignID = numCampaigns++; // campaignID is return variable
        // Creates new struct and saves in storage. We leave out the mapping type.
        campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
    }

    function contribute(uint campaignID) public payable {
        Campaign storage c = campaigns[campaignID];
        // Creates a new temporary memory struct, initialised with the given values
        // and copies it over to storage.
        // Note that you can also use Funder(msg.sender, msg.value) to initialise.
        c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
        c.amount += msg.value;
    }

    function checkGoalReached(uint campaignID) public returns (bool reached) {
        Campaign storage c = campaigns[campaignID];
        if (c.amount < c.fundingGoal)
            return false;
        uint amount = c.amount;
        c.amount = 0;
        c.beneficiary.transfer(amount);
        return true;
    }
}

参数及局部变量
局部引用变量的默认属性为storage,而默认的参数属性为memory。合约内声明的公有变量默认的数据位置为storage
pragma solidity ^0.4.0;
contract StateVariable{
  struct S{string a;uint b;}
  //状态变量,默认是storage
  S s;
}

将memory赋值给状态变量,实际是将内存变量拷贝到存储中

pragma solidity ^0.4.0;

contract MemoryConvertToStateVariable{
  struct S{string a;uint b;}

  //默认是storage的
  S s;

  function memoryToState(S memory tmp) internal{
    s = tmp;//从内存中复制到状态变量中。

    //修改旧memory中的值,并不会影响状态变量
    tmp.a = "Test";
  }

  function call() returns(string){
    S memory tmp = S("memory", 0);
    memoryToState(tmp);

    return s.a;
  }
}

由于在区块链中,storage必须是静态分配存储空间的[2]。局部变量虽然是一个storage的,但它仅仅是一个storage类型的指针。如果进行这样的赋值,实际会产生一个错误。

pragma solidity ^0.4.0;
contract MemoryToLocalVar{
  struct S{string a;uint b;}

  //默认参数是memory
  function memoryToLocal(S s) internal{
    //默认的变量是storage的指针
    //Type struct MemoryToLocalVar.S memory is not implicitly convertible to expected type struct MemoryToLocalVar.S storage pointer.
    //S tmp = s;
    
    //修改变量为memory类型
    S memory tmp = s;
  }
}

通过上面的代码,我们可以看到这样的赋值的确不被允许。你可以通过将变量tmp改为memory来完成这样的赋值。

storage转为memory,实际是将数据从storage拷贝到memory中。

pragma solidity ^0.4.0;
contract StorageToMemory{
  struct S{string a;uint b;}

  S s = S("storage", 1);

  function storageToMemory(S storage x) internal{
    S memory tmp = x;//由Storage拷贝到memory中

    //memory的修改不影响storage
    tmp.a = "Test";
  }

  function call() returns (string){
    storageToMemory(s);
    return s.a;
  }
}

在上面的例子中,我们看到,拷贝后对tmp变量的修改,完全不会影响到原来的storage变量。



memory之间是引用传递,并不会拷贝数据。我们来看看下面的代码。

pragma solidity ^0.4.0;
contract MemoryToMemory{
  struct S{string a;uint b;}

  //默认参数是memory
  function memoryToMemory(S s) internal{
    S memory tmp = s;

    //引用传递
    tmp.a = "other memory";
  }

  function call() returns (string){
    S memory mem = S("memory", 1);
    memoryToMemory(mem);
    return mem.a;//other memory
  }
}

在上面的代码中,memoryToMemory()传递进来了一个memory类型的变量,在函数内将之赋值给tmp,修改tmp的值,发现外部的memory也被改为了other memory

参考链接:
1. Solidity的数据位置特性深入详解(九)| 入门系列(老是遇到转换问题的来看看)
2. 黎跃春区块链博客


你可能感兴趣的:(学习笔记)