Transcript Document

C言語
第12講
生物機能制御学講座 濱田
農学部7号館209室
1
本日の講義内容
• おさらい
関数など
テキスト Lesson 8
• 関数ほか
テキスト Lesson 8~
• プログラムの作成
• アンケート
2
アンケート結果
質問および要望
1.「ローカル変数」と「グローバル変数」の違い
が良くわからない
2.「戻り値」の意味が良くわからない
3.「引数」の意味が良くわからない
4.「Static」の意味が良くわからない
3.第12講の関数の内容について
わかった(特に問題ない) 16名
むずかしい
10名
3
関数
プログラムが、以下の構成の場合!
1) 同じ内容のブロックを何度も
記述しないといけない
2) 1つのブロックが冗長
すっきりしたプログラムを作成したいですね!
すっきりしたプログラムは、デバッグ効率を
格段に向上させ、メンテナンスも楽になります!
main()
{
Block_A
・・・・
Block_A
Block_A
・・・・
Block_A
}
1)や2)のブロックをmain()以外の箇所に記載(関数)し、main()
の実行したい当該箇所とリンクする! コーディングがすっきりします
4
関数のしくみ
main()
{
A1; ・・・
・・・・
A1; ・・・
・・・・
A1; ・・・
}
F_A()
詳述
処理の呼び出し
{
A1; ・・・ AN;
}
AN;
AN;
AN;
restructure
何度も A1; ・・・ AN;がある
(A1;・・・AN;はBlock!)
F_A() : 関数!
実は、main()も関数!
main()
{
粗筋
F_A();
・・・・
F_A();
・・・・
F_A(); 次の処理へ
}
5
関数の定義
構文 <#include文からmain()の間に記載>
戻り値の型 関数名(仮引数リスト)
z  f x, y
{
変数宣言;
int f(int x, int y)
(例1)
intに限りません
{
文;
int z;
return 式or値;
z = x + y;
}
return z;
← 戻り値
}

void: 該当なし!
戻り値なし;引数なし
voidは省略可能!
void output(void)
(例2)
{
printf(“関数名output!\n”);
} 戻り値なしの場合、return 文を省略可 6

関数の仕様パターン
構文 <#include文からmain()の間に記載>
戻り値の型 関数名(仮引数リスト)
{
変数宣言;
# 戻り値
引数リスト
文;
1 ×(void)
×(void)
return 式or値;
2 ×(void)
○(変数宣言)
}
3
4
○(return) ×(void)
○(return) ○(変数宣言)
7
関数のよびだし 1
構文
1) 関数の戻り値が void
関数名(実引数リスト);
2) 関数の戻り値が 値
変数 = 関数名(実引数リスト);
関数は何度もよびだせる!
voidの実引数は空欄にする
output関数定義部
void output(void)
{
printf(“output!\n”);
}
void main()
{
output();
output(); 関数よびだし
}
関数を呼び出すと、定義しておいた処理がまとめて行なわれる8
関数のよびだし 2
void output(void)
{
printf(“output!\n”);
}
関数定義部
2) 関数の戻り値が 値
void ope1(void)
変数 = 関数名(実引数リスト); {
output(); 関数よびだし
関数はmain()中だけでなく、
}
任意の関数中からもよびだせる! main()
(カスケードできる)
{
関数よびだし
ope1();
全てのよびだしはmain()に帰着
}
構文
1) 関数の戻り値が void
関数名(実引数リスト);
関数はよびだし元よりも上記すること!
9
戻り値
構文
1) 関数の戻り値が void
関数名(引数リスト);
2) 関数の戻り値が 値
変数 = 関数名(引数リスト);
関数内の計算結果(戻り値)は、
1つだけ、return 文でよびだし元
に返還される。
戻り値と受け側変数の型が一致し
なくてはならない(ここではint)。
z  f x, y 
int f(int x, int y)
{
intに限りません
int z;
z = x + y;
return z; /*戻り値z*/
}
関数定義部
main()
{
int a,b,c;
a = 4; b = 2;
c = f(a,b); 関数よびだし
}
10
関数の引数 1 (変数)
構文
戻り値の型 関数名(仮引数リスト)
void buy(int z)
(例)
intに限りません
{
printf(“%d万円です\n”,z);
}
main()
{
int x;
printf(“いくら?\n”);
x = 500;
buy(x); /*実引数:変数*/
buy(300); /*実引数:値*/
}
引数は、関数のよびだ
し元から関数内に情報
(値)を渡す。
関数はその情報に応じ
た処理を行う。
x:実引数; z:仮引数;
xとzの型は一致すること!
仮引数初期値は実引数の値
% a.out
いくら?
500万円です
300万円です
11
関数の引数 2 (変数)
構文
戻り値の型 関数名(仮引数リスト)
void buy(int a, int b) (例)
intに限りません
{
printf(“%d万円と”,a);
printf(“%d万円です\n”,b);
}
main()
{
int x;
printf(“いくら?\n”);
x = 100;
buy(x,200); /*実引数:変数*/
}
関数には複数の引数を
渡すことができる。
引数リストの順に値が
代入される。
実引数と仮引数は対応
仮引数
a
b
実引数
x
200
% a.out
いくら?
100万円と200万円です12
関数の引数 3 (なし)
構文
戻り値の型 関数名(仮引数リスト)
void output(void)
(例)
{
printf(“C言語大好き\n”);
}
main()
{
int x;
output(); /*実引数:なし*/
}
プログラムを断片化する
と、プログラムが理解し
やすくなる。引数のない
関数を用いて、プログラ
ムを分散させよう。
戻り値および引数の型
voidは、省略可能。
% a.out
C言語大好き
注意事項:C言語は、基本的に実引数の値の更新を許さない!13
残念ながら、引数は情報を流すだけ(配列は例外)。
関数の引数 4 (一次元配列)
構文
戻り値の型 関数名(仮引数リスト)
#define MAX 2
void output(int t[MAX])
(例)
{
仮引数:
int i;
配列名[要素数]
for(i=0;i<MAX;i++){
printf(“t[%d]=%d\n”,i,t[i]);}
}
main()
実引数:
{
配列名
int a[MAX];
a[0]=0; a[1]=1;
output(a); }
/*実引数:配列*/
変数と同様、
配列も引数に
適用可能!
要素数の設定
にはマクロを!
% a.out
t[0]=0
t[1]=1
14
関数の引数 5 (二次元配列)
構文
戻り値の型 関数名(仮引数リスト)
void output(int t[1][2])
(例)
{
仮引数:配列名[要素数] [要素数]
int i;
for(i=0;i<2;i++){
printf(“t[0][%d]=%d\n”,
i,t[0][i]);}
}
main()
実引数:
{
配列名
int a[1][2];
a[0][0]=0; a[0][1]=1;
output(a); }
/*実引数:配列*/
二次元配列も
一次元配列と
同様!
% a.out
t[0][0]=0
t[1][1]=1
15
関数の引数 6 (配列:値更新1)
構文
戻り値の型 関数名(仮引数リスト)
関数に渡された配列
は、関数の中で実引
数の更新可能!
(変数はダメ!!)
void vector(int t[2])
(例)
{
t[0]=3; t[1]=6; /*値の更新*/
}
main()
% a.out
{
a[0]=0 a[1]=1
int a[2];
*a[0]=3 *a[1]=6
a[0]=0; a[1]=1;
printf(“a[0]=%d a[1]=%d\n”,a[0],a[1]);
vector(a);
printf(“*a[0]=%d *a[1]=%d\n”,a[0],a[1]);
}
16
関数の引数 7 (配列:値更新2)
構文
戻り値の型 関数名(仮引数リスト)
仮引数リストで、変
更したくない引数に
(例)
constを添付すると、
実引数の変更が不
可となる!
void vector(const int t[2])
{
t[0]=3; t[1]=6; /*値の更新*/
}
% a.out or Error!
main()
a[0]=0 a[1]=1
{
*a[0]=0 *a[1]=1
int a[2];
a[0]=0; a[1]=1;
printf(“a[0]=%d a[1]=%d\n”,a[0],a[1]);
vector(a);
printf(“*a[0]=%d *a[1]=%d\n”,a[0],a[1]);
17
}
変数とスコープ1
#include <stdio.h>
int a;
void func(void)
{
int b;
変数 bの値が
・・・・・・
保障される範囲
}
void main()
{
int c
変数 cの値が
・・・・・・
保障される範囲
}
変数 aの値はプログラ
ム全域で保障される
範囲:スコープ
変数 変数種類
a
b
C
グローバル変数
ローカル変数
ローカル変数
異なる関数内のローカル変数は
仮引数名を含めて、名前が重複
してもよい。異なる変数である。
18
変数とスコープ2
#include <stdio.h>
int a;
void func(void)
{
ローカル変数
int a;
aの値が保障
a = 1;
される範囲
}
void main()
{
a = 0; グローバル変数
・・・・・・
aの値が保障
}
される範囲
変数種類
値
グローバル変数 a
ローカル変数
a
0
1
変数値を更新するとき!
ローカル変数で宣言されている場合
は、ローカル変数を更新!
否は、グローバル変数を更新
グローバル変数は引数として使う必
要はない!
グローバル変数とローカル変数
は同じ名前にしてもよい。が、
混乱を招くので避けよう! 19
変数とスコープ3
変数の種類
記憶クラス
グローバル変数
(大域変数)
ローカル変数
(局所変数)
記憶(値)保存期間
プログラム起動時から終了まで
static
プログラム起動時から終了まで
(auto)
宣言されてから関数終了まで
ローカル変数 static宣言の構文 グローバル変数とStatic変数は
自動的に初期値=0が設定される
static int a;
/*例*/
ローカル変数は関数がよびだされると、変数や配列が
準備され、関数終了次第、廃棄(値は消滅)される。
一方、staticローカル変数は、値が保持される!
20
変数とスコープ4
プログラム例
void func(int b)
{
static int a;
/*例*/
a=a+1;
b=b+1;
printf(“%d %d\n”,a,b);
}
main()
{
int b;
b = 0;
func(b); func(b);
func(b); func(b);
}
func()回数
1
2
3
4
a
1
2
3
4
b
1
1
1
1
static変数aは
関数終了とともに
記憶が破棄されない!
Local変数b(仮引数)は
関数終了とともに
記憶が破棄される 21
関数のプロトタイプ宣言
構文 <#include文から最初の関数の間に記載>
戻り値の型 関数名(仮引数リスト);
#include <stdio.h>
int Output(int X); ← 関数のプロトタイプ宣言
main()
{
構文は同様
F = Output(x);セミコロン;
}
メリット:
① main()関数をプログラム
の先頭に記載可能
② 関数の記載の順序を
無視可能
int Output(int x)
{
関数の定義
・・・・
}
main()関数よりも下に記載
22
関数の利用(練習)
問題4 配列と関数を用いて、任意個数の数列の
合計と平均を算出するプログラムを開発してくだ
さい。
<アルゴリズム>
1 main()にて任意個数の数列を入力し、配列に
格納する。
2 配列を合計用関数と平均用関数に渡す。
3 関数で合計と平均を算出し、計算結果をmain()
に戻す。
4 main()にて関数の戻り値(合計と平均)を出力
する。
演算関数は、引数あり戻り値ありの関数を用いる
23
問題4 解答例
#include <stdio.h>
#define MAX 101
double F_sum(int n, double data[MAX])
{ double z;
int i;
z =0;
for(i=0;i<=n;i++) {
z = z + data[i];
}
return z; }
合計
double F_ave(int n, double data[MAX])
{ double z;
int i;
z =0;
for(i=0;i<=n;i++) {
z = z + data[i];
}
return z/n; }
平均
main()
{
double sum, ave, data[MAX];
int n,i;
for(i=0;i<MAX;i++)
{ data[i] = 0.0;
}
printf(“Number of data(n<=100) ? ");
scanf("%d",&n);
for(i=1; i<=n; i++) {
printf("data[%d]= ",i);
scanf("%lf",&data[i]); }
sum = F_sum(n,data);
ave = F_ave(n,data);
printf("sum = %10.5lf ave = %10.5lf\n“
,sum,ave);
}
24
引数とポインタ1
注意事項:C言語は基本的に実引数の変数の値の更新を許さない!
残念ながら、引数は情報を流すだけ。(配列は可能)
プログラム例
func(int x,int y)
{
y=x=x+3;
}
×
main()
変更できません
{
int x,y; 実引数
func(x,y);
・・・・・・・
}
実引数1個の更新なら
return 文をつかえばよい!
例) x=func(x,y);
どうしても、2個以上の実引数の値を
同時に更新したいときがあります。
→ 1.配列に置換する(^^;
→ 2.ポインタを使う
25
引数とポインタ2
プログラム例 (主要部)
func(int *x,int *y)
{
*変数
・・・・・
(仮引数)
*x=*x+3;
*y=*y+5;
・・・・・
}
main()
{
&変数
int x,y;
(実引数)
func(&x,&y);
・・・・・・・
}
構文
①実引数リストの各変数の前
に&をつける!
func(&x,&y)
②仮引数リストの各変数の前
に*をつける!
func(int *x,int *y)
③関数の中では、仮引数の変
数は、全て*つきで記載す
る!!
*x=*y; *y=temp;
26
引数とポインタ3
プログラム例 (主要部)
メモリ上の変数の保存状態
func(int *x,int *y)
{
関数?
main
func
・・・・・
*変数
temp
x y
*x=*x+3;
(仮引数)
2
3
4
5
6 7
*y=*y+5; 番地の中身 番地 1
・・・・・
func実行中
}
x,yは範囲外なので直接更新不可
&変数
番地を使ってパスポートを発行!
main()
(実引数)
① &x=1, &y=2 と引数を渡す
{
番地
② *xは番地1の変数!
int x,y;
*yは番地2の変数!
func(&x,&y);
番地は計算機内の公用語
・・・・・・・
27
→*x,*yはmain内のx,yを参照
}
引数とポインタ(練習)
問題5 ポインタを用いて、関数main内の任意の
2つの変数を入れ替える関数(swap)を作成し、
実行してください。
アルゴリズム
1.関数mainにて任意の2値を変数にとりこむ。
2.関数mainからswapに上記2値の変数をポイ
ンタで渡し、swap内で入れ替える。
3.関数mainにて、swap使用前後の変数内の値
の入れ替わり具合を出力する。
28
問題5 解答例
プログラム例 (主要部)
func(int *x,int *y)
{
*変数
int t;
(仮引数)
t=*x;
*x=*y; *y=t;
}
main()
{
int x,y;
&変数
func(&x,&y); (実引数)
・・・・・・・
・・・・・・・
}
構文
①実引数リストの各変数の前
に&をつける!
func(&x,&y)
②仮引数リストの各変数の前
に*をつける!
func(int *x,int *y)
③関数の中では、仮引数の変
数は、全て*つきで記載す
る!!
*x=*y; *y=temp;
29
構造体1 構文
異なる型を、まとめて新しい型として宣言する!
これまでは、int,float,doubleなどを使った。
#include文から最初の関数の記述までに記載
構文
例)
typedef struct {
typedef struct {
型名 識別子;
int
ID;
int
Age;
型名 識別子; メンバ
float
Height;
・・・・・・・・
float
Weight;
}構造体名;
}Person;
30
構造体2 変数
これまでの
変数の規則
と同じ!
プログラム 例
#include <stdio.h>
メンバの値の
typedef struct {
扱い方は変数
の場合と同じ
int ID; int math; int prog;
scanf
} test;
構造体型宣言
printf
main()
変数a,bは、test型
などもOK!
{
test a,b;
メンバの参照方法は
a.ID=1001; a.math=98;
変数名.メンバ名
a.prog=61;
構造体aの内容を構造体bに丸写し
b = a;
Printf(“%d %d %d”,a.ID,a.math,a.prog);
31
}
構造体3 配列
これまでの
配列の規則
と同じ!
プログラム 例
#include <stdio.h>
構造体の配列は
typedef struct {
データの整理に
int ID; int math; int prog;
好都合
} test;
構造体型宣言
main()
配列a,bは、test型
{
メンバの参照方法は
test a[3],b[3];
配列名[要素数].メンバ名
scanf(“%d”,&a[0].ID);
a[0].math=98; a[0].prog=61;
b[0] = a[0];
構造体aの内容を構造体bに丸写し
Printf(“%d\n”,a[0].ID);
32
}
構造体4 引数
これまでの
引数の規則
と同じ!
プログラム 例
#include <stdio.h>
実引数の構造体のメ
typedef struct {
ンバの値がそれぞれ
int ID; int math; int prog;
コピーされて関数の
仮引数に渡される
} test;
構造体型宣言
func(test a, test b[2])
関数func()定義部
仮引数
{
Printf(“%d %d”,a.ID,b[0].math); }
配列は関数内
main()
で実引数の値
{
を更新可能
実引数
test a, b[2];
a.ID=1001; a.math=98; a.prog=61;
33
b[0] = a;
func(a,b); }
構造体5 ポインタ
これまでの
ポインタの
概念とほぼ
同じ!
プログラム 例
#include <stdio.h>
typedef struct {
int ID; int math; int prog;
} test;
構造体型宣言
func(test *a)
関数func()定義部
仮引数(ポインタ*)
{
ポインタで構造体のメンバ
a->ID = 11; }
を参照する場合は、「.」で
main()
なく「->」を使用し、関数内
{
実引数(ポインタ&) で*をつけない!!
test a;
a.ID=1001; a.math=98; a.prog=61;
34
func(&a); printf(“%d”,a.ID) }
構造体(練習)
問題6 50名以下の任意人数のIDと3科目(英語、数学、
プログラム)の成績を格納する構造体を宣言し、構造
体に格納した成績を用いて各教科の平均値と3科目総
得点の高い人(ID)を探すプログラムを作成してくださ
い。
アルゴリズム
1.関数mainにて任意人数分の3科目の値を構造体にと
りこむ(入力する)。
2.各科目の平均値を算出する(関数・ポインタをもちいて
も可)。
3.3科目総得点が最大のIDを探す(関数・ポインタをもち
いても可)。
35
最終練習
問題5 任意の関数、構造体などを用いて、前作の
直線回帰のプログラムをすっきりと再構築しなおし
てください。
<アルゴリズム>
1 関数を用いて、入力・計算・出力部に分割
2 さらに各関数を再分割(平均・和算など)
%
%
%
%
cp 元file名 新file名 ← file複製命令
a.out < infile名
← file入力命令
a.out > outfile名
← file出力命令
a.out < infile名 > outfile名
36
おわりに
• さて、問題です。
1.ポインタを用いると何ができますか?
2.構造体の構文は?
3.その他、質問など
4.本講義の感想をお願いします。
37