インクリメント・デクリメント演算子

C 言語にはインクリメント演算子とデクリメント演算子という便利な演算子があります:

#include <stdio.h>

int main ()
{
    int a = 5;
    --a;

    printf ("%d\n", a);
    return 0;
}

上の例なら、変数 a は最終的に 4 になります。

では以下はどうなるでしょうか:

/* Practical C Programming (O'Reilly) 参照 */
#include <stdio.h>

int main ()
{
    int a = 5;
    a = --a - a--;

    printf ("%d\n", a);
    return 0;
}

答えは -1 です。以下のように分解すると分かりやすいです:

--a;
a = a - a;
a--;

実際、コンパイラ (gcc) は以下のようなアセンブリコードを出力します:

        movl    $5, 28(%esp)
        subl    $1, 28(%esp)
        movl    28(%esp), %eax
        subl    %eax, %eax
        movl    %eax, 28(%esp)
        subl    $1, 28(%esp)

スタック領域に long 型の 5 をコピーし、1 を引き、結果 (=4) を eax レジスタにコピーし、eax – eax し、結果をスタックにコピーし直し、さらに 1 を引く、という内容です。

ちなみに、書き下した C コードは微妙に異なるアセンブリコードに変換されました:

        movl    $5, 28(%esp)
        subl    $1, 28(%esp)
        movl    $0, 28(%esp)
        subl    $1, 28(%esp)

なお、Python にインクリメント・デクリメント演算子が実装されない理由の 1 つに、上のような一見意味不明のコードを避けるということがあるようです (http://www.gossamer-threads.com/lists/python/dev/455027)。

※ 追記

gcc 4.6.3 で以下のコードをコンパイルすると、8 が表示されます:

/* Practical C Programming (O'Reilly) 参照 */
#include <stdio.h>

int main ()
{
    int value;
    int result;

    value = 1;
    result = (value++ * 5) + (value++ * 3);
    printf ("%d\n", result);
    return 0;
}

以下のようなイメージです:

value = 1;
result = value * 5 + value * 3;
value++;
value++;

アセンブリコードは以下のようです:

        movl    $1, 28(%esp)      # 28(%esp) = 1
        movl    28(%esp), %edx    # %edx = 1
        movl    %edx, %eax        # %eax = 1
        sall    $2, %eax          # %eax = 4
        leal    (%eax,%edx), %ecx # %ecx = 5
        movl    28(%esp), %edx    # %edx = 1
        movl    %edx, %eax        # %eax = 1
        addl    %eax, %eax        # %eax = 2
        addl    %edx, %eax        # %eax = 3
        addl    %ecx, %eax        # %eax = 8
        movl    %eax, 24(%esp)    # 24(%esp) = 8 = result
        addl    $1, 28(%esp)      # 28(%esp) = 2
        addl    $1, 28(%esp)      # 28(%esp) = 3 = value

結果はコンパイラ依存のようです。pcc 1.0.0 でコンパイルすると、13 が表示されます。

アセンブリコードは以下のようです:

        movl $1,-4(%ebp)      # -4(%ebp) = 1
        movl -4(%ebp),%edx    # %edx = 1
        incl %edx             # %edx = 2
        movl %edx,-4(%ebp)    # -4(%ebp) = 2
        decl %edx             # %edx = 1
        imull $3,%edx         # %edx = 3
        movl -4(%ebp),%eax    # %eax = 2
        incl %eax             # %eax = 3
        movl %eax,-4(%ebp)    # -4(%ebp) = 3 = value
        decl %eax             # %eax = 2
        imull $5,%eax         # %eax = 10
        leal (%eax,%edx),%eax # %eax = 13
        movl %eax,-8(%ebp)    # -8(%ebp) = 13 = result

トリッキーなコードは避けないといけませんね!

(コウヅ)

インクリメント・デクリメント演算子」への2件のフィードバック

コメントを残す