計算物理学II (第9回)
•演習(消化できていないところを) •第３回レポート(提出締切：12/28) •配列の動的割付(C/Fortran) •関数の引数について•ポインタ(C言語)
•通常の配列の領域確保では次元を整数値で設定複数の同じサイズの配列がある場合に変更が大変program main implicit none integer :: array(5) end program main •配列のサイズに変数を使う配列より手前でparameter属性をつけて変数を宣言parameter属性がついた変数の値は変更できないprogram main implicit none integer, parameter :: n = 5 integer :: array(n) end program mainマクロ定数(コンパイル時にNは5に置き換えられる) #include<stdio.h> #define N 5 int main(){ int array[N];変数を使うと複数の同じサイズの配列があるときに書き換える箇所は1箇所で済む

サイズも引数で渡す場合
program main implicit none integer, parameter :: n = 5 integer :: array(n) array = 4 call print_array(n,array) call print_array(3,array) contains subroutine print_array(m,array_to_print) implicit none integer,intent(in) :: m integer,dimension(1:m),intent(in) :: array_to_print print *, array_to_print(1:m) end subroutine print_array end program main配列のサイズはintent(in)属性とすると配列の次元の宣言で使える実行結果4 4 4 4 4 4サイズは関数側で計算する場合program main implicit none integer, parameter :: n = 5 integer :: array(n) array = 4 call print_array(array) call print_array(array(1:3)) contains subroutine print_array(array_to_print) implicit none integer,dimension(:),intent(in) :: array_to_print integer :: size size = sizeof(array_to_print(:))/4 print *, array_to_print(1:size) end subroutine print_array end program main関数内では配列のサイズは指定しないで宣言sizeofはバイト数を返すのでinteger型なら4バイトで割る4 4
サイズも引数で渡す必要があるprintf("\n"); int main(){ int array[5]={0,1,2,3,4}; array_to_print(5,array); array_to_print(3,array); ←array[m]はarray[]でもよい実行結果0 1 2 3 4 0 1 2 C言語では配列を関数に渡すとき関数内から要素数がわからない
•コンパイル時に配列のサイズが決まらない場合program main integer, allocatable :: array(:) write(*,*) ''n = ?'' read(*,*) n print *, "allocated? ", allocated(array) allocate(array(n)) print *, "allocated? ", allocated(array) array= 3 print *, array(1:n) deallocate(array) end program main実行結果3 allocatable属性を付けて配列を宣言、allocateでメモリに配列の領域を実行時に確保deallocateでメモリに確保した配列領域を解放allocatedで配列の割付状況がわかる(.True. or .False.)すでにallocateされているものを再割付したり、allocateされてないものをdeallocateするとエラーになる
program main implicit none real*8 :: x, y x = 3.0; y = 0.0; print *, "main: x = ", x, "y = ", y call func(x,y) print *, "main: x = ", x, "y = ", y contains subroutine func(x,y) real*8, intent(in) :: x     intent(in)をつけるとサブルーチン内で変数の値を変更できないreal*8, intent(out) :: y   intent(out)をつけるとサブルーチン内で変数の値を更新できるy = X**2 print *, "func: x = ", x, "y = ", y return end subroutine func end program main実行結果
宣言された変数はメモリ上に格納する領域が確保され、アドレスが割り当てられる。変数aのメモリ上のアドレスは&a (&はアドレス演算子)配列b[3]の先頭の要素b[0]のアドレスはbまたは&b[0] %pは変数のメモリ上でのアドレスを表示(16進数)表示される変数のアドレスは実行するごとに異なる#include<stdio.h> int main(){ int a, b[3]; double c, d[3];
16進数で表示されたメモリ上のアドレスbと&b[0]は同じb[1]のアドレスは4バイト後ろd[1]のアドレスはd[0]の8バイト後ろ
•ポインタ変数には変数のアドレスを格納(%pでアドレスを表示できる) • *(間接演算子)をポインタ変数の手前につけるとポインタの指す先の値にアクセスできる。 include<stdio.h> int main(){ int a = 1, b = 2; int *p; // pは整数型へのポインタ変数printf("&a = %p\n", &a); // aのアドレス値printf("&b = %p\n", &b); // bのアドレス値p = &a; // pにaのアドレスを代入printf("p = %p\n", p); //ポインタ自体の値(アドレス値) printf("a=%d,b=%d,*p=%d\n", a,b,*p); // *pは変数aの値a = 3; printf("a=%d,b=%d,*p=%d\n", a,b,*p); p = &b; // pにbのアドレスを代入printf("p = %p\n", p); //ポインタ自体の値(アドレス値) printf("a=%d,b=%d,*p=%d\n", a,b,*p); a = 0; printf("a=%d,b=%d,*p=%d\n", a,b,*p); // aの値を変えても*pは変わらない&a = 0x7ffc48f0fa08 &b = 0x7ffc48f0fa0c p = 0x7ffc48f0fa08 a=1,b=2,*p=1 a=3,b=2,*p=3 p = 0x7ffc48f0fa0c a=3,b=2,*p=2 a=0,b=2  *p=2 aとbのアドレスは宣言時に割り当てられるp=&aによりpにaのアドレスが格納される*pはaに格納された値を示すp=&bによりpにbのアドレスが格納される*pはbに格納された値を示すアドレス： a 0x7ffc48f0fa08~ 0x7ffc48f0fa0b 0x7ffc48f0fa0c~ 0x7ffc48f0fa0f
配列の任意の要素のアドレスをポインタに格納すれば要素の値も参照できる#include<stdio.h> int main(){ int a[3]; // 3要素の配列int *p; //整数型へのポインタ変数int i; a[0]=12; a[1]=24; a[2]=36; for(i=0; i<3; i++){ printf("a[%d]=%d, &a[%d] = %p\n", i, a[i], i, &a[i]); printf(" a = %p\n", a); // aは&a[0]と同じく配列の先頭のアドレスp=a; //pに配列の先頭アドレスを代入printf(" *p = %d\n", *p); p=&a[1] ;  //pにa[1]のアドレスを代入printf(" *p = %d\n", *p); p=&a[2]; // pにa[2]のアドレスを代入printf('' *p = %d\n'', *p); a[0]=12, &a[0] = 0x7ffd0b4b17ac a[1]=24, &a[1] = 0x7ffd0b4b17b0 a[2]=36, &a[2] = 0x7ffd0b4b17b4 a = 0x7ffd0b4b17ac ＊D = 12 *p = 24 *p = 36配列(1要素4バイトでアドレスは連続) 0x7ffd0b4b17ac 0x7ffd0b4b17b0 0x7ffd0b4b17b4
ポインタは間接参照以外に加減算、等号不等号を用いた値の比較が可能#include<stdio.h> int main(){ int a[3]; // 3要素の配列int *p; //整数型へのポインタ変数int i; a[0]=12; a[1]=24; a[2]=36; for(i=0; i<3; i++){ printf("a[%d]=%d, &a[%d] = %p\n", i, a[i], i, &a[i]); printf(" a = %p\n", a); // aは&a[0]と同じく配列の先頭のアドレスp=a; //pに配列の先頭アドレスを代入printf(" *p = %d\n", *p); p++; //pのアドレスを1つ(4バイト)すすめるbruntf(" *p = %d\n", *p); p++; // pのアドレスを1つ(4バイト)すすめるprintf (" *p = %d\n", *p); a[0]E12, &a[0] = 0x7fff85a4c58c a[1]=24, &a[1] = 0x7fff85a4c590 a[2]=36, &a[2] = 0x7fff85a4c594 a = 0x7fff85a4c58c *p = 12 *p = 24 *p = 36整数型へのポインタは1増やすとアドレスが4バイト増える倍精度実数型へのポインタは1増やすとアドレスが8バイト増える
#include<stdio.h> void func(double x, double y){ printf("in func: &x = %p &y = %p\n", &x,&y); y = X*X; printf("in func; x = %f, y = %f\n", x, y); in main: &x = 0x7ffe423f9c08, &y = 0x7ffe423f9c10 in func: &x = 0x7ffe423f9bd8 &y = 0x7ffe423f9bd0 in func; x = 2.00000, y = 4.000000 in main: x = 2.00000, y = 0.000000 int main(){ double x,y; x = 2.0; y=0.0; printf("in main: &x = %p, &y = %p\n", &x,&y); func(x,y); printf("in main: x = %f, y = %f\n", x, y);関数funcの中でyを計算してもmainに戻ったときに値を参照できない関数に値渡しをすると関数側で値がコピーされた新しい領域が用意される関数の中で計算された変数には呼び出し元のmainから参照できない
#include<stdio.h> void func(double x, double *y){ // yはポインタ変数printf("in func: &x = %p, y = %p\n", &x,y); *y = x*x; // yの指す値にx*xを代入printf("in func; x = %f, y = %f\n", x, *y); int main(){ double x,y; x = 2.0; y=0.0; printf("in main: &x = %p, &y = %p\n", &x,&y); func(x,&y); // yのアドレスを渡すprintf("in main: x = %f,  y = %f\n", x, y);関数の中で更新したい変数はアドレスを渡す。関数側ではポインタ変数として宣言する。 in main: &x = 0x7ffc3d66cc68, &y = 0x7ffc3d66cc70 in func: &x = 0x7ffc3d66cc38,  y = 0x7ffc3d66cc70 in func; x = 2.00000, y = 4.000000 in main: x = 2.00000, y = 4.000000 mainからfuncにyのアドレスが渡される。 xは値がコピーされるためmainとfuncでxのアドレスが異なる関数の中からyの値を更新できる
free(array); //確保した領域を解放