変数とデータ型

変数の宣言

Solidityは静的型付け言語であり、変数を定義する際にそこに格納するデータ型を明示する必要があります。 以下に符号付整数であるint型の変数xを宣言する例を示します。

int x;

また次のように宣言時に初期値を指定する事も可能です。

int x = 10;

ちなみに初期値を設定しない場合には、各型のデフォルト値が格納されます。例えばintの場合、0が格納されます。

データ型

基本型と参照型

先に記したとおり、Solidityは静的型付け言語であり、変数を扱う際にはその変数のデータ型を強く意識する必要があります。Solidityには様々なデータ型が規定されていますが、それらは大きく基本型 と参照型という2つのタイプに分類されます。

これらは変数に格納される際の方法に違いがあります。まず基本型の変数には、「値そのもの」が格納されます。一方で参照型の変数には「値を格納しているメモリ上のアドレス(参照)」が格納されます。この動作の違いにより基本型と参照型で変数を扱う際の挙動に違いが生まれます。基本形であるuint型の変数と、参照型である配列の変数を例にして、両者で代入の挙動の挙動に違いをみてみましょう。

contract VarTypeTest {
  function valType() constant returns (uint retVal){ 
    uint a; 
    a = 10; 
    uint b = a; // (1)
    b = 20;  // (2)
    return a; // Execution result shows 10 !
  }

  function refType() constant returns (uint[2] retVal){
    uint[2] x;
    x[0] = 100;
    x[1] = 200;
    uint[2] y = x; //(3)
    y[0] = 500; // (4)
    y[1] = 600; // (4)
    return x; // Execution result shows [500, 600] !
  }
}

関数valType()では基本型であるuintの変数を代入した場合の挙動を試験しています。(1)の行では、変数aに代入された「10」という値そのものがコピーされて変数bに代入されます(値渡し)。その結果bの値が(2)で変更されても、元の変数aの内容には影響を与えません。その結果この関数の実行結果は10が示されます。

一方で関数refType()では参照型である配列の変数を代入した場合の挙動を試験しています。配列の変数は参照型であり、参照型の変数には「値を格納しているメモリ上のアドレス(参照)」が格納されています。そのため(3)で配列xyに代入していますが、ここで実際に行われていることは、変数xの値を格納しているメモリ上のアドレスを変数yにセットしていることになります(参照渡し)。そのためこの時点でxyは同じ場所を見ていることになり、(4)でyの内容を変えると同時に変数xの内容も変更されることになります。結果、この関数の実行結果は[500, 600]が示されます。

以下でSolidityで規定されている基本型と参照型のそれぞれのデータ型について見ていきます。

符号付き整数型

以下の例のように符号付き整数型はint●句を用いて宣言します。ここで「●」は変数のビット長の指定であり8~256までの8の倍数が指定されます。例えばint8は8ビット符号付整数型を表わします(-128 ~ 127)。数字が省略されintと指定された場合はint256を意味します。またデフォルト値は0です。

int16 a = -10;  // 16 bitの符号付き整数型を宣言し「-10」を格納。
int b = 100;    // 256 bitの符号付整数型を宣言し「100」を格納。
int c;  //代入を行わないとデフォルト値「0」が格納される。

符号なし整数型

以下の例のように符号なし整数型はuint●句を用いて宣言します。ここで「●」は変数のビット長の指定であり8~256までの8の倍数が指定されます。例えばuint8は8ビット符号なし整数型を表わします(0 ~ 255)。数字が省略されuintと指定された場合はuint256を意味します。またデフォルト値は0です。

uint16 a = 10;  // 16 bitの符号付き整数型を宣言し「10」を格納。
uint b = 100;    // 256 bitの符号付整数型を宣言し「100」を格納。
uint c;  //代入を行わないとデフォルト値「0」が格納される。
uint d = -10; //【コンパイルエラー】uint型に負値は格納不可。

真偽型

真偽(論理)型変数はbool句により宣言します。true(真)またはfalse(偽)の2値のみが格納可能です。デフォルト値はfalseになります。

アドレス型

アドレス型はEOAやContractの20バイトの長さのアドレスを格納する型でありaddress句で宣言します。

address a = 0xabc;  // "0x0000000000000000000000000000000000000abc"のアドレス値が格納される。
address b; //デフォルト値は"0x0000000000000000000000000000000000000000"

■ balance属性

アドレス型にはbalance属性が規定されています。例えば下記の例の用にbalance属性を用いると指定のアドレスが保有するetherの量が取得可能です。

address a = 0xa; //アドレス型変数aに0xaのアドレスを格納。
uint b = a.balance; //アドレス"0xa"の持つetherの量をbに格納。

■ send()関数

アドレス型にはsend関数が規定されています。<address>.send(x);により指定のアドレス<address>xweiのetherを送金することができます。以下に実際に送金を行う簡単なコントラクトのコードを示します。

contract Test {
    function sendTest(){
        address a = 0xf2057b8aefb9093331faf48f30c1ebeab4ff961d; //送信先のアドレスの指定
        a.send(5); //コントラクト・アドレスが保有するetherから指定のアドレス"a"へ5wei送金
    }
}

【TIP】実際に上記のコントラクトのsendTest関数を動かす際は、コードをコンパイルしブロックチェーンに登録後、本コントラクトのアドレスに5wei以上のetherを送金しておく必要があります。というのもsendTest()関数はコントラクトが保有するetherを指定のアドレスに送る関数であるためです。また、アカウントの状態を変える関数のために、「Contractを作成してみる」節で説明したとおり、sendTestの関数の実行はsendTransaction関数を用いて呼び出す必要があります。

配列

Solidityでは、固定長、可変長のどちらの配列型も扱うことが可能です。配列要素の型はストレージ変数の場合は任意のデータ型の配列を定義することが可能です。

固定長配列は、データ型T、長さkの配列はT[k]で宣言します。一方可変長配列の場合はT[]とします。

以下に、固定長配列を扱うSolidityコードの例を示します。

contract arrayTest {
    uint8[5] uintArray;
    string[3] stArray;

    function arrayTest(){ // コンストラクタ
        uint8 x = 0;
        while(x < 5){
            uintArray[x] = 100 - x;
            x++;
        }

        stArray[0] = "Apple";
        stArray[1] = "Orange";
        stArray[2] = "Pineapple";
    }

    //uint型配列全体を取り出す。
    function getUintArray() constant returns (uint8[5]){
        return uintArray;
    }

    //uint型配列の特定の要素(x番目の要素)を取り出す。
    function getUintValue(uint8 x) constant returns (uint8){
        return uintArray[x];
    }

    //string型配列の特定の要素(x番目の要素)を取り出す。
    function getStValue(uint8 x) constant returns (string){
        return stArray[x];
    }
/*
    //string型配列全体を取り出す。(Not Supported)
    function getStArray() constant returns (string[3]){
        return stArray;
    }
*/
}

上記のContractコードをコンパイルし、ブロックチェーン上に登録しContractのそれぞれの関数を呼び出すと下記のような実行結果になります。

> arraytest.getUintArray()
[100, 99, 98, 97, 96]
> arraytest.getUintValue(2)
98
> arraytest.getStValue(1)
"Orange"

また、上記Contractコードでコメントアウトしている関数(getStArray)はstring型の配列全体を返す関数を意図していますが現バージョンのコンパイラでは「Error: Internal type is not allowed for public and external functions.」というエラーになりコンパイルできません。これはuint等とは異なり、文字列型自体がコンパイラ内でバイト配列型として扱われており、「文字列型の配列」はネストされた2次元配列として扱われます。現バージョンコンパイラは、ネストされた配列を関数の引数または返り値に指定することをサポートしておらず、エラーになります。

■ length属性、push関数

配列には配列長さを示すlength属性が規定されています。可変長配列では下記の例のようにlength属性を指定することで配列要素を削除することも可能です。

また可変長配列の最後に要素を追加するpush関数が用意されています。使い方の例を以下に示します。

contract pushLengthTest {
    uint8 arraylength = 5;
    uint[] uintArray;

    function pushLengthTest(){ 
        uint8 i = 0;
        uint8 basenum = 10;
        while(i < arraylength){
            uintArray.push(basenum + i);
            i++;
        }
    }

    function getArray() constant returns (uint[]){
        return uintArray;
    }

    function getLength() constant returns (uint){
        return uintArray.length;
    }

    function setLength(uint x) returns (uint[]){
        uintArray.length = x;
        return uintArray;
    }
}

上記のContractコードをコンパイルし、ブロックチェーン上に登録しContractのそれぞれの関数を呼び出すと下記のような実行結果になります。

> pushlengthtest.getArray()
[10, 11, 12, 13, 14]
> pushlengthtest.getLength()
5
> pushlengthtest.setLength.sendTransaction(2, {from:eth.coinbase}) //Contractの状態変数を更新するためsendTransaction関数を使用する必要があることに注意。
"0x4a2b2cb6954b0cdd9dbae869f758d425334194736d0fb7f1c4913f4b75c17acb"
>
(トランザクションが採掘された後、再度getArray()関数を呼出す。)
> pushlengthtest.getArray()
[10, 11]  ← 先にlengthが2と指定されたため、indexが2以上の要素は削除される。

Last updated