Download presentation
Presentation is loading. Please wait.
Published byῬαμά Γλυκύς Modified 約 6 年前
1
プログラミング入門2 第12回 構造体の配列 データ型 関数のプロトタイプ宣言 動的な記憶域確保 芝浦工業大学情報工学科
I’ll get started with Introduction and Conventional works around our study, then mention our motivation and goal. Next, I’ll explain our 3D Face Modeling Method and its medical application. Finally, I’ll conclude this presentation with some future works. 芝浦工業大学情報工学科 青木 義満、篠埜 功
2
先週の補足1 構造体は関数の引数として渡したり、関数の返り値として返したりすることができる。(配列は関数の引数として渡したり、関数の返り値として返すことはできない。) typedef struct {double re; double im;} complex; complex sum (complex num1, complex num2) { complex result; result.re = …; result.im = …; return result; } プログラミング入門2 2
3
先週の補足2 構造体のサイズは、単純に各メンバーのサイズの和ではない。 (例)
x (例) typedef struct {int x; char y; int z;} foo; int main () { printf (“%d\n”, sizeof (foo)); /* 12が表示される */ printf (“%d\n”, sizeof (int)); /* 4が表示される */ printf (“%d\n”, sizeof (char)); /* 1が表示される */ } y z アラインメント(alignment)があるため。 Alignmentとは、CPUのアドレス指定において、アドレスの下位数ビット(2bitなど)を使用しないようになっている場合(これが普通)、それに対応させて、ある一定数(4など)の倍数のアドレスに合わせてint型などのデータを格納をすること。(そうなるようにコンパイラが機械語コードを生成する。) プログラミング入門2 3
4
先週の補足2 先週の身体検査用の構造体のサイズは32byteだったが、メンバーnameの配列の要素数を19にしても、構造体のサイズは32byteのままで変わらない。 taro.name (例) struct { char name[20]; int height; double weight; } taro; 20byte taro.height 4byte taro.weight 8byte 宣言する変数名 プログラミング入門2 4
5
複数のデータの扱い (構造体の配列, p.282) 構造体型の配列の宣言 構造体データを複数扱う際には,構造体の配列を用いる。
std[0].name std[0].height std[0] typedef struct { char name[20]; int height; float weight; } student; std[0].weight std[1].name std[1] std[1].height student std[5]; std[1].weight …. プログラミング入門2
6
構造体の配列 ソースファイル名:list1208.c (p.283を変更) 構造体の配列 プログラミング入門2
#include <stdio.h> #define NUMBER /* 学生の人数 */ typedef struct { char name[20]; /* 名前 */ int height; /* 身長 */ float weight; /* 体重 */ } student; int main(void) { int i; student std[]= { { "Sato", 178, 61.0 }, { "Sanaka", 175, 60.5 }, { "Takao", 173, 80.0 }, { "Mike", 165, 72.0 }, { "Masaki", 179, 77.5 }, }; puts(" "); for (i = 0; i < NUMBER; i++) printf("%-8s %6d%6.1f\n", std[i].name, std[i].height, std[i].weight); return (0); } プログラミング入門2
7
構造体の配列を関数へ渡す方法 = ソースファイル名: struct4.c 配列データを関数に渡す時には,
#include <stdio.h> #define NUMBER /* 学生の人数 */ typedef struct { char name[20]; /* 名前 */ int height; /* 身長 */ float weight; /* 体重 */ } student; void print_data( student data[] ) { int i; puts(" "); for (i = 0; i < NUMBER; i++) printf("%-8s %6d%6.1f\n",data[i].name, data[i].height, data[i].weight); } in main(void) student std[] = { { "Sato", 178, 61.0 }, { "Sanaka", 175, 60.5 }, { "Takao", 173, 80.0 }, { "Mike", 165, 72.0 }, { "Masaki", 179, 77.5 }, }; print_data( std ); return (0); void print_data( student data[ ] ) = student *data どちらも配列の先頭要素のアドレスを受け取る。 受け取ると,関数内で data[i]として 配列要素を扱える 配列データを関数に渡す時には, その配列の先頭要素のアドレスを渡す。 stdと書いてもよいし、&std[0] と書いてもよい。 プログラミング入門2
8
データ型 データ型 整数型,文字型,浮動小数点数,倍精度型 C言語で扱うことのできるデータ型 データ型 意味 整数型 整数を表現する 文字型
文字を表現する 浮動小数点数 実数を表現する 倍精度型 精度の高い浮動小数点数を表現する C言語で扱うことのできるデータ型 プログラミング入門2
9
各データが扱える数値範囲 変数を用意する際,目的にあった(扱うデータの値の取りうる範囲,必要とされる精度)データ型を以下から選択して使用
ビット長 扱える数値の範囲 short 16 -32768 ~ +32767 int 32 ~ long unsigned short 0 ~ 65535 unsigned int 0 ~ unsigned long char 8 (-128 ~ 127) unsigned char (0 ~ 255) float 3.4 x ~ 3.4 x 10+38 double 64 1.7 x ~1.7 x ※実際のデータサイズは,処理系によって異なる(特にint) プログラミング入門2
10
各データ型の変換指定子 ※scanfの場合,float は%f, double は %lf で読み込み 標準入出力のための変換指定子 指定子
意味 %d(%ld) 整数の10進数として出力 (longの場合,%ld) %u (%lu) 整数の符号なし10進数として出力 (unsigned longの場合,%lu) %f 浮動小数点表示(float, double共通) %c 1文字を出力 %s 文字列を出力 %p ポインタの値(アドレス)を出力 ※scanfの場合,float は%f, double は %lf で読み込み プログラミング入門2
11
データ型の値の範囲を出力 ソースファイル名: data.c 各データ型の値の取りうる範囲を出力
#include <stdio.h> #include <limits.h> int main(void) { printf("char : %d to %d\n", CHAR_MIN, CHAR_MAX); printf("unsigned char : %d to %d\n", 0, UCHAR_MAX); printf("short : %d to %d\n", SHRT_MIN, SHRT_MAX); printf("int : %d to %d\n", INT_MIN, INT_MAX); printf("long : %ld to %ld\n", LONG_MIN, LONG_MAX); printf("unsigned short : %u to %u\n", 0, USHRT_MAX); printf("unsigned int : %u to %u\n", 0, UINT_MAX); printf("unsigned long int: %lu to %lu\n", 0, ULONG_MAX); return(0); } 各データ型の値の範囲が定義(#define)されている プログラミング入門2
12
データ型のサイズ(バイト数)を表示 ソースファイル名: datasize.c 各データ型の大きさ(サイズ)を表示
#include <stdio.h> int main(void) { printf("sizeof(char) = %u\n", sizeof(char) ); printf("sizeof(short) = %u\n", sizeof(short) ); printf("sizeof(int) = %u\n", sizeof(int) ); printf("sizeof(long) = %u\n", sizeof(long) ); printf("sizeof(float) = %u\n", sizeof(float) ); printf("sizeof(double) = %u\n", sizeof(double) ); return(0); } sizeof( データ型 ) → データ型の大きさ(byte数) ※p.180に詳しい解説 プログラミング入門2
13
関数のプロトタイプ宣言 プログラムの冒頭に,使用する関数の仕様を先に宣言しておく → 関数のプロトタイプ宣言
プログラムの冒頭に,使用する関数の仕様を先に宣言しておく → 関数のプロトタイプ宣言 #include <stdio.h> int main(void) { int x, y, z; x = 5; y = 10; z = func( x, y ); printf( "x+y = %d\n", z); return(0); } int func(int x, int y) return (x+y) ; $ gcc –W –Wall test.c のように、オプションをつけてコンパイルすると 警告がでる。 プログラミング入門2
14
関数のプロトタイプ宣言 関数のプロトタイプ宣言 (書く習慣をつけておいた方が良い) int func(int x, int y);
#include <stdio.h> int func(int x, int y); int main(void) { int x, y, z; x = 5; y = 10; z = func( x, y ); printf( "x+y = %d\n", z); return(0); } int func(int x, int y) return (x+y) ; 型をプログラムの冒頭に記述 (どんな引数を何個受け取り,どんな値を返すのか) 関数のプロトタイプ宣言 (書く習慣をつけておいた方が良い) プログラミング入門2
15
動的記憶域確保の必要性 動的な記憶域確保の必要性! これまでのプログラム 問題に応じて、適切なサイズの配列(記憶域)を確保する方法は?
配列の要素数は固定 あらかじめ大きめの配列を確保しておく #defineマクロで、要素数を定数として宣言し、その値を変更することで対応 → 静的な配列(記憶域)の確保 問題に応じて、適切なサイズの配列(記憶域)を確保する方法は? メモリの節約 プログラムの柔軟性向上 動的な記憶域確保の必要性! プログラミング入門2
16
calloc関数 : 記憶域の確保 必要になったら記憶域を動的(ダイナミック)に確保し、不要になったら解放する機能を提供
ヘッダ #include <stdlib.h> 形式 void *calloc(size_t n, size_t size ); 解説 大きさがsizeであるn個のオブジェクト(記憶域)の領域を確保する。 返却値 領域確保に成功した場合は、その領域の先頭へのポインタを返し、失敗した場合は、空ポインタ(NULL)を返す。 プログラミング入門2
17
Callc関数による記憶域の確保(1) ソースファイル名:calloc1.c 整数1個分の記憶域を動的に確保
#include <stdio.h> #include <stdlib.h> int main(void) { int *p; p = (int *)calloc( 1, sizeof(int) ); /*整数を1個分動的に確保*/ if( p == NULL ) puts(“記憶域の確保に失敗しました"); else { *p = 15; printf("*p = %d\n", *p ); } return(0); stdlib.hのインクルードを忘れずに! プログラミング入門2
18
Calloc関数の動作イメージ p = (int *)calloc( 1, sizeof(int) ); Intのデータ1個を動的に確保
500番地 sizeof( int ) p p Intのデータ1個を動的に確保 プログラミング入門2
19
void へのポインタ (void *)calloc(size_t n, size_t size );
p = (int *)calloc( 1, sizeof(int) ); Calloc関数の返却値 → void * 型 (voidへのポインタ型) 動的確保の対象: int, char, double, 構造体 など、様々なオブジェクト ・ 特定の型へのポインタを返す使用 → × ・ 融通の利く万能なポインタ void * → ○ プログラムでは、確保する変数の型に合わせて、(void *)型のポインタを 任意のポインタ型にキャスト ※上の例では、int * 型 プログラミング入門2
20
free関数 : 記憶域の解放 動的に確保した記憶域は、不要になった時点で必ず解放 #include <stdlib.h>
ヘッダ #include <stdlib.h> 形式 void free( void *p ); 解説 Pが指す領域を開放する。但しpがNULLであれば何も行わない。 プログラミング入門2
21
記憶域の解放 ソースファイル:calloc2.c 記憶域の解放 free(p); 記憶域解放 記憶域確保 プログラミング入門2
#include <stdio.h> #include <stdlib.h> int main(void) { int *p; p = (int *)calloc( 1, sizeof(int) ); /*整数を1個分動的に確保*/ if( p == NULL ) puts(“記憶域の確保に失敗しました"); else { *p = 15; printf("*p = %d\n", *p ); free(p); /* 確保していた領域を解放 */ } return(0); free(p); p = (int *)calloc( 1, sizeof(int) ); 記憶域確保 プログラミング入門2
22
確保した領域への値の書き込み ソースファイル名:calloc3.c 記憶域を1個動的に確保し、キーボードから値を読込み
#include <stdio.h> #include <stdlib.h> int main(void) { int *p; p = (int *)calloc( 1, sizeof(int) ); /*整数を1個分動的に確保*/ if( p == NULL ) puts(“記憶域の確保に失敗しました"); else { printf(“整数を入力して下さい:”); scanf(“%d”, p); printf("*p = %d\n", *p ); } return(0); プログラミング入門2
23
1次元配列の動的確保 配列宣言の例 実行時に、扱うデータのサイズによって最適な配列を用意することが可能 int x[10];
実行時に、扱うデータのサイズによって最適な配列を用意することが可能 配列の要素数は定数式でなければならない → 要素数を変数とすることは不可能 → 開発時に要素数を決定する必要 ※もしくは、#defineで宣言した定数 配列の動的確保 → 実行時に要素数を決定! プログラミング入門2
24
1次元配列の確保 ソースファイル名:calloc4.c int型の配列を動的に確保 p[0] p[1] p[2] p[3] p[4] p
#include <stdio.h> #include <stdlib.h> int main(void) { int no; /* 配列の要素数 */ int i; int *p; printf(“確保する配列の要素数:”); scanf(“%d”, &no); p = (int *)calloc( no, sizeof(int) ); /*整数を1個分動的に確保*/ if( p == NULL ) puts(“記憶域の確保に失敗しました"); else { for(i=0; i < no; i++) p[i] = i; for(i=0; i< no; i++) printf(“p[%d] = %d¥n”, i, p[i] ); free(p); } return(0); p[0] p[1] p[2] sizeof(int) * 5 p[3] p[4] p あたかも int p[5]; と宣言された配列が存在する かのように処理できる! プログラミング入門2
25
realloc : 確保した領域の大きさの変更
ヘッダ #include <stdlib.h> 形式 void *realloc( void *ptr, size_t size ); *ptr : 大きさを変更したい領域へのポインタ size: 新しい大きさ 解説 ptrが指す記憶域の大きさをsizeバイトに変更する。変更前後の小さい方までのオブジェクトの内容は変わらない。 プログラミング入門2
26
記憶域の大きさを変更 ソースファイル名:calloc5.c 確保した記憶域の大きさを途中で変更
printf("Before----\n"); for(i=0;i<no;i++) printf("p[%d] = %d\n", i, p[i]); printf("Input new array size : "); scanf("%d", &no2); temp = (int *)realloc(p, no2 * sizeof(int) ); if(temp==NULL) puts("Cannot allocate memory.\n"); else{ p = temp; for(i=no; i < no2; i++) p[i] = i; printf("After----\n"); for(i=0;i<no2;i++) } free(p); return(0); #include <stdio.h> #include <stdlib.h> int main(void) { int no, no2; int i; int *p; int *temp; printf("Input size: "); scanf("%d", &no); p = (int *)calloc(no, sizeof(int)); if(p==NULL) puts("Cannot allocate memory.\n"); else { for(i=0;i<no;i++) p[i] = i; 領域サイズの変更 1回目の領域確保 領域の解放 プログラミング入門2
27
reallocの動作イメージ ←0 ←1 ←2 ←3 ←4 ←5 ←6 ←7 p[0] p[0] p[1] p[2] p[1] p[2]
要素数はno 新たに確保 した領域 p[6] p[7] ※realloc関数で確保済みの領域の大きさを変更する際には、 realloc関数の返却値が、空ポインタでないことを確認! 要素数はn2 プログラミング入門2
28
二次元配列の動的確保(1) ソースファイル名:calloc6.c height行3列の2次元配列を確保(行数固定)
#include <stdio.h> #include <stdlib.h> int main(void) { int height; int (*p)[3]; int i, j; printf("Gyou :"); scanf("%d", &height); p = (int (*)[3])calloc(height * 3, sizeof(int)); if(p==NULL) puts("Cannot allocate memory.\n"); else{ for(i=0;i<height;i++) for(j=0;j<3;j++) p[i][j]=0; printf("p[%d][%d] = %d\n", i, j, p[i][j]); free(p); } return(0); 要素型がintで要素数が3の配列へのポインタ プログラミング入門2
29
2次元配列の動的確保 イメージ p[0] p[1] p[2] p[3] p[0][0] p[0][1] p[0][2] p[1][0]
2次元配列の動的確保 イメージ p[0][0] p[0][1] p[0] p[0][2] p[1][0] p[1][1] p[1] p[1][2] 4(height) * 3 * sizeof(int) p[2][0] p[2][1] p[2] p[2][2] p[3][0] p[3] p[3][1] p[3][2] ※n次元配列を動的に確保する際は、最高位のn次元の 要素数のみが可変。それ以外の次元の要素数は定数でなければならない プログラミング入門2
30
二次元配列の動的確保(2) 上級者向け ソースファイル名:calloc7.c
二次元配列の動的確保(2) 上級者向け ソースファイル名:calloc7.c ダブルポインタを用いた2次元配列の動的確保(をしたように見える)(行数列数共に可変)(2次元配列とは違い、heightの個数の領域に分かれている。) #include <stdio.h> #include <stdlib.h> int main(void) { int height, width; int i,j; int **p; printf("Gyou: "); scanf("%d", &height); printf("Retsu: "); scanf("%d", &width); p= (int **)calloc(height, sizeof(int *)); if(p==NULL) printf("Cannot allocate memory.\n"); else{ for(i=0;i<height;i++) p[i]=NULL; for(i=0;i<height;i++){ p[i]=(int *)calloc(width, sizeof(int)); if(p[i]==NULL){ puts("Cannot allocate memory.\n"); goto Free; } for(i=0;i<height;i++) for(j=0;j<width;j++) p[i][j]=0; printf("p[%d][%d]=%d\n", i, j, p[i][j]); Free: free(p[i]); free(p); } return(0); プログラミング入門2
31
構造体配列の動的確保のやり方 (0) point構造体を定義 (1) point構造体へのポインタ型の変数pを宣言しておく。
(2) Nにscanfで配列の要素数を入れる。 (3) p = (point *) calloc (N, sizeof (point)); で必要な領域を確保し、その先頭アドレスをpに代入 (4) pを使って、確保した領域内の各要素にアクセス。 p[0], p[1] などが領域内の各構造体を表す(とプログラマが決める)。(*p, *(p + 1), 等でも同じ) p[0].x, p[0].y, p[1].x, …などが、領域の中に確保された各構造体のメンバーを表すことになる。 p -> x, p -> y, (p+1)-> x 等、アローを使った表記でもよい。 typedef struct { double x; double y; } point; プログラミング入門2
32
今日の課題1 構造体配列の動的確保(kadai12-1.c) 以下のようなプログラムを作成せよ。
以下に示すように、2つのdouble型を格納する構造体pointを定義する。この構造体は1つの点の座標を表すために用いる。 キーボードから点の数Nを入力し、point構造体をN個格納できる連続した領域を確保する。(構造体の配列とみなせる。) 領域確保後、全ての座標値を0.0で初期化し、その結果を表示する。 最後に、確保した領域を解放する。 typedef struct { double x; double y; } point; プログラミング入門2
33
今日の課題2 2次元配列の動的確保(kadai12-2.c) N行3列の行列の足し算(要素はint型)を行うプログラムの作成
3つの行列(の領域へのポインタ)m1, m2, m3および行数Nを引数として受け取り、行列m1, m2の和をm3に格納する関数sumを定義。 実行時にNをキーボードから入力する。 N行3列の行列の(3N個の)要素を格納するための連続した領域を動的に3つ確保する。 領域確保後、そのうちの2つの領域(行列)に適当に初期値を格納し、sum関数を呼び出すことにより3つ目の領域にそれらの和を格納する。 sum関数を呼び出したあとで結果を表示する(表示形式自由)。 行列の要素の値の与え方は自由とする。また、行列の要素をどのような順番で確保した領域に格納するかも自由とする。 プログラミング入門2
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.