Presentation is loading. Please wait.

Presentation is loading. Please wait.

オセロを作るとします。 各マスの状態を保存するために64個の変数が必要です。 でも64個も変数を作るのはめんどくさいですよね。

Similar presentations


Presentation on theme: "オセロを作るとします。 各マスの状態を保存するために64個の変数が必要です。 でも64個も変数を作るのはめんどくさいですよね。"— Presentation transcript:

1 オセロを作るとします。 各マスの状態を保存するために64個の変数が必要です。 でも64個も変数を作るのはめんどくさいですよね。
第7章 配列 オセロを作るとします。 各マスの状態を保存するために64個の変数が必要です。 でも64個も変数を作るのはめんどくさいですよね。 第7章 配列

2 配列とは(1) 配列とは、一列に並んだ変数の集合体 配列 変数 メモリ メモリ 必ず一列に並んで確保 確保される場所はバラバラ 配列の宣言
文法 データ型 配列名[ 領域を確保する個数 ]; 個数は定数や数字でなければならない。変数や式は不可能! 第7章 配列

3 配列とは(2) 配列を宣言すると指定した個数分、連続してメモリに 領域が確保される 配列名をそのまま書くと、配列の先頭のアドレスを
取得できる #include <stdio.h> int main(void){ int a[5]; printf("配列aの先頭のアドレスは%pです\n", a); return 0; } アドレス + 1で 指定したアドレスの 隣のアドレスが取得できる #include <stdio.h> int main(void){ int a[5]; printf(“配列aの2つめのアドレスは%pです\n", a + 1); return 0; } 第7章 配列

4 配列の代入と参照(1) 配列の実体がポインタだと分かれば代入も参照もできますよね?
#include <stdio.h> int main(void){ int a[5]; *(a + 3) = 7; printf(“配列aの4番目の値は%dです\n", *(a + 3)); return 0; } ※*(a + 3)は配列の先頭の3つ隣なので、4番目になります メモリ *a *(a+1) *(a+2) *(a+3) *(a+4) 1番目 2番目 3番目 4番目 5番目 第7章 配列

5 配列の代入と参照(2) *(a + n)とa[n]は同じ意味 (nは整数) ゲームになると配列をたくさん使う
添字演算子で省略して書くことができる 演算子 意味 優先順位 種類 [n] 配列の参照・代入 2 単項 *(a + n)とa[n]は同じ意味 (nは整数) #include <stdio.h> int main(void){ int a[5]; a[3] = 7; printf(“配列aの4番目の値は%dです\n", a[3]); return 0; } 第7章 配列

6 配列の代入と参照(3) 特に後でやる2次元配列を 間接参照演算子を使った表し方で表すと 無茶苦茶見にくくなる 添字演算子の方が見やすい
添字演算子のがよくつかわれている 添字演算子を積極的に使うべき むしろ*(a + n)なんて書くべきではない (今回はポインタとの関係性について説明するために使っただけ) 特に後でやる2次元配列を 間接参照演算子を使った表し方で表すと 無茶苦茶見にくくなる (説明のために書くけどね) 第7章 配列

7 配列の代入と参照(4) 用語も覚えよう aを配列、nを整数として、a[n]とあるとき、 nを添字 a[n]の値を、添字nの要素
又は単に要素 配列の要素の個数のことを要素数 と言います 用語も覚えよう 第7章 配列

8 配列と引数 関数の引数として配列を渡したいこともあるでしょう 次のようにすると、引数に配列を指定できます
void func(int arr[],int arrCount){ ・・・・ } 配列と一緒に配列の要素数を渡す時が多い 配列はただのアドレスだということを用いれば、 以下のようにも書けます void func(int* arr,int arrCount){ ・・・・ } 第7章 配列

9 配列とループ処理(1) 配列とループ処理(特にfor)は非常に相性が良いです。 二つを合わせて使う場合がほとんどです
#include <stdio.h> #define COUNT 5 int main(void){ int scores[COUNT]; for(int i = 0; i < COUNT; i++){ printf("%d人目の点数 : ", i + 1); scanf("%d", &scores[i]); } printf("%d人目の点数は%dです\n", i + 1, scores[i]); return 0; 配列scoresに生徒5人分の点数を保存することができました 第7章 配列

10 配列とループ処理(2) ここに平均点を求める処理を追加しましょう ・・・先ほどと同じなので略・・・
#include <stdio.h> #define COUNT 5 int getTotal(int arr[],int arrCount){ int total = 0; for(int i = 0;i < arrCount;i++){ total += arr[i]; } return total; int main(void){ ・・・先ほどと同じなので略・・・ printf("平均点は%lfです\n", (double)getTotal(scores, COUNT) / COUNT); return 0; 第7章 配列

11 配列の初期化 配列は左のように初期化できます。 ただ、要素数が増えたら めんどくさいですよね 右のように省略して 書くことができます
#include <stdio.h> int main(void){ int a[3]; a[0] = 1; a[1] = 6; a[2] = -4; return 0; } 配列は左のように初期化できます。 ただ、要素数が増えたら めんどくさいですよね 右のように省略して 書くことができます #include <stdio.h> int main(void){ int a[] = { 1, 6, -4}; return 0; } 少しは楽になりましたね この場合は、 要素数は省略できます 第7章 配列

12 配列のコピー(1) 次のプログラムで配列をコピーできないでしょうか? コンパイルエラーが出ますね。 配列の要素に値を代入できても
#include <stdio.h> int main(void){ int a[5]; int b[5]; b = a; return 0; } コンパイルエラーが出ますね。 配列の要素に値を代入できても 配列そのものには代入できません。 第7章 配列

13 配列のコピー(2) 配列に値が代入できないなら、ポインタならどうでしょう このプログラムはコンパイルエラーは
#include <stdio.h> int main(void){ int a[5]; int* b; b = a; return 0; } このプログラムはコンパイルエラーは 出ませんが、重大な問題があります 下のプログラムを実行しよう #include <stdio.h> int main(void){ int a[5]; int* b; b = a; b[2] = 3; printf("a[2]=%d\n",a[2]); return 0; } これはあくまでアドレスの コピーであって、 配列の要素そのものは コピーされません。 よって、bを編集すれば aも編集されます。 第7章 配列

14 配列のコピー(3) 配列のコピーは要素を一つ一つコピーするしかありません #include <stdio.h>
#define COUNT 5 int main(void){ int a[COUNT]; int b[COUNT]; for(int i = 0;i < COUNT;i++){ b[i] = a[i]; } return 0; 第7章 配列

15 二次元配列(1) 配列を要素に持つ配列のことを二次元配列と言います 配列の宣言 文法 データ型 配列名[要素数][要素内の配列の要素数];
int a[2][3]と宣言すると・・・ 要素数が3の配列が2つできる 要素数が2の配列ができ、その要素に要素数3の 配列のアドレスが入る 第7章 配列

16 二次元配列(2) int a[2][3]を図式化するとこんな感じ・・・ 矢印は、終点のアドレスが始点に代入されていることを表す メモリ
要素数2の配列ができて、 要素数3の配列のアドレスが代入される a *a *(a+1) 要素数3の配列が2つ出来る **(a+1) *(*(a+1)+1) *(*(a+1)+2) **a *(*a+1) **(a+2) 変数に配列の アドレス代入 2×3の配列と考えることができる 第7章 配列

17 二次元配列(3) *(*(a + n) + m) と a[n][m]は同じ意味 (n,mは整数)
二次元配列も普通の配列と同じように添字演算子を使用できる *(*(a + n) + m) と a[n][m]は同じ意味 (n,mは整数) #include <stdio.h> int main(void){ int a[2][3]; *(*(a + 1) + 2)= 5; printf("a[1][2]=%d\n", a[1][2]); return 0; } 第7章 配列

18 二次元配列とループ処理(1) 一次元配列はループ処理と相性が良かったですが、 二次元配列もループ処理、特に二重ループと相性が良いです
次ページからその例を出します 次のページの例は、 5人の生徒の5教科の点数をそれぞれ入力させ、 各生徒の合計点数を表示するプログラムです 第7章 配列

19 二次元配列とループ処理(2) #include <stdio.h> #define PEOPLE_COUNT 5
#define SUBJECT_COUNT 5 ・・・・・getTotal関数はスライド10ページと同じ・・・・ int main(void){ int scores[PEOPLE_COUNT][SUBJECT_COUNT]; for(int i = 0;i < PEOPLE_COUNT;i++){ for(int j = 0;j < SUBJECT_COUNT;j++){ printf("%d人目の%d教科目の点数 : ",i + 1, j + 1); scanf("%d",&scores[i][j]); } printf("%d人目の合計点%d\n", i + 1, getTotal(scores[i], SUBJECT_COUNT)); return 0; 第7章 配列

20 二次元配列とループ処理(3) 2次元配列とループ処理はさまざまなところで用いられる オセロの右からn、上からmマス目には何がある? 将棋
テトリスも マインスイーパー 二次元上で自由に動けるRPGのマップ 第7章 配列

21 二次元配列とループ処理(4) 小さいので 家に帰って wikiなどで 確認してください #include <stdio.h>
#define COUNT 8 #define WHITE 1 #define BLACK 2 int cells[COUNT][COUNT]; void showColLine(){ //横罫線を表示 for(int j = 0;j < COUNT;j++){ printf("┼─"); } printf("┼\n"); void show(){ //画面を表示 printf(" \n"); for(int i = 0;i < COUNT;i++){ showColLine(); if(cells[j][i]== WHITE ){ printf("│○"); }else if(cells[j][i]==BLACK){ printf("│●"); }else{ printf("│ "); printf("│\n"); int main(void){ int player = 1; for(int i = 0;i < COUNT;i++){ for(int j = 0;j < COUNT;j++){ cells[j][i]=0; } cells[3][3]=WHITE; cells[3][4]=BLACK; cells[4][3]=BLACK; cells[4][4]=WHITE; while(true){ int x,y; show(); printf("x : "); scanf("%d",&x); printf("y : "); scanf("%d",&y); if(x >= COUNT || x < 0 || y >= COUNT || y < 0) break; cells[x][y]=player; if(player == BLACK) player = WHITE; else player = BLACK; return 0; 小さいので 家に帰って wikiなどで 確認してください 第7章 配列

22 二次元配列とループ処理(5) 配列をマスターすれば オセロも作れる さっきのスライドは オセロっぽい何か未完成の物 だけどね 第7章 配列

23 二次元配列とループ処理(6) 小さいので 家に帰って wikiなどで 確認してください #include <stdio.h>
#define WIDTH 10 #define HEIGHT 20 bool cells[WIDTH][HEIGHT]; void show(){ printf(" \n"); for(int i = 0;i < HEIGHT;i++){ for(int j = 0;j < WIDTH;j++){ if(cells[j][i] ) printf("■"); else printf(" "); } printf("\n"); void drop(){ for(int i=0;i < WIDTH;i++){ if(cells[i][HEIGHT - 1]) return; for(int i = HEIGHT - 2;i >= 0;i--){ for(int j = 0;j < WIDTH;j++) cells[j][i+1]=cells[j][i]; for(int i = 0;i < WIDTH;i++) cells[i][0]=false; int main(void){ for(int i = 0;i < HEIGHT;i++){ for(int j = 0;j < WIDTH;j++) cells[j][i]=false; } cells[4][0]=true; cells[4][1]=true; cells[5][1]=true; cells[5][2]=true; while(true){ int num; show(); printf("[0:継続 それ以外:終了]"); scanf("%d",&num); if(num != 0) break; drop(); return 0; 小さいので 家に帰って wikiなどで 確認してください 第7章 配列

24 二次元配列とループ処理(7) なんか落ちてきたー!! 第7章 配列

25 実際に打ち込んで、実行して、理解してください
二次元配列とループ処理(8) このように、二次元配列とループ処理を組み合わせると すごく強力な武器になります 皆さんもぜひ、これらのプログラムは 実際に打ち込んで、実行して、理解してください あと、改造して、実際にゲームとして動くようにしてもいいかも 第7章 配列

26 静的配列の寿命 静的配列とは、今まで扱ってきた配列です。 これらの寿命は、変数と同じです。
よって以下のプログラムは正常にする保証はありません。 時間がたつと使えない 寿命を迎えてすぐなら 使えるかも #include <stdio.h> #include <windows.h> ・・・・ int main(void){ int* arr; init(&arr); Sleep(1000);//1秒待つ printf("arr[2]=%d\n", arr[2]); return 0; } #include <stdio.h> void init(int* arr[]){ int a[5]; a[2] = 3; *arr = a; } int main(void){ int* arr; init(&arr); printf("arr[2]=%d\n", arr[2]); return 0; このような運任せプログラムは 禁物です 第7章 配列

27 sizeof演算子 sizeof演算子は、 データ型、変数のメモリ上サイズを取得できる演算子 演算子 意味 優先順位 種類 sizeof
サイズ取得 3 単項 #include <stdio.h> int main(void){ printf("int型変数は%dバイトメモリを消費します\n",sizeof(int)); return 0; } 第3章でint型は4バイトと教えましたがあってますね。 sizeof演算子の後は、直接データ型を指定してもよいですし、 あるデータ型の変数を指定してもかまいません 第7章 配列

28 動的配列(1) malloc関数(stdlib.hで定義) 引数で指定したByte数分だけメモリを確保します
静的配列では要素数は必ず整数でなければならない。 要素数がどれぐらいになるかが分からないこともあるよね malloc関数を使えば、要素数を変数で宣言できる malloc関数(stdlib.hで定義)  引数で指定したByte数分だけメモリを確保します 引数 何Byte連続してメモリを確保するか 戻り値 確保したメモリの先頭のアドレス int型ポインタ変数に代入する際は 戻り値を(int*)にキャストする必要があります 第7章 配列

29 動的配列(2) ポインタ変数arrは 要素数4の配列 実はこのプログラムには問題あり!! 要注意 動的配列は寿命がない
#include <stdlib.h> int main(void){ int *arr; arr = (int*)malloc(sizeof(int) * 4); arr[0] = 5; return 0; } 実はこのプログラムには問題あり!! 要注意 動的配列は寿命がない つまり、メモリの確保をするとその領域が永遠に残ってしまう メモリ不足の原因 これを防ぐために、強制的に寿命を迎えさせる処理が必要になる 第7章 配列

30 動的配列(3) free関数(stdlib.hで定義) 引数で指定したアドレスのメモリを開放します 引数 解放するメモリの先頭のアドレス
強制的に寿命を迎えさせることを、メモリの解放と呼びます free関数でメモリの解放を行えます free関数(stdlib.hで定義)  引数で指定したアドレスのメモリを開放します 引数 解放するメモリの先頭のアドレス 戻り値 なし 第7章 配列

31 動的配列(4) 寿命がないので、 これなら正常動作が保証される メモリの解放は忘れやすいので 極力静的配列を使うべき
#include <stdio.h> #include <windows.h> void init(int* arr[]){ int *a; a = (int*)malloc(sizeof(int)*5); a[2] = 3; *arr = a; } int main(void){ int* arr; init(&arr); Sleep(1000);//1秒待つ printf("arr[2]=%d\n", arr[2]); free(arr);//使ったら絶対解放 return 0; 寿命がないので、 これなら正常動作が保証される メモリの解放は忘れやすいので 極力静的配列を使うべき 第7章 配列

32 動的配列(5) 配列の要素に、変数も指定可能です #include <stdio.h> int main(void){
int* scores; int count; printf("生徒は何人いますか?"); scanf("%d", &count); scores = (int*)malloc(sizeof(int) * count); for(int i = 0;i < count;i++){ printf("%d人目の点数 : ", i + 1); scanf("%d", &scores[i]); } printf("%d人目の点数は%dです\n", i + 1, scores[i]); free(scores); return 0; 第7章 配列

33 動的配列(6) 二次元配列の動的確保はどのようにやるのでしょう。 残念ながらこのようなことをする文法はありません。 メモリ a *a
二次元配列のこの表を用いれば作れます intポインタ型の配列をつくる そこに、int型配列を入れていく 第7章 配列

34 動的配列(7) メモリの確保、 解放だけで大変ですね この方法を用いると m×nではない二次元配列も 作成可能です
#include <stdlib.h> #define WIDTH 3 #define HEIGHT 5 int main(void){ int **arr; //メモリの確保 arr = (int**)malloc(sizeof(int) * WIDTH); for(int i = 0;i < WIDTH;i++){ arr[i] = (int*)malloc(sizeof(int) * HEIGHT); } //メモリの解放 free(arr[i]); free(arr); return 0; メモリの確保、 解放だけで大変ですね この方法を用いると m×nではない二次元配列も 作成可能です 第7章 配列

35 動的配列(8) こんな配列ができる メモリ #include <stdlib.h> #define WIDTH 3
int main(void){ int **arr; //メモリの確保 arr = (int**)malloc(sizeof(int) * WIDTH); for(int i = 0;i < WIDTH;i++){ arr[i] = (int**)malloc(sizeof(int) * (i + 1)); } //メモリの解放 free(arr[i]); free(arr); return 0; メモリ こんな配列ができる 第7章 配列

36 ここまでのまとめ 配列とは、連続して確保されたメモリの領域 配列の実体はポインタ 配列とループ処理は相性抜群
配列のコピーは一つ一つの要素を見るしかない 二次元配列はn×mの配列だと思えばいい 第7章 配列

37 練習問題 問1 問2 2×2の行列を2×2の二次元配列で表す。 このページの行列とは全て2×2の行列である。
2つの行列の積を計算する関数を作れ ユーザーにx,y,θ,scaleをdouble型で入力させ、 点(x,y)を原点周りにθ回転させた点を さらに、原点中心にscale倍に拡大した点(X,Y) を問1の関数を使って求めるプログラムを作れ 問2 第7章 配列

38 char型 char型は-128~127の範囲を扱える整数型でした。 このデータ型はなぜ存在するのでしょうか。
コンピュータでは文字も数字として扱います。 半角文字1文字文が、ちょうどchar型変数1つ分! 次のようなプログラムで、char型変数に値を代入できます #include <stdio.h> int main(void){ char a; a = 'A'; printf("%c\n",a); return 0; } シングルクォーテーション(')で 文字を囲む フォーマット指定子は%c 第7章 配列

39 char型配列(1) 注意 では、複数の文字を代入したい場合はどうしましょうか? もうわかりますね!配列です!! 文字の配列の最後には、
必ず'\0'(ヌル文字)を指定します 注意 また、char型配列のフォーマット指定子は%sです #include <stdio.h> int main(void){ char a[] = {'A', 'B', 'C', '\0'}; printf("%s\n",a); return 0; } 第7章 配列

40 配列の実体はポインタなのでscanfの第二引数に指定した場合、
char型配列(2) 配列の実体はポインタなのでscanfの第二引数に指定した場合、 アドレス演算子は不必要です #include <stdio.h> int main(void){ char name[100];//適当な分だけメモリ確保 printf("名前を入力してください "); scanf("%s", name); printf("あなたの名前は%sです\n" ,name); return 0; } 第7章 配列

41 今まで文字をダブルクォーテーションでくくってたのは
char型配列(3) char型配列{'A', 'B', 'C', '\0'}を "ABC"と書くことができます 最後の\0は自動挿入される 何かに気づきましたよね? 今まで文字をダブルクォーテーションでくくってたのは 全てchar型配列だったのです! #include <stdio.h> int main(void){ char name[100]; char text[] = "あなたの名前は"; printf("名前を入力してください "); scanf("%s", name); printf("%s", text); //フォーマット指定子を使ってもいいし printf(name); //使わなくてもchar型配列は表示できる printf("%s","です\n"); //こんな書き方もできる return 0; } 第7章 配列

42 夏休み前の講習はここまで! (の予定) 皆さんお疲れ様でした 楽しい夏休みを過ごしてください!! そして、夏休みはプログラミングに
最適な時期なので、 たくさんプログラミングしよう!! 次の講習は「第8章 構造体」です 第7章 配列


Download ppt "オセロを作るとします。 各マスの状態を保存するために64個の変数が必要です。 でも64個も変数を作るのはめんどくさいですよね。"

Similar presentations


Ads by Google