配列とポインタ

奇数の魔方陣を例にして、配列とポインタの扱い方を説明します。
魔方陣とは1~Nまでの数字を「縦、横の合計が等しくなる」ように並べた行列です。
全ての奇数の魔方陣(3方陣、5方陣、7方陣・・・)は共通のアルゴリズムで作成することができます。

前田稔(Maeda Minoru)の超初心者のプログラム入門

魔方陣のアルゴリズム

魔方陣とは1~Nまでの数字を「縦、横、[斜め] の合計がいずれも等しくなる」ように並べた行列です。
3*3の魔方陣は1~9の数を縦、横、[斜め] の合計が15になるように並べます。
5*5の魔方陣は1~25の数を縦、横、[斜め] の合計が65になるように並べます。

奇数の魔方陣(3,5,7,9,11,...)は全て次のアルゴリズムで作成することができます。
  1. 下段の中央に1を格納してここからから始めます。
  2. 1の位置から右下に移動します。
    枠の外に出たときは、その位置に対応する枠の中に移動します。
  3. 2の位置から右下に移動します。
    枠の外に出たときは、その位置に対応する枠の中に移動します。
  4. 3の位置の右下には既に1が格納されています。
    そのときは右下ではなくて上に移動します。
  5. これを全ての箱が埋まるまで繰り返すと魔方陣が完成します。

プログラムの説明

  1. 最初に3*3の魔方陣を作成してみます。
    Function Prototype と main() 関数です。
    二次元配列のパラメータは t[3][3] または t[][3] で受け取ります。
    一次元目の 3 は省略可能ですが、二次元目は省略出来ません。
    二次元目が省略されると、添え字の計算が出来なくなるためでしょう。
    #include <stdio.h>
    short   t3[3][3];
    
    // Function Prototype
    void  SetNumber(const short n, short t[][3]);
    void  Disp(const short n, short t[][3]);
    
    //★ MAIN PROGRAM
    void  main()
    {   printf("\n3×3の魔方陣\n");
        SetNumber(3,t3);
        Disp(3,t3);
    }
    
  2. Disp() 関数です。
    3*3以外の奇数の魔方陣にも対応したいので、配列の大きさを n で受け取ります。
    ところが残念ながら short t[][3] で受け取ると 3*3 になります。
    void  Disp(const short n, short t[][3])
    {   int     x,y;
        for(y=0; y<n; y++)
        {    for(x=0; x<n; x++)     printf("%3d",t[y][x]);
             printf("\r\n");
        }
    }
    
  3. SetNumber() 関数です。
    3*3以外の奇数の魔方陣にも対応したいので、配列の大きさを n で受け取ります。
    ところが残念ながら short t[][3] で受け取ると 3*3 になります。
    // t[n][n] に数値を設定
    void  SetNumber(const short n, short t[][3])
    {   int     i,x,y,xw,yw;
    
        for(y=0; y<n; y++)
            for(x=0; x<n; x++)  t[y][x]= 0;
        x= (n-1)/2;             //y,x 下段、中央
        y= n-1;
        for(i=1; i<=n*n; i++)
        {   t[y][x]= i;
            xw= (x+1)%n;        //右斜め下
            yw= (y+1)%n;
            if (t[yw][xw])  y= (y+n-1)%n;
            else
            {   x= xw;
                y= yw;
            }
        }
    }
    
  4. Disp() 関数や SetNumber() 関数が全ての奇数の魔方陣に対応するように、二次元配列を short 型のポインタで渡します。
    配列名(t3, t5)は short 型配列の先頭ポインタです。
    古いバージョンでは t3 をそのまま渡せたのですが、新しいバージョンではキャストが必要なようです。
    #include <stdio.h>
    
    short   t3[3][3];
    short   t5[5][5];
    
    // Function Prototype
    void  SetNumber(const short n, short *p);
    void  Disp(const short n, short *p);
    
    //★ MAIN PROGRAM
    void  main()
    {   printf("\n3×3の魔方陣\n");
        SetNumber(3,(short*)t3);
        Disp(3,(short*)t3);
        printf("\n5×5の魔方陣\n");
        SetNumber(5,(short*)t5);
        Disp(5,(short*)t5);
    }
    
  5. Disp() 関数では「大きさを n で、配列を *p」で受け取ります。
    受け取った *p は二次元配列の先頭ポインタですが、一次元配列のように扱うことが出来ます。
    t3[] が渡されたときは nn=n*n; は 9 になり、九個の値が表示されます。
    void  Disp(const short n, short *p)
    {   int nn,i;
    
        nn= n*n;
        for(i=0; i<nn; i++)
        {    printf("%3d",*(p+i));
             if (0==(i+1)%n)    printf("\r\n");
        }
    }
    
  6. 受け取った二次元配列を一次元配列のように添え字が使えるとなると、汎用的な SetNumber() 関数を作成することが出来るでしょう。
    全ての奇数の魔方陣に対応する SetNumber() 関数と Disp() 関数を完成させて下さい。
    7方陣,11方陣,13方陣なども試して下さい。

超初心者のプログラム入門(C/C++)