1から100の整数を出力してください( ただし5つの異なる方法を用いて )の1

forやめろ -- LL Diver 2014 LT枠

LL DiverのLTの1コマで、「1から100の整数を出力してください( ただし5つの異なる方法を用いて )」という話がありました。バカらしいの込みでもいいから5つ以上つくってみたいと思います。

まずはシンプルにforループを使用したもの。

はじめに

for(i = 0; i < 100; i++)

って書いちゃって、0から99の整数を出力してしまうというポカをしてしまいました。

多次元配列のちょっと変わった参照の仕方

いきなりですが、配列の値を参照するための a[1] というのは、 *(a + 1) を少しでも気楽に書けるようにするための構文らしいです(こういうのをシンタックスシュガーと呼ぶらしい)。
でもって、*(a + 1) と *(1 + a) の値は同じになりますね?ここで、*(1 + a) を[]使った形に直すと 1[a] になります。実は a[1] と 1[a] は同じ意味になってしまうのだ!


とまあ、ここまでは

C言語ポインタ完全制覇 (標準プログラマーズライブラリ)

C言語ポインタ完全制覇 (標準プログラマーズライブラリ)

を読んだりして知っていたんですが、多次元になるとどうなるのかとふと思い立ちやってみました。
といっても、構文規則は同じなので恐れることはない。

#include <stdio.h>

int main()
{
    int a[2][3][4] = {
        {
            {  0,  1,  2,  3, },
            {  4,  5,  6,  7, },
            {  8,  9, 10, 11, },
        },
        {
            { 12, 13, 14, 15, },
            { 16, 17, 18, 19, },
            { 20, 21, 22, 23, },
        },
    };

    printf("%d\n", a[1][2][3]);
    printf("%d\n", (*(a + 1))[2][3]);
    printf("%d\n", (*(*(a + 1) + 2))[3]);
    printf("%d\n", *(*(*(a + 1) + 2) + 3));
    printf("%d\n", *(*(1[a] + 2) + 3));
    printf("%d\n", *(2[1[a]] + 3));
    printf("%d\n", 3[2[1[a]]]);

    return 0;
}

どの printf でも 23 が出力されます。だからなんだっていうんだって話ですがね。
そういえば、*(a + 1)[2][3]だと *((a + 1)[2][3]) になるみたい。ここは実行してみてから気づいた。

じつはそんなに不思議な現象ではないのかも

このコードについて???? コンパイラの不思議 - goinger的日記を読んで軽く気になったので -fno-builtin でビルトインなものを消してみたりして考えみました。たどり着いたのは、C言語はファイル別にコンパイルするというだけの理由ではないかというもの。


たとえば、

int add(int a, int b)
{
    return a + b;
}

という関数を add.c として保存して、

void add();
main()
{
    add();
}

を add.c とは別に test.c として作成する。
そのうえで

$ gcc test.c add.c

としてもエラーはでない。


コンパイルするときはプロトタイプ宣言のみしか見ていないからこれでも動くのでしょう。今回は、 puts() であると警告が入るので特別に感じたのではないかと考えます。

ちょっと奇をてらって

構造体変数に自分自身のポインタを持たせて、ごにょごにょしてみた。

#include <stdio.h>
#include <stdlib.h>

struct hoge {
    struct hoge *self;
    int i;
    struct hoge *(*print)(struct hoge *);
};

struct hoge *p(struct hoge *v)
{
    printf("%d\n", v->i);
    v->i += 2;
    return v;
}

int main()
{
    struct hoge *val;

    val        = malloc(sizeof(struct hoge));
    val->self  = val;
    val->i     = 3;
    val->print = p;

    val->print(val->self)->print(val->self)->print(val->self); // これでも動く不思議

    return 0;
}

実行すると

$ gcc --version
gcc (GCC) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc -Wall -ansi -o test test.c
$ ./test 
3
5
7
$ 

警告も吐きません。GJ