• 検索結果がありません。

第3回 配列とリスト

N/A
N/A
Protected

Academic year: 2021

シェア "第3回 配列とリスト"

Copied!
33
0
0

読み込み中.... (全文を見る)

全文

(1)
(2)

この回の要点

• C言語における変数

– プリミティブ型とポインタの違い – 参照型における実体オブジェクトへの参照

• リストとは?

• 配列によるリスト

– 配列の利点と欠点 – C言語による配列の実現 – 配列の代入と複製の違い

(3)

データ構造

• アルゴリズム+データ構造=プログラム

– アルゴリズム・・・データをどのように加工するか

– データ構造・・・データをどのように表現するか

• アルゴリズムと、データ構造とは、互いに密接に関

係している

– プログラムを書くとき、アルゴリズムとデータ構造とを同時

に考えていく必要がある

– 並べ替えアルゴリズムの場合

• データの移動がほとんどないアルゴリズム・・・配列による表現 • データの削除・挿入が頻繁にあるアルゴリズム・・・リンクリストに よる表現

– どちらでも可能であるが、効率が問題

(4)

変数

• 変数(variables)とは

– プログラム中で、値が変化するデータを格納する箱 – 1つの箱に、1つのデータを格納できる – 箱にはいろいろな大きさがある(型、Type)

• 変数には名前をつける=

変数名

(識別子)

123 3.141592 “Hello World” (100,120) PersonalData x = pi = str = pt = p = 箱 箱にはいろいろな データが入っている 変数名

(5)

C言語の基本型(プリミティブ)

種類 型 長 signed unsigned 型なし void ー ー ー 文字型 char 8 -128 ~ 127 0 ~ 255 整数型 short 16 -32768 ~ 32767 0 ~ 65535 int 32 -2147483648 ~ 2147483647 0 ~ 4294967295 long 32 -2147483648 ~ 2147483647 0 ~ 4294967295 long long 64 -9223372036854775808 ~ 9223372036854775807 0 ~ 18446744073709551615 浮動小 数点型 float 32 最小の正の数1.175494351e-38 最大値3.402823466e+38 double 64 最小の正の数2.2250738585072014e-308 最大値1.7976931348623158e+308 ILP32(一般的な32ビット環境)での例

(6)

ポインタ型

• メモリへの参照を示す • 参照するメモリのアドレスが格納されている • 変数名の前に*(アスタリスク)を付けた型 – int *ip; – double *dp; (*は変数名ではない) 4バイト 8バイト 参照 参照 アドレス幅 メモリ メモリ 123 3.14 ip dp

(7)

プリミティブとポインタ

123 3.141592 123 “Hello World” 構造体 int x = double pi = int *ip = char *str = MyData *dp = 参照 実体への参照 実体 (オブジェクト) 実体(値) 参照 参照 プリミティブ ポインタ

(8)

変数への代入

• プリミティブ

int x=123; double pi=3.141592; MyPoint pt={3,4};

• ポインタ

int *ip=&x; double *dp=π

const char *str=“Hello World”;

int *data=(int*)malloc(100*sizeof(int)); MyPoint *pt1=(MyPoint*)malloc(sizeof(MyPoint)); プリミティブには直接数値を代入 プリミティブのアドレスを代入 malloc() を使用 free()でメモリを解放すること! 構造体の初期化

(9)

構造体

• いくつかのデータをまとめた型

– キーワード

struct

で定義する

– タグ名

を付ける

– 内部に複数の異なる型の変数を持つ

– 構造体の宣言の形

struct タグ名 { データ型 メンバー名 データ型 メンバー名 ・・・ };

– 変数の宣言

struct タグ名 変数名;

– メンバーへのアクセス(.か->を使用)

変数.メンバー名 ポインタ変数->メンバー名

(10)

構造体の例1

#include <stdio.h> struct MyPoint { int x; int y; }; int main(void){ struct MyPoint pt1; struct MyPoint pt2={ 3,4 };

struct MyPoint *pt3=&pt2;

printf(“(%d,%d)¥n”,pt1.x,pt1.y); printf(“(%d,%d)¥n”,pt2.x,pt2.y); printf(“(%d,%d)¥n”,pt3->x,pt3->y); } 構造体タグの宣言 構造体タグを使って 変数を宣言 初期化

(11)

構造体の例2

#include <stdio.h> typedef struct { int x; int y; } MyPoint; int main(void){ MyPoint pt1; MyPoint pt2={ 3,4 }; MyPoint *pt3=&pt2; printf("(%d,%d)¥n",pt1.x,pt1.y); printf("(%d,%d)¥n",pt2.x,pt2.y); printf("(%d,%d)¥n",pt3->x,pt3->y); } 構造体型の定義 構造体型を使って 変数を宣言 typedefを使った場合 タグ名は省略

(12)

抽象データ型とリスト(list)

• 抽象データ型とは

– データが持つ性質+データに行える操作

– 「クラス」として表現される

– 中にどんなデータが入るかは関知しない

• リストとは

– 一般には、一覧表、目録、名簿

– 要素を順番に並べた構造を表す総称

• データを一列に並べたもの→線形リスト • リスト x の k 番目の要素→ x[k] とすると、 • それの1つ前→ x[k-1] • それの1つ後→ x[k+1] • 先頭の要素→ x[1] • 末尾の要素→ x[n] (n個の場合) 番号によって 要素にアクセス可能 (ただし、C言語の配 列の添字は0から始 まるので注意)

(13)

抽象データ型としてのリスト

• リストのメンバ変数

– そのリストに格納されているデータ

• リストが持つべきメソッド

– k番目の要素の内容を読む・書く – k番目の要素の前に要素を挿入する – k番目の要素の後に要素を挿入する – k番目の要素を削除する – 特定のキーを持つ要素を取り出す – 複数のリストを1つにまとめる – 1つのリストを複数のリストに分割する – リストを複製する – リストの要素数を得る – ・・・

• すべのメソッドを効率よく実現するリストは困難

– 必要に応じて、どのメソッドを重視するかで、データ構造が決まる

(14)

配列(array)

• リストの実現の一種

• たくさんの箱を一列に並べた構造

– 箱の数(配列の大きさ)は固定

• 配列を作るときに決める • あとから変更はできない

– 箱には番号(添え字)がつけられる

• 0,1,2,・・・,N-1(JavaやCの場合)

– 番号を使って、中のデータにアクセス可能

• たくさんのデータを1つの名前(配列名)で管理できる • 多くのデータを扱うプログラムで有効

• 多次元配列

– 配列の添え字が2つ以上

0 1 2 3 N-1

(15)

配列の利点と欠点

• 利点

– 箱の番号を利用してデータに直接アクセスできる=

O(1)

– データだけから構成され、無駄なメモリが不要

• 欠点

– サイズが固定

– 途中に要素を挿入、削除する際、前後のデータを移動さ

せる必要がある=

O(N)

0 1 2 3 ・・・ N-1 挿入 後ろにずらす 削除 前にずらす

(16)

C言語の配列1

• C言語の配列は連続するメモリ領域(=ポインタ)

• 変数名に[ ]をつけて宣言する

int a[10]; // 10個の箱を持つ配列変数a int b[5]={1,2,3,4,5}; // 初期化 int c[]={1,2,3}; // 初期化すればサイズは不要

• 配列要素へのアクセス

a[0]=3; a[9]=123;

• 2次元配列

int x[3][4]; // 3行4列の2次元行列 int y[][2]={{1,2},{3,4},{5,6}}; // 3行2列 (実際には2次元配列も1次元として確保されている)

• 注意

– C言語の配列のインデックスはチェックされない – 初期化しなければ、何が入ってるかは不定

(17)

C言語の配列2

• ポインタとしての配列

int *a=(int*)malloc(10*sizeof(int)); // 10個の配列 MyPoint *pt=(MyPoint*)malloc(5*sizeof(MyPoint)); // 5個のMyPointの配列

• 配列へのアクセス

– 前ページの配列同様、[]でアクセスできる ? ? ? ? ? a[0] a[1] a[2] a[3] a[9] MyPoint pt[0] 参照 a pt 参照 配列 変数 配列 変数 MyPoint pt[1] MyPoint pt[2] MyPoint pt[3] MyPoint pt[4]

(18)

ポインタの配列

• MyPointクラスの

ポインタの配列

MyPoint **pt=(MyPoint**)malloc(5*sizeof(MyPoint*)); pt[1]=(MyPoint*)malloc(sizeof(MyPoint)) // 実体 pt[3]=(MyPoint*)malloc(sizeof(MyPoint)); pt[4]=(MyPoint*)malloc(sizeof(MyPoint)); 参照 pt ? 参照 ? 参照 参照 pt[0] pt[1] pt[2] pt[3] pt[4] (x,y) (x,y) (x,y) 実体 (ヒープメモリ) 配列自体 の参照 MyPoint 型の参照 2段階の参照

(19)

別なメモリ

配列の代入と複製

• 配列の

代入

– 参照だけが代入され、その先は同じもの

• 配列の

複製(コピー)

– 配列の先は別々なオブジェクトとなる 参照 int a[5] 5 14 3 8 4 a[0] a[1] a[2] a[3] a[4] 参照 int *b 5 14 3 8 4 c[0] c[1] c[2] c[3] c[4]

b=a

memcpy(c,a,sizeof(a)); 参照 int c[5]

(20)

代入と複製の違い

• 代入と複製の違いを確認する

– 配列 a を { 5,14,3,8,4 } で初期化 – a を配列 b に代入 – a を配列 c に複製 – a と b , c を表示 – a の2番目 a[1] の 14 を 99 に変更 – a と b , c を表示

• 結果

– 代入・・・b[1] は 99 に変わる – 複製・・・c[1] は 14 のまま

(21)

ArrayTest.c

1. #include <stdio.h> 2. #include <stdlib.h> 3. #include <memory.h> 4.

5. void printArray(char c,int *a,int s){ 6. printf("%c: ",c); 7. int i; 8. for(i=0;i<s;i++) 9. printf("%d ",a[i]); 10. printf("¥n"); 11. } 12. 13. int main(void){ 14. int a[5]={5,14,3,8,4}; 15. int *b=a; 16. int c[5]; 17. memcpy(c,a,sizeof(a)); 18. a[1]=99; 19. printArray('a',a,5); 20. printArray('b',b,5); 21. printArray('c',c,5); 22. }

$ gcc –o ArrayTest ArrayTest.c $ ./ArrayTest.exe a: 5 99 3 8 4 b: 5 99 3 8 4 c: 5 14 3 8 4 $ 実行結果:

(22)

プログラムの作成方法

• 実際のプログラム

– ヘッダファイルと実装ファイルに分けて作る

• ヘッダファイル(.h)

– データ型(クラス)の宣言

– データ操作(関数)のプロトタイプ宣言

• 実装ファイル(.cc)

– 関数の具体的なコード

– 拡張子.ccで書くことで、コンパイラがソースコードをC++として

扱うようになる

– C++言語はC言語の拡張

– この講義ではC言語の機能だけを用いるが、C++として扱って

おくと後々便利(かもしれない)

– ということで、以下ではコンパイラとして

g++

を用いる

(23)

ヘッダファイル

• ソースファイルを分割する手段

• 拡張子 .h

• #include文で読み込む

– プリプロセッサによる作業

– 標準ヘッダ <stdio.h>

– 自作ヘッダ “hogehoge.h”

• 目的

– 複数のソースで共通して使う場合

– 定義を書く場合

– プロトタイプ宣言

– 関数の実体を書いてはいけない

• リンクエラーになる場合がある

.cc

.h

.h

.h

.o

コンパイル #include

(24)

コンパイルとリンク

• コンパイル

– .hと.ccから.oを作る

– 共通で使える.oを使う場合

– 規模が大きい場合

• リンク

– .oから.exeを作る

• コンパイル&リンク

– .hと.ccから直接.exeを作る

– 規模が小さい場合はこれでもよい

.o

.o

.o

.exe

リンク

$ g++ –o test1 test1.cc $ ./test1

(25)

成績型Seiseki

• 小学生の1回の試験の成績

– 名前と国語、算数、理科、社会の点数

– 成績の生成と表示ができる

メンバー

const char* name 名前

int kokugo 国語の点数

int sansuu 算数の点数

int rika 理科の点数

int shakai 社会の点数

関数

Seiseki* makeSeiseki(const char*,int,int,int,int) 成績の生成 void print(Seiseki*) 成績の表示 void free(Seiseki*) 成績の解放

(26)

Seiseki.h

1. #ifndef __Seiseki__h 2. #define __Seiseki__h 3. #include <stdio.h> 4. #include <stdlib.h> 5. 6. // データ型宣言 7. typedef struct { 8. const char *name; 9. int kokugo; 10. int sansuu; 11. int rika; 12. int shakai; 13. } Seiseki; 14. 15. // プロトタイプ宣言

16. Seiseki* makeSeiseki(const char*,int,int,int,int); 17. void print(Seiseki*);

18. void free(Seiseki*); 19.

20. #endif // __Seiseki__h

(27)

Seiseki.cc

1. #include "Seiseki.h"

2.

3. // 成績の生成(コンストラクタ)

4. Seiseki* makeSeiseki(const char *n,int k,int m,int r,int h){ 5. Seiseki *s=(Seiseki*)malloc(sizeof(Seiseki)); 6. s->name=n; 7. s->kokugo=k; 8. s->sansuu=m; 9. s->rika=r; 10. s->shakai=h; 11. return s; 12. } 13. 14. // 成績の表示 15. void print(Seiseki *s){ 16. printf(“%s 国%d 算%d 理%d 社%d¥n", 17. s->name,s->kokugo,s->sansuu,s->rika,s->shakai); 18. } 19. 20. // 成績の解放 21. void free(Seiseki *s){ 22. free((void*)s); 23. } ヘッダファイルの読み込み Seiseki型のメモリを確保し、 中にデータを入れて、返す

(28)

TestSeiseki.cc

1. #include "Seiseki.h" 2. 3. int main(){ 4. Seiseki *s[3]; 5. s[0]=makeSeiseki("山田太郎",78,55,80,88); 6. s[1]=makeSeiseki("佐藤花子",90,80,85,87); 7. s[2]=makeSeiseki("中村裕次郎",40,62,72,21); 8. 9. for(int i=0;i<3;i++) 10. print(s[i]); 11. 12. for(int i=0;i<3;i++) 13. free(s[i]); 14. } s s[0] s[1] s[2] “山田太郎” 78 55 80 88 “佐藤花子” 90 80 85 87 “中村裕次郎” 40 62 72 21

(29)

データ構造の形

• XXXという型を作る場合:

• XXX.h

– 型宣言

– プロトタイプ宣言

• XXX.cc

– 関数の実装

• AAA.ccで使う場合

– g++ -o AAA AAA.cc XXX.cc

(30)

データ構造の形

1. #ifndef __XXX__h 2. #define __XXX__h 3. #include <stdio.h> 4. #include <stdlib.h> 5. 6. // データ型宣言 7. typedef struct { 8. int xx,yy; 9. } XXX; 10. 11. // プロトタイプ宣言

12. XXX* makeXXX(int x,int y); 13. void print(XXX*); 14. void free(XXX*); 15. 16. #endif // __XXX__h 1. #include “XXX.h" 2. 3. // XXXの生成(コンストラクタ)

4. XXX* makeXXX(int x,int y){

5. XXX *s=(XXX*)malloc(sizeof(XXX)); 6. // ... 7. return s; 8. } 9. 10. // XXXの表示 11. void print(XXX *s){ 12. // ... 13. } 14. 15. // XXXの解放 16. void free(XXX *s){ 17. free((void*)s); 18. } “XXX.h” “XXX.cc”

(31)

課題181015

• TestSeiseki.ccをコンパイルして実行し、実行結果を示せ。 • 次のページのTestSeiseki2.ccは、ポインタ配列の代わりに二重ポインタ をつかって、同じプログラムを書きなおしたものである。このコードの不足 分を埋めて完成させ、完成したコードと実行結果を示せ。 • 作成方法:ワードで作成し、実行結果は図として貼り付けること。 • ファイル名は”scXXXXXX-al181015.docx” • 提出方法:メールで渕田まで送付すること。 • メールの本文中にも学籍番号を氏名を明記すること。 • 提出先:fuchida@ibe.kagoshima-u.ac.jp • メールタイトル:”アルゴリズム課題181015” ← 厳守! • 期限:2018年10月21日(日) 24:00

(32)

TestSeiseki2.cc

1. #include "Seiseki.h" 2. 3. int main(){ 4. Seiseki **s; 5. /* ここにs用のメモリを確保するコードを追加する */ 6. s[0]=makeSeiseki("山田太郎",78,55,80,88); 7. s[1]=makeSeiseki("佐藤花子",90,80,85,87); 8. s[2]=makeSeiseki("中村裕次郎",40,62,72,21); 9. 10. for(int i=0;i<3;i++) 11. printSeiseki(s[i]); 12. 13. for(int i=0;i<3;i++) 14. freeSeiseki(s[i]); 15. /* ここにs用のメモリを開放するコードを追加する */ 16. } ここを変えた

(33)

リストと配列

終了

参照

関連したドキュメント

などに名を残す数学者であるが、「ガロア理論 (Galois theory)」の教科書を

Inter-universal Teichmuller Theory IV: Log-volume Computations and Set-theoretic

Intervals graphs (denoted by INT ) are intersection graphs of intervals on a line, circular-arc graphs (CA ) are intersection graphs of intervals (arcs) on a circle, circle graphs (CI

What relates to Offline Turing Machines in the same way that functional programming languages relate to Turing Machines?.. Int Construction.. Understand the transition from

[3] Chari, Vyjayanthi, On the fermionic formula and the Kirillov-Reshetikhin conjecture, Int. and Yamada, Y., Remarks on fermionic formula, Contemp. and Tsuboi, Z., Paths, crystals

Tsouli, Infinitely many solutions for nonlocal elliptic p-Kirchhoff type equation under Neumann boundary condition, Int. Journal

平成3

Q7 建設工事の場合は、都内の各工事現場の実績をまとめて 1