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で指定するだけでした。
コメント