Fortran90(多次元配列)

旧2-5. Fortran毎日学習

Fortranで多次元配列を使ってみます。

二次元配列

まずは二次元から。

takk@deb9:~$ cat arr1.f90
      program main
        integer a(2,3)
        data a /10,20,30,40,50,60/

        write(*,*) a(1,1)
        write(*,*) a(1,2)
        write(*,*) a(1,3)
        write(*,*) a(2,2)
        write(*,*) a(2,1)
        write(*,*) a(2,3)

        stop
      end
takk@deb9:~$

dataを使って、初期化をしていますが、どのような順番で格納されたのでしょうか。

takk@deb9:~$ gfortran arr1.f90
takk@deb9:~$ ./a.out
          10
          30
          50
          40
          20
          60
takk@deb9:~$

実行結果から、このように格納されているようです。

 (,1) (,2) (,3)
+----+----+----+
| 10 | 30 | 50 | (1,)
+----+----+----+
| 20 | 40 | 60 | (2,)
+----+----+----+

二次元配列も配列代入が使えるのでしょうか。試してみます。

takk@deb9:~$ cat arr2.f90
      program main
        integer a(2,3)

        a = 33

        do m=1,3
          do n=1,2
            write(*,*) a(n,m)
          end do
        end do

        stop
      end
takk@deb9:~$ gfortran arr2.f90
takk@deb9:~$ ./a.out
          33
          33
          33
          33
          33
          33
takk@deb9:~$

できました。1行の代入文ですべて初期化されました。

多次元配列をGDBで確認する

配列代入が中で何をしているか気になって仕方がありません。GDBを使って見ていきます。
まずは、配列要素が1つで、次元数1つのプログラムで確認してみます。

a(1)

という整数を定義し、配列代入a=100を実行するプログラムです。

takk@deb9:~$ cat arr1x1.f90
      program main
        integer a(1)
        a = 100
        stop
      end
takk@deb9:~$ gfortran -g arr1x1.f90
takk@deb9:~$

GDBを起動します。

takk@deb9:~$ gfortran -g arr1x1.f90
takk@deb9:~$ gdb a.out

          ~

(gdb) l
1             program main
2               integer a(1)
3               a=100
4               stop
5             end
(gdb)

a=100の行にブレイクポイントを設定し実行します。

(gdb) b 3
Breakpoint 1 at 0x7d8: file arr1x1.f90, line 3.
(gdb) r
Starting program: /home/takk/a.out

Breakpoint 1, MAIN__ () at arr1x1.f90:3
3               a=100
(gdb)

アセンブラを見てみましょう。

(gdb) disas
Dump of assembler code for function MAIN__:
   0x00005555555547d0 <+0>:     push   %rbp
   0x00005555555547d1 <+1>:     mov    %rsp,%rbp
   0x00005555555547d4 <+4>:     sub    $0x10,%rsp
=> 0x00005555555547d8 <+8>:     mov    $0x1,%eax
   0x00005555555547dd <+13>:    cmp    $0x1,%rax
   0x00005555555547e1 <+17>:    jg     0x5555555547f5 <MAIN__+37>
   0x00005555555547e3 <+19>:    lea    -0x1(%rax),%rdx
   0x00005555555547e7 <+23>:    movl   $0x64,-0x4(%rbp,%rdx,4)
   0x00005555555547ef <+31>:    add    $0x1,%rax
   0x00005555555547f3 <+35>:    jmp    0x5555555547dd <MAIN__+13>
   0x00005555555547f5 <+37>:    mov    $0x0,%esi
   0x00005555555547fa <+42>:    mov    $0x0,%edi
   0x00005555555547ff <+47>:    callq  0x555555554660 <_gfortran_stop_string@plt>
End of assembler dump.
(gdb)

100は、16進で0x64ですので、<+23>の行あたりで、a(1)へ配列代入しているものと思われます。<+35>の行のjmpは、アドレス0x5555555547ddに戻っているので配列のループを作っていますね。同アドレスでチェックしてます。
この作りを覚えておいて、次は二次元配列にした場合、どう変わるのか見てみます。

takk@deb9:~$ cat arr1x2.f90
      program main
        integer a(1,1)
        a=100
        stop
      end
takk@deb9:~$

アセンブラを見てみます。

(gdb) disas
Dump of assembler code for function MAIN__:
   0x00005555555547d0 <+0>:     push   %rbp
   0x00005555555547d1 <+1>:     mov    %rsp,%rbp
   0x00005555555547d4 <+4>:     sub    $0x10,%rsp
=> 0x00005555555547d8 <+8>:     mov    $0x1,%eax
   0x00005555555547dd <+13>:    cmp    $0x1,%rax
   0x00005555555547e1 <+17>:    jg     0x55555555480a <MAIN__+58>
   0x00005555555547e3 <+19>:    lea    -0x2(%rax),%rsi
   0x00005555555547e7 <+23>:    mov    $0x1,%edx
   0x00005555555547ec <+28>:    cmp    $0x1,%rdx
   0x00005555555547f0 <+32>:    jg     0x555555554804 <MAIN__+52>
   0x00005555555547f2 <+34>:    lea    (%rdx,%rsi,1),%rcx
   0x00005555555547f6 <+38>:    movl   $0x64,-0x4(%rbp,%rcx,4)
   0x00005555555547fe <+46>:    add    $0x1,%rdx
   0x0000555555554802 <+50>:    jmp    0x5555555547ec <MAIN__+28>
   0x0000555555554804 <+52>:    add    $0x1,%rax
   0x0000555555554808 <+56>:    jmp    0x5555555547dd <MAIN__+13>
   0x000055555555480a <+58>:    mov    $0x0,%esi
   0x000055555555480f <+63>:    mov    $0x0,%edi
   0x0000555555554814 <+68>:    callq  0x555555554660 <_gfortran_stop_string@plt>
End of assembler dump.
(gdb)

前に戻るjmpが2つになったので、次元数と一致します。要素数が1でもそれ以外でも関係なくこの作りになってそうです。
ではビルドの限界である七次元配列で確認してみます。要素数はそれぞれ2にしておきます。
確認に使ったプログラムはこれです。

takk@deb9:~$ cat arr2x7.f90
      program main
        integer a(2,2,2,2,2,2,2)
        a=100
        stop
      end
takk@deb9:~$

アセンブラです。

 (gdb) disas
Dump of assembler code for function MAIN__:
   0x00005555555547d0 <+0>:     push   %rbp
   0x00005555555547d1 <+1>:     mov    %rsp,%rbp
   0x00005555555547d4 <+4>:     push   %r15
   0x00005555555547d6 <+6>:     push   %r14
   0x00005555555547d8 <+8>:     push   %r13
   0x00005555555547da <+10>:    push   %r12
   0x00005555555547dc <+12>:    push   %rbx
   0x00005555555547dd <+13>:    sub    $0x208,%rsp
=> 0x00005555555547e4 <+20>:    mov    $0x1,%edi
   0x00005555555547e9 <+25>:    cmp    $0x2,%rdi
   0x00005555555547ed <+29>:    jg     0x5555555548c3 <MAIN__+243>
   0x00005555555547f3 <+35>:    mov    %rdi,%rax
   0x00005555555547f6 <+38>:    shl    $0x6,%rax
   0x00005555555547fa <+42>:    lea    -0x7f(%rax),%rbx
   0x00005555555547fe <+46>:    mov    $0x1,%r8d
   0x0000555555554804 <+52>:    cmp    $0x2,%r8
   0x0000555555554808 <+56>:    jg     0x5555555548ba <MAIN__+234>
   0x000055555555480e <+62>:    mov    %r8,%rax
   0x0000555555554811 <+65>:    shl    $0x5,%rax
   0x0000555555554815 <+69>:    lea    (%rax,%rbx,1),%r12
   0x0000555555554819 <+73>:    mov    $0x1,%r9d
   0x000055555555481f <+79>:    cmp    $0x2,%r9
   0x0000555555554823 <+83>:    jg     0x5555555548b1 <MAIN__+225>
   0x0000555555554829 <+89>:    mov    %r9,%rax
   0x000055555555482c <+92>:    shl    $0x4,%rax
   0x0000555555554830 <+96>:    lea    (%rax,%r12,1),%r13
   0x0000555555554834 <+100>:   mov    $0x1,%eax
   0x0000555555554839 <+105>:   cmp    $0x2,%rax
   0x000055555555483d <+109>:   jg     0x5555555548a8 <MAIN__+216>
   0x000055555555483f <+111>:   lea    0x0(,%rax,8),%rdx
   0x0000555555554847 <+119>:   lea    (%rdx,%r13,1),%r14
   0x000055555555484b <+123>:   mov    $0x1,%edx
   0x0000555555554850 <+128>:   cmp    $0x2,%rdx
   0x0000555555554854 <+132>:   jg     0x5555555548a2 <MAIN__+210>
   0x0000555555554856 <+134>:   lea    0x0(,%rdx,4),%rcx
   0x000055555555485e <+142>:   lea    (%rcx,%r14,1),%r15
   0x0000555555554862 <+146>:   mov    $0x1,%ecx
   0x0000555555554867 <+151>:   cmp    $0x2,%rcx
   0x000055555555486b <+155>:   jg     0x55555555489c <MAIN__+204>
   0x000055555555486d <+157>:   lea    (%rcx,%rcx,1),%rsi
   0x0000555555554871 <+161>:   lea    (%rsi,%r15,1),%r11
   0x0000555555554875 <+165>:   mov    $0x1,%esi
   0x000055555555487a <+170>:   cmp    $0x2,%rsi
   0x000055555555487e <+174>:   jg     0x555555554896 <MAIN__+198>
   0x0000555555554880 <+176>:   lea    (%rsi,%r11,1),%r10
   0x0000555555554884 <+180>:   movl   $0x64,-0x230(%rbp,%r10,4)
   0x0000555555554890 <+192>:   add    $0x1,%rsi
   0x0000555555554894 <+196>:   jmp    0x55555555487a <MAIN__+170>
   0x0000555555554896 <+198>:   add    $0x1,%rcx
   0x000055555555489a <+202>:   jmp    0x555555554867 <MAIN__+151>
   0x000055555555489c <+204>:   add    $0x1,%rdx
   0x00005555555548a0 <+208>:   jmp    0x555555554850 <MAIN__+128>
   0x00005555555548a2 <+210>:   add    $0x1,%rax
   0x00005555555548a6 <+214>:   jmp    0x555555554839 <MAIN__+105>
   0x00005555555548a8 <+216>:   add    $0x1,%r9
   0x00005555555548ac <+220>:   jmpq   0x55555555481f <MAIN__+79>
   0x00005555555548b1 <+225>:   add    $0x1,%r8
   0x00005555555548b5 <+229>:   jmpq   0x555555554804 <MAIN__+52>
   0x00005555555548ba <+234>:   add    $0x1,%rdi
   0x00005555555548be <+238>:   jmpq   0x5555555547e9 <MAIN__+25>
   0x00005555555548c3 <+243>:   mov    $0x0,%esi
   0x00005555555548c8 <+248>:   mov    $0x0,%edi
   0x00005555555548cd <+253>:   callq  0x555555554660 <_gfortran_stop_string@plt>
End of assembler dump.
(gdb)

予想通り配列処理のループを作っているjmpが7つあります。(jmpqが混ざってますが)
配列代入といえども、一つずつ代入していることには変わりありませんね。

配列の内容表示

配列の値を表示するのにwriteを使うため、doで繰り返し制御していましたが、簡単な書き方があるようです。使ってみます。
まずは、今までのdoでwriteを繰り返し実行する方法。

takk@deb9:~$ cat arr1.f90
      program main
        integer a(5)
        data a /1,2,3,4,5/

        do i=1,5
          write(*,*) a(i)
        end do

        stop
      end
takk@deb9:~$ gfortran arr1.f90
takk@deb9:~$ ./a.out
           1
           2
           3
           4
           5
takk@deb9:~$

次にdoを使わず、writeに指定する変数に指定する方法。do型反復というらしいです。

a(i)の部分を、
(a(i),i=1,5)に置き換えます。

takk@deb9:~$ cat arr2.f90
      program main
        integer a(5)
        data a /1,2,3,4,5/

        write(*,*) (a(i),i=1,5)

        stop
      end
takk@deb9:~$ gfortran arr2.f90
takk@deb9:~$ ./a.out
           1           2           3           4           5
takk@deb9:~$

タブ区切りで全要素表示されました。
これはこれで良いのですが、改行して表示したい場合どうすれば良いでしょう。

takk@deb9:~$ cat arr3.f90
      program main
        integer a(5)
        data a /1,2,3,4,5/

        write(*,10010) (a(i),i=1,5)

        stop
10010 format(I3)
      end
takk@deb9:~$ gfortran arr3.f90
takk@deb9:~$ ./a.out
  1
  2
  3
  4
  5
takk@deb9:~$

formatで指定するだけでした。

コメント

タイトルとURLをコピーしました