C言語基礎 関数・ポインタ.

Slides:



Advertisements
Similar presentations
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
Advertisements

情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
ファーストイヤー・セミナーⅡ 第8回 データの入力.
データ構造とアルゴリズム 第10回 mallocとfree
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
基礎プログラミングおよび演習 第9回
プログラミング演習Ⅱ 第12回 文字列とポインタ(1)
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
12: コマンドライン引数 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと
C言語講座 配列と文字列 Ver 0.3.
C言語講座 第4回 ポインタ.
第5回C言語講座 ~ポインタと配列.
第8回 プログラミングⅡ 第8回
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
構造体.
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
第10章 char 文字列; 文字列を入力させるよ!.
C言語講座 第3回 ポインタ、配列.
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
プログラミング2 関数
プログラミング応用 printfと変数.
プログラミング論 II 2008年10月30日 文字列
プログラミング 4 記憶の割り付け.
プログラミング演習I 2003年6月25日(第10回) 木村巌.
プログラミング演習I 2003年5月7日(第4回) 木村巌.
第10章 これはかなり大変な事項!! ~ポインタ~
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
メモリの準備 メモリには、その準備の方法で2種類ある。 静的変数: コンパイル時にすでにメモリのサイズがわかっているもの。 普通の変数宣言
前回の練習問題.
第7回 プログラミングⅡ 第7回
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
アルゴリズムとデータ構造 補足資料5-1 「メモリとポインタ」
高度プログラミング演習 (08).
地域情報学 C言語プログラミング 第5回 ポインタ、関数、ファイル入出力 2017年11月17日
P n ポインタの基礎 5 q m 5 7 int* p; int 型の変数を指すポインタ int* q; int 型の変数を指すポインタ int n=5, m=7; int 型の変数 int array[3]; int* pArray[3]; p = &n; ポインタにアドレスを代入しているのでOK.
関数への道.
プログラミング基礎B 文字列の扱い.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
C言語 はじめに 2016年 吉田研究室.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
第1章 いよいよプログラミング!! ~文章の表示 printf~
情報基礎演習B 後半第2回 担当 岩村 TA 谷本君.
プログラミング論 ポインタ
第5回 プログラミングⅡ 第5回
第2章 printf(“変数と入力”); scanf(“%d”,&num);
プログラミング論 文字列
コンパイラ 2012年10月11日
プログラミング 4 文字列.
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
第7章 そろそろ int 以外も使ってみよう! ~データ型 double , bool~
プログラミング演習I 2003年6月11日(第9回) 木村巌.
2005年度 データ構造とアルゴリズム 第2回 「C言語の復習:配列」
情報処理Ⅱ 2005年11月25日(金).
プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌.
プログラミング演習II 2004年11月 2日(第3回) 理学部数学科・木村巌.
printf・scanf・変数・四則演算
第2章 数値の入力と変数 scanfと変数をやります.
計算技術研究会 第5回 C言語勉強会 関数(function)を使う
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング 3 ポインタ(1).
12: コマンドライン引数 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
Presentation transcript:

C言語基礎 関数・ポインタ

関数ってなによ? 関数という言葉を聞いて、最初に思うのは、やはり数学の関数でしょう。 数学でいう定義の関数とは みたいな形だったよね。関数にxとyを渡すと結果は とかけたでしょ^^。じゃぁこれを試しにC言語で書いてみると num = f(2,3); printf(“%d”,num); と書いて、25と表示させることができそうだ。 別の例を挙げてみよう。

関数の概念 出力 入力 あまり「関数ってなにか」を考えたことはないでしょう^^。 「ばかにするな」っていわれるかも知れませんが、説明しましょう。 自動販売機 120円 商品1 コーラ 入力 出力 まずは図を描いてみました。関数とは入力に見合った出力をだす「機械」ですね^^

関数とは2 前の図を少し形式的に書いてみると 自動販売機(120,商品1)= コーラ 自動販売機(120,商品2)= ドクターペッパー 自動販売機(120,商品1)=  コーラ 自動販売機(120,商品2)= ドクターペッパー 自動販売機(100,商品1)= エラー というように、入力と出力を表せるでしょ? これが数字に限らない「関数」です。 じゃぁ関数「自動販売機(x,y)」の中身はどのようになってるのか?

自動販売機(金、商品のボタン) 自動販売機(金、商品ボタン)の内容 If(金<120){  結果(エラー); } if(商品ボタン==“商品1”){ 結果(コーラ排出); }else if(商品ボタン==“商品2”){ 結果(ドクターペッパー排出); }

C言語で関数を定義 関数名(引数1、引数2、引数3・・・)=戻り値 では、実際にC言語で関数を作ってみよう。 まず、関数には、引数と戻り値というものがあります。 関数名(引数1、引数2、引数3・・・)=戻り値 先ほどの例でいえば、引数1が「金額」、引数2が「商品番号」であり、戻り値が、「商品」ということになります。 もちろん、関数を使うまえに引数達が「数字をあらわすのか、文字をあらわすのか」をキチンと定義する必要があります。もちろん戻り値もですが^^ C言語では、これを以下のように宣言します。 戻り値型 関数名(引数型  変数名、引数型 変数名、・・・) 例) int fuction01(int a,int b) 数字のa,bを加工して、数字を返す。

実際の関数 #include <stdio.h> int wa(int x,int y); /* 関数を使うよ、という宣言 */ main(void) { int a=5,b=3; int res; res=wa(a,b); printf(“RES:%d”,res); } int wa(int x,int y) { return( x + y); }

解析1 #include <stdio.h> int wa(int x,int y); /* 関数を使うよ、という宣言 */ main(void) { int a=5,b=3; int res; res=wa(a,b); /* 関数の呼び出し */ printf(“RES:%d”,res); } int wa(int x,int y) { return( x + y); } 黄緑色の部分が、main関数、黄色部分が新しくつくった「和を求める関数、wa関数」、 水色部分が、宣言部分です。Main関数では、wa関数を呼び出して、その戻り値(結果)を RESに入れる、という形です。Wa関数の中のreturnは、その値を戻り値として返す命令です。

解析2 #include <stdio.h> int wa(int x,int y); /* 関数を使うよ、という宣言 */ 先ほどの宣言文、これは、このような形の関数を使います、という宣言文で、これを記述しないと、エラーが発生することがあります。そのため、しっかりと忘れないようにしましょう。この宣言の仕方は、関数の中身を記述する、 int wa(int x,int y) { return( x + y); } の部分の先頭にセミコロンをつけるだけです。

解析3 int wa(int x,int y) { return( x + y); } 関数の中身の定義をするとき、気をつけなれければいけないのは、変数の扱いです。もし、関数の中でループをしたい場合、変数iなどを使いたいですよね?その場合は、関数の中で変数を宣言すればいいんです。 int wa(int x,int y) { int i=0; int res=a; for(i=0;i<10;i++){ res = res +y; } return res; }

解析4 ひとつ前の例をみて、少し不安になったでしょうか?? 「main関数でも変数resを使っている」ということです。しかし、関数の中では、変数は独立して作ってくれますので、いくら他の関数と変数名が重複しても問題はないのです。 また、int wa(int x,int y){で部分で、xとyは宣言されたことになっているので、 宣言をする必要はありません。

プログラム例 引数とした文字が大文字なら0、小文字なら1を返す関数uord()を使って、入力した文字列の大文字だけ表示プログラム int uord(char ch); main(void) { char str[500];   printf(“INPUT>”); scanf(“%s”,str); for(i=0;str[i]!=‘\0’;i++){ if(uord(str[i])==0){ printf(“%c”,str[i]); } } } int uord(char ch) { if(‘A’ <= ch && ch <= ‘Z’){ return 0: }else{ return 1; } }

関数なんていらない? 関数を勉強してみて、いかがでしょうか(まだ終わったわけではないですが^^) 「別に関数なんて面倒なことしなくても、mainに書いちゃえばいいじゃん」と思うかもしれません。実際学校でで出る課題だったら、関数は必要ないかもしれません。しかし、自分でプログラムを組もうとすると、関数は必須です。 例を挙げると、私は、文字列をバイナリ形式で表示する関数を必ずつくります。 データをこのように表示させる関数をあらかじめ作っておくのです。 こうすれば、データが数字として、もしくは文字として現在どのような数値が入っているのかを見ることができます。文字列を渡すと、これを表示してくれる関数を、つくっておけば、binary(str);とするだけで表示してくれるのです。これは便利でしょう??(NULL文字に関係なく指定バイト分表示してくれるよ)

ポインタという言葉がちらほら では、戻り値が二つ、三つある場合、どうすればいいか? ポインタですよ!

さぁ、そろそろポインタでもはじめっか さぁ、最後の難関、ポインタです。 がんばってついてきてね^^ まず、変数とは何かをもう一度確認してみよう。いままで、変数とは、データを持っているものであったが、では、内部ではどのような動きをしているのか? ここで、アセンブリの知識が少し必要となります。まずアセンブリでの値の参照方法は、 mov ax,[0x012345] で、メモリの番地0x012345にある数値をaxに入れるという動作をします。 mov ax,0x012345 の場合は、axに0x012345(数値)を入れるという動作をします。 つまり、プログラムにおいて、数値を「アドレス」とみなすか、「数値」とみなすかの二通りがあるということです。

プログラムにおいて、数値を「アドレス」とみなすか、「数値」とみなすかの二通りがある まずはじめに プログラムにおいて、数値を「アドレス」とみなすか、「数値」とみなすかの二通りがある いままでプログラミングしてきて、そんなことを考えたこと無かったでしょう? 実は、いままでの「変数」は、アドレスとしての変数だったわけです。変数の宣言でアドレス(0x012345)が割り当てられたら、たしかにプログラムは0x012345と変数を対応付けているが、結局 mov ax,[0x012345] と置き換えてしまうので、プログラマは「どこのアドレスに在るのかよくわからないけど、とりあえず、宣言されたら、ひとつのメモリ領域が確保されるから、その値だけ考えればいいや」と割り切れるわけです。 別の表現。 変数と、メモリ番地は対応表のようなもので、関連付けられている。よって、変数に割り当てられた番地はプログラムが終了するまで変わらない。すべての命令が mov ax,[番地]などと置き換えられるのならば、プログラマは、アドレスを意識しなくても、変数名だけ気にしてれば良い。

変数と対応表で結ばれているのはメモリ番地である。 図 変数と対応表で結ばれているのはメモリ番地である。 mov ax,[test] add ax,0x50 mov [a],ax 対応表(コンパイル時に自動生成) ----------------------------------------- 変数名 アドレス Test 0x12345 a 0x33BA0 I 0x12FFA mov ax,[0x12345] add ax,0x50 mov [0x33BA0],ax ほら、コンパイル時に勝手に対応表が作られるのだったら、アドレスを意識しなくてもOKだよね^^

変数とアドレスの関係を調べよう では理解を深めるために、このようなプログラムを書いてみよう printf(“DATA:%d\n”,test) printf(“ADDRESS:%p\n”,&test); これを実行すると、testのメモリ番地と、その番地を参照して得ることができるデータを読む(つまり通常の動作)ことができる。 どうだろうか。変数の最初に「&」をつけることにより、変数そのものの値、つまりアドレスを参照できるわけ。 ほら、気づいたかな?この「&」、scanfで使ったよね^^。Scanfに&aとかを渡して、実行すると、aになにか数字が入っているという動作は、「アドレスそのものを関数に教えて、そこに直接書き込んでもらうという」ということをやっているわけです。

アドレスに直接数値を入れる 変数に&をつけることで、アドレスが参照できることが分かった。これを関数で応用すると、scanfのように「&a」と指定して、その値を変更できるような、関数が設計可能であるl。でもいまの状態ではなにもできない。 なぜなら、あるアドレスに直接書き込むということができないからだ。いままでやった変数を思い出してみても、書き込めるのは「変数が保持しているアドレス番地先」でしかないのだから。やっとここでポインタが登場する(わーい)

まずは・・ ポインタの宣言 ポインタ型 *ポインタ名 Char *chp; 初期化 Char *chp = &ch; 値の参照 ポインタ型 *ポインタ名 Char *chp; 初期化 Char *chp = &ch; 値の参照 Printf(“%p”,chp); printf(“%c”,*chp); *chp=‘a’; わからなければ、ここは読み飛ばしてもらってもOKです。慣れてきてから読むと「はいはい」と思うはず!

とりあえず、知っておくこと まぁまぁ、突っ込んでいくと教えることが多すぎるので、とりあえず、知っておくこと。 ★ 内容の参照は「*」をつけて、アドレスの参照はそのまま。    例) char *cp; char ch; cp = &ch; *cp = ‘K’; ★ ポインタ型は、代入しようとしている変数と同じものにしておく。    例) char ch; int num; char *cp; cp = &ch; // ○       cp = &num // ×

変数とポインタは一緒? 変数 変数 :アドレスを参照した値(可変) &変数:そのアドレス自身(0x012345)(不変) ポインタ ポインタ:アドレス自身(0x012345)(可変) *ポインタ:アドレスを参照した値(可変) この(可変)とか(不変)に注意されたい。変数は宣言されたときにアドレスが固定されてしまう。だから、特定のアドレスに書き込みができない。しかし、ポインタは宣言時にその存在のみが宣言されるため、すべてが可変なのです。では、小さな例を。

例 Int *chp; int test=100; chp = &test; //変数testのアドレスをポインタに代入 *chp = 500; //変数testのアドレスに直接、数値500をぶち込む printf(“%d\n”,*chp); //ポインタが保持するアドレスを参照した値 printf(“%d\n”,test); これは意味がないやり方ですが、このような使い方もできる これを関数で応用してみよう!

例 void swap(int *a,int *b); //二つの変数を取り替える関数 void main(void) { int data1,data2; scanf(“%d”,&data1); scanf(“%d”,&data2); printf(“[%d %d]\n”,data1,data2); swap(&data1,&data2); printf(“[%d %d]\n”,data1,data2); } void swap(int *a,int *b) { int tmp; tmp = *a; *a = *b; *b = tmp; }

じゃぁ自分で作ってみよう。 なんでもいいからポインタを使ったプログラムを作ろう。 先ほどの例を改造して、文字の交換をする関数に変えてみるとか。 とりあえず、ここまで。

文字列を関数に渡してみよう #include <stdio.h> int _strlen(char data[]); void main(void) { char str[512]; scanf("%s",str); printf("length:%d\n",_strlen(str)); } int _strlen(char data[]) int i; for(i=0;data[i]!='\0';i++); //これで空ループ return i; これは、文字列を渡すと、その長さを返してくれる関数。これは、相当便利だと思います。C言語にこの関数(strlen())があることが、それを物語っていますね^^

まず、配列はメモリ上でこのように表現されていることに注意してください。 簡単そうに見えるけど 別に、引数のところをstr[]とするだけやん!と思うでしょう。 しかし、この構造はそこまで簡単ではありません。 まず、配列についての復習をします。これを抑えないとヤバいですよ^^ アドレス ‘k’ 012000番地 ‘k’ ‘e’ ‘I’ ‘s’ ‘a’ ‘n’ ‘\0’ ‘e’ 012001番地 ‘I’ 012002番地 Str[0] Str[1] Str[2] Str[3] Str[4] Str[5] Str[6] ‘s’ 012003番地 ‘a’ 012004番地 ‘n’ 012005番地 ‘\0’ 012006番地 まず、配列はメモリ上でこのように表現されていることに注意してください。