C言語講座第4回 2017 関数とポインタ.

Slides:



Advertisements
Similar presentations
C 言語講座第 5 回 構造体. 構造体とは ... 異なる型の値をまとめて新しい型とする 機能がある . つまり , 複数の変数を 1 つのまとまりにできる . 配列と違って同じ型でデータをまとめるのではな く違った型のデータをまとめられる .
Advertisements

C 言語講座 第 7 回 ポインター. メモリとアドレス(ポインターの前 に) コンピュータのメモリには 1 バイトずつ 0 番地、 1 番地、 2 番地・・・というように 住所が割り当てられている この住所をアドレスという。 メモリはデータをしまうもので それを引き出すためには メモリに番号(アドレス)を振っておけばよいな.
オブジェクト指向言語・ オブジェクト指向言語演習 中間試験回答例. Jan. 12, 2005 情報処理技術基礎演習 II 2 オブジェクト指向言語 中間試験解説 1  (1) 円柱の体積(円柱の体積 = 底面の円の面積 x 高さ) を求めるプログラムを作成しなさい。ただし、出力結果は、入 力した底面の円の半径.
復習 配列変数の要素 5は配列の要素数 これらの変数をそれぞれ配列の要素と呼ぶ この数字を配列の添え字,またはインデックスと呼ぶ
復習 配列変数の要素 5は配列の要素数 これらの変数をそれぞれ配列の要素と呼ぶ この数字を配列の添え字,またはインデックスと呼ぶ
プログラミング入門2 第4回 配列 for文 変数宣言 初期化
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
第2章 数値の入力と変数 scanfと変数をやります.
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
文字配列の課題1 解説 /* a */ #include <stdio.h> main( ) { int i;
プログラミング演習(2組) 第12回
C言語講座 第4回 ポインタ.
第8回 プログラミングⅡ 第8回
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
第6章 2重ループ&配列 2重ループと配列をやります.
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
精密工学科プログラミング基礎 第9回資料 (12/11 実施)
精密工学科プログラミング基礎Ⅱ 第3回資料 今回の授業で習得してほしいこと: 2次元配列の使い方 (前回の1次元配列の復習もします.)
C言語講座 第3回 ポインタ、配列.
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
プログラミング2 関数
関数とポインタ 値呼び出しと参照呼び出し swapのいろいろ 関数引数 数値積分
関数の定義.
第10回関数 Ⅱ (ローカル変数とスコープ).
プログラミング論 II 2008年10月30日 文字列
第10章 これはかなり大変な事項!! ~ポインタ~
プログラミング入門2 第11回 情報工学科 篠埜 功.
今までの練習問題の復習.
第7回 プログラミングⅡ 第7回
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
地域情報学 C言語プログラミング 第5回 ポインタ、関数、ファイル入出力 2017年11月17日
第11回 プログラミングⅡ 第11回
プログラミング基礎B 文字列の扱い.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
精密工学科プログラミング基礎Ⅱ 第4回資料 今回の授業で習得してほしいこと: 文字列の扱い ファイル入出力の方法 コマンドライン引数の使い方
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
C言語 はじめに 2016年 吉田研究室.
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
11.1 標準ライブラリ関数 11.2 関数呼び出しのオーバーヘッド 11.3 大域変数 11.4 プロトタイプ宣言 11.5 関数引数
第5回 プログラミングⅡ 第5回
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
プログラミング入門2 第5回 配列 for文 変数宣言 初期化
標準入出力、変数、演算子、エスケープシーケンス
プログラミング 4 文字列.
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
第7章 そろそろ int 以外も使ってみよう! ~データ型 double , bool~
プログラミング演習I 2003年6月11日(第9回) 木村巌.
情報処理Ⅱ 2005年11月25日(金).
プログラミング演習II 2004年11月 2日(第3回) 理学部数学科・木村巌.
プログラミング入門2 第5回 配列 変数宣言、初期化について
第4回 配列.
第2章 数値の入力と変数 scanfと変数をやります.
知能情報工学演習I 第11回(後半第5回) 課題の回答
知能情報工学演習I 第9回(後半第3回) 課題の回答
計算技術研究会 第5回 C言語勉強会 関数(function)を使う
C言語講座第5回 2017 構造体.
分岐(If-Else, Else if, Switch) ループ(While, For, Do-while)
第5回 配列.
知能情報工学演習I 第10回( C言語第4回) 課題の回答
プログラミング演習I 補講用課題
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
プログラミング 3 ポインタ(1).
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
Presentation transcript:

C言語講座第4回 2017 関数とポインタ

前回の補足:マクロ定義 #define P 3+5+7 #define Q 3*P //Q=3*3+5+7=21 また、置換後の式全体をかっこで囲まないと予想外の挙動になる場合がある。

関数とは 複数の処理をまとめたもの。 一連の処理をくり返し使いたい時などに便利。 stdlib.hの中のrand()など、汎用性の高いものはあらかじめ用意されている。 今回はこの関数を自作する。

関数を使わないと #include <stdio.h> int main(){ char a; scanf("%c", &a); printf("%c", a); return 0; } 繰り返したい処理を main()の中に何度も 書かなければならない。 1つの間違いを修正するために 全ての箇所をチェックするはめになる。

関数を使うと #include <stdio.h> mojiprint(){ char a; scanf("%c", &a); printf("%c", a); } int main(){ mojiprint(); return 0; 関数の呼び出し 詳しい解説は次ページ以降で行うが、 処理の中身を書くのは 一度だけで良くなる。 修正が楽になる他、 main()の構成が分かりやすくなる等の 利点がある。

ステップ1:一番簡単な関数 mojiprint(){ //処理 } 関数の名前は任意。 後述のプロトタイプ宣言をしない場合は 自作関数を呼び出す前に処理の中身を書いておく必要がある。 (先ほどの使用例でいえばmain関数の前にmojiprint関数が書いてある)

演習1 hello,world.をprintfする関数を作成し、 それをmain関数から呼び出すプログラムを作成せよ。

演習1解答例 #include <stdio.h> hw(){ printf("hello,world\n"); } int main(){ hw(); return 0; printfはstdio.hの中にあるため、 hwの前にincludeする必要がある。

プロトタイプ宣言 #include <stdio.h> hw(); int main(){ return 0; } hw(){ printf("hello,world\n"); 自作関数の並び順が 上手く定まらない場合、 プロトタイプ宣言を行う。 右の例のhw();のように、 関数名();と書く。 これをすることで 呼び出し先の関数が 下にあっても呼び出すことができる。

ステップ2:引数(ひきすう) printnum(int n){ printf("%d\n", n); } 関数名の後の()で変数を宣言すると その関数に値を渡すことができる。

引数使用例1 #include <stdio.h> printnum(int n); int main(){ printnum(3); return 0; } printnum(int n){ printf("%d\n", n); printnumで宣言しているnに 3を代入して処理を行う。

引数使用例2 #include <stdio.h> printsum(int n, double r); int main(){ int pren = 3; double prer = 4.1; printnum(pren, prer); return 0; } printsum(int n, double r){ printf("%f\n", n+r); 引数をカンマで区切って 複数指定することもできる。 右の例では第一引数のprenの値を printsumの第一引数nに代入している。 prerとrについても同様。

ステップ3:戻り値 int calcsum(int m, int n){ return m+n; } 関数名の前に戻り値の型を書いた場合、 その型の値を返すことができる。 戻り値のことを返り値ともいう。

戻り値使用例 #include <stdio.h> int calcsum(int m, int n); int main(){ int prem = 3; int pren = 4; int sum; sum = calcnum(prem, pren); printf("%d\n", sum); return 0; } int calcsum(int m, int n){ return m+n; calcsum(int, int)がその値に変わると 考えればよい。 右の例ではsumにcalcsumの戻り値を 代入している。

演習2 int型変数prea, prebにscanfで値を入力したのち、 その二つの値の差を返す関数を呼び出しなさい。 小数点第一位まで表示せよ。

演習2解答例 int calcdist(int a, int b){ return b-a; } #include <stdio.h> int calcdist(int a, int b); int main(){ int prea, preb,dist; scanf("%d", prea); scanf("%d", preb); dist = calcdist(prea, preb); printf("%.1f", prea+dist/2.0) return 0; }

関数内での 変数宣言 #include <stdio.h> int addnum(int a); int main(){ int prea, a2; scanf("%d", prea); a2 = addnum(prea); printf("%.d", a2); return 0; } int addnum(int a){ int num; scanf("%d", &num); return a+num; 関数では引数以外にも 変数を宣言できる。 関数内で宣言した変数は その関数内でのみ使用可能で、 main関数内では使用不可能。 例でいうとnumという変数はaddnumの 実行中のみ存在しており、 addnumの終了時に破棄。(未定義状態)

変数のスコープ #include <stdio.h> void ato5(int a); int main(){ int a = 3; ato5(a); printf("%d", a);//3 return 0; } void ato5(int a){ a = 5; 逆に、main関数で宣言した変数を 直接他の関数で使うことはできない。 右の例ではmainとato5のどちらにも aという変数があるが、 これらは同じ名前の別物であり、 ato5側でaを5に変えても mainのaは変わらず3のままである。 変数の有効範囲のことを その変数のスコープという。

間接代入 #include <stdio.h> void ato5(int a); int main(){ int a = 3; ato5(&a); printf("%d", &a);//5 return 0; } void ato5(int *pa){ *pa = 5; main関数の変数を変更する方法として ポインタを使う方法がある。 右の例ではato5を呼び出す時 引数を&aにしている。 これによってato5(int *pa)の paに&aが代入され *paがaを指すようになっている。 int *pa = &a ポインタ変数の 宣言時代入は pa = &a

演習その3 int型変数a,bをscanf関数を用いて入力させて出力したのち、 自作関数swap()で値a, bを交換し、 main関数でa,bを出力しなおしてください。

演習その3解答例 int main(void){ int a, b; printf("a = "); scanf("%d", &a); printf("b = "); scanf("%d", &b); printf("a = %d, b = %d\n", a, b); swap(&a, &b); return 0; } #include <stdio.h> void swap(int *x, int *y){ int tmp; printf("入れ替えます!\n"); tmp = *x; *x = *y; *y = tmp; }

文字列の導入 C言語では文字列をcharの配列で表す。 char a[5]={'E', '1', '0', '1', '\0'};とすれば、"E101"という文字列を作れる。 ここで'\0'とは、その文字列が終了することを表すNULL文字であり、 文字列の最後に必ず入れなければならない。 char a[5]="E101";やchar a[]="E101"と書くこともできるが、 この場合も5文字分用意しなければならないことに注意。

文字列の入出力 文字列を入出力したい場合、"%s"(string)という指定子が使われる。 例えばchar a[5]="E101"を表示したい場合は、 printf("%s", a);とすればよい。 ここでのaはa[0]のアドレスであり、 これを利用してa[0]、a[1]、…と順に表示していく。 逆に入力したい場合はa[5]が宣言してある状態で scanf("%s", a);とすればよい。

scanf("%s", a);の注意点 scanf("%s", a);をそのまま使うのは危険性が高いのですが、 授業で深く扱わないのでここでも深く扱いません。 ここで最低限覚えておくべきなのは、 ・空白文字の入力は禁止 ・a[5]に対して入るのは4文字だけ の2つです。 名前などを入力させる際は、 予想される文字列の長さより大きめの配列を宣言しておきましょう。

演習その4 アルファベットを何文字かずらした暗号をシーザー暗号といいます。 例:HAL→IBM(すべてを1文字後ろにずらした場合) 文字列を1文字後ろに置換したシーザー暗号に変換するvoid関数zurasuと 元の文字列に戻すvoid関数modosuを作成してください。 平文として入力されるのは半角英文字のみからなる文字列とし、 (それ以外が入力されることは考えなくてよい) Zからずらした場合はA、zからずらした場合はaとします。 また、文字列の表示はmain関数内で行うこと。

演習その4解答例 #include <stdio.h> void zurasu(char *hira); void modosu(char *angou); int main(void){ char str[1024];//文字列は1023文字まで printf("半角英文字列を入力してください:"); scanf("%s", str); zurasu(str); printf("暗号文=> %s\n", str); modosu(str); printf("平文=> %s\n", str); return 0; } void zurasu(char *hira){ while (*hira != '\0'){ *hira += 1; if (*hira == 'z' + 1 || *hira == 'Z' + 1){*hira -= 26;} hira++; } void modosu(char *angou){ while (*angou != '\0'){ *angou -= 1; if (*angou == 'a' - 1 || *angou == 'A' - 1){*angou += 26;} angou++;

演習その5 5名のそれぞれの得点を0~20点の範囲で手入力させた後、 最⾼点と最低点の各1名を除いた、残り3名の得点の合計を求めてください。 ただし、 2つの数値のうち⼩さい⽅の値を戻り値とする関数lt(a,b) 2つの数値のうち大きい⽅の値を戻り値とする関数gt(a,b) を作成し、使用してください。

演習 その5 解答例 #include <stdio.h> int lt(int a, int b); int gt(int a, int b); int main(){ int pt, i; int max = 0; int min = 20; int sum = 0; for (i = 0; i < 5; i++){ printf("得点を入力: "); scanf("%d", &pt); max = gt(pt, max); min = lt(pt, min); sum += pt; } printf("最高点: %d\n", max); printf("最低点: %d\n", min); printf("残合計: %d\n", sum - (max + min)); return 0; int gt(int a, int b){ return (a > b) ? a : b; } int lt(int a, int b){ return (a < b) ? a : b;

補足 関数やNULL文字に関して

main関数 main関数自体も関数であるが、 mainは「特別な名前」として定義されており、 プログラム実行時に最初に呼び出されることになっている。

プロトタイプ宣言の略記法 printnum(int); のように、変数名を省略することができる。

特殊なスコープ1:グローバル変数 main関数を含むどの関数にも属していない部分でも 変数を宣言することができる。 この変数はグローバル変数と呼ばれ どの関数からも参照および変更が可能。 ただしその分危険性も高いため、安易には使えない。

特殊なスコープ2:静的(static)変数 (主に自作の)関数内での変数宣言で static int a; のように前にstaticを付けると その変数は関数が終了しても保持される。 ただし、その関数からしか参照および変更ができない。 次ページに使用例を示す。

静的変数使用例 void count(); int main(){ count();//1回目の呼び出し count();//2回目の呼び出し return 0; } void count(){ static int i = 0; i++; printf(“%d回目の呼び出し”, i); 右の例で static int i = 0; によるiの宣言と初期化は 1回しか行われない。 また、このiをcount以外の関数から 参照、変更することはできない。

特殊なスコープ3:for内限定 for(int i=0; i<N; i++){} のようにforの初期化式内で変数を定義すると、

スコープの意味 #include <stdio.h> void swap(int *x, int *y){ int tmp; printf("入れ替えます!\n"); tmp = *x; *x = *y; *y = tmp; } 右のswap(int*, int*)は 演習その3で使ったものだが、 これをmain関数で書くと tmpという 今後一切使わない変数も 保持され続ける。 スコープの限定は 間違った参照、代入を防ぐ以外に 保持する変数を減らして 実行速度を上げる意味もある。

"%s"とNULL文字 char a[5]={'E', '1', '0', '1', '\0'};を宣言したうえで、 printf("%s", a);とすると、"E101"と表示される。 これに対し、 char a[5]={'E', '1', '\0', '1', '\0'};と宣言したうえで、 printf("%s", a);とすると、"E1"と表示される。 これはprintf("%s", a);アドレスaに入っている文字から1文字ずつ NULL文字が出てくるまで表示していく命令だからである。

再帰関数(recursion) #include <stdio.h> int zenka(int n){ if(n == 1){ return 3; } else{ return 5*zenka(n-1)+2; int main(){ printf("%d", zenka(4)); return 0; 右のzenka(int n)は 引数nに対し ・a1=3 ・an=5an-1+2(n≧2) を返す。 この例では 一般項を求めれば済んでしまうが もっと問題が難しい場合や ランダムアクセス不可能な データを扱う場合に使う。

お疲れさまでした!