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

C言語講座 後半

N/A
N/A
Protected

Academic year: 2021

シェア "C言語講座 後半"

Copied!
89
0
0

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

全文

(1)
(2)
(3)

 これまではあまり気にしてきませんでしたが、

それぞれ変数の型が持つ特性を

気にかけていないと困る場合があります。

 例えば、double型とint型を混ぜて使う場合を

(4)

 int型の a に整数 5 を代入し double型の b には、それを2で割った 2.5 を代入したいとします。 ですが、これを実行すると…?? int a; double b; a = 5; b = a/2.0; printf("b : %lf",b);

(5)

 こうなってしまいます。  代入される b は確かにdouble型なのですが、 小数点以下が切り捨てられてしまいました。 b : 2.000000 int a; double b; a = 5; b = a/2.0; printf("b : %lf",b);

(6)

b = a/2;  これはint型の値で演算をした時にはint型の 性質が適用されることに原因があります。  int型は整数専用なので、小数点以下はすべて 切り捨てしまうのです。  これを解決し、きちんと小数点以下まで b に代入するには2通りの方法があります。

(7)

 方法1: a もdouble 型にする。  double型なら、割ったときも 小数点以下がきちんと代入されます。 double a, b; a = 5; b = a/2.0; printf("b : %lf",b); b : 2.500000

(8)

 方法2:  キャスト演算子を使い、キャストする。 int型の a を一時的にdouble型として見て 演算を行うことができます。 int a; double b; a = 5; b = (double)a/2.0; printf("b : %lf",b); b : 2.500000

(9)

 (型)のようにして、変数の前に付けます。 これが付けられた変数は、演算の際 一時的にその型として見て計算されます。 int a; double b; a = 5; b = (double)a/2.0; printf("b : %lf",b); b : 2.500000

(10)

 ところで、先ほどからわざわざ  「2」を「2.0」と書いていますが、 これは2をdouble型として書いているからです。 (「2」だとint型です。)  数字にもちゃんと型があるのです。 int a; double b; a = 5; b = (double)a/2.0; printf("b : %lf",b);

(11)

 型はなるべく合わせて書きましょう。  上だとdouble型をint型で割っています。 (実行結果に支障はないと思いますが。)  代入の際も同じです。 上はdouble型へのint型の代入です。 b = (double)a/2; b = (double)a/2.0; double c = 2; double d = 2.0;

(12)

 ちなみに、float型の数字は 最後にfを付けることで書くことができます。 対応はこんな感じです。 float num = 3.0f; 型 0 1 -2 int 0 1 -2 double 0 1.0 -2.0 float 0 1.0f -2.0f

(13)

 問題です。 基本問題ページにある 「整数を5個入力し、 平均を小数点以下まで求め、 平均と、平均以上の整数の個数を 出力するプログラム。」 は、誤りを含んでいて正しく動きません。 実行例のように動くプログラムに 修正してください。

(14)
(15)

 関数…複数の処理をまとめて書くことで、

それを呼び出すだけで特定の処理を させることができるもの。

 これまでに使ってきた、

main() や printf() 、scanf() も関数の一種。 (printf() は、「文字を出力する」

という処理する関数。)

※printf() 、scanf()の処理の内容は、

(16)

 関数には、 「関数の中に書かれた処理を実行する機能」 以外に、 「他の関数から値を受け取る機能」と、 「呼び出した関数に値を返す機能」があります。 値を受け取る必要があるか、 値を返す必要があるかは関数の内容によります。

(17)

 関数の書き方  引数(ひきすう)…呼び出した関数から受けとる値  戻り値…呼び出した関数に返す値 戻り値の型 関数名( 引数 ){ 処理の内容 } 半角スペース

(18)

 戻り値の型  int型の値を返す場合  double型の値を返す場合  値を返さない場合 int example(void){ 処理 } double example(void){ 処理 } void example(void){ 処理 }

(19)

 引数

 値を受け取らない場合

あるいは

ex.)int型のaとb、double型のnumを受け取る場合

void example(int a,int b,double num){ 処理 } void (void){ 処理 }

void (){ 処理 }

(20)

 関数の例 1: 値を受け取る・返す関数

ex.整数を2つ受け取り、合計(整数)を返す関数。

return は、そのあとに書かれた値を

呼び出した側の変数に返し、

その関数の処理を終了する命令。

int add(int a, int b){ int answer;

answer = a + b;

return answer; }

(21)

 先ほどのaddをmain()から呼び出す。

int add(int a, int b){ int answer; answer = a + b; return answer; } int main(){ int x=5, y=10, z; z = add( x, y ); printf("z:%d ¥n",z); return 0; } 実行結果 z:15 1.xとyの値を渡す 2. 渡されたxとyの値を それぞれaとbに 格納する 3.値を返す 4.返された値をzに格納する ※C言語のプログラムはmain()から始まります。

(22)

 関数の例2 : 値を受け取る・返さない関数

ex.整数nを受け取り、n個 * を出力する関数。

return は無くても良い。

void line(int n){ int n;

for(i=0;i<n;i++)printf("*"); printf("¥n");

return;

(23)

void line(int n){ for(i=0;i<n;i++)printf("*"); printf("¥n"); return; } int main(){ line(10); return 0; } 実行結果 **********

(24)

 main()以外の関数も他の関数を呼び出せるが、 それぞれの関数が呼び出せるのは、 自分より上に書かれた関数のみ。  呼び出したい関数を、どうしても呼び出す側の 関数より下に書きたい場合は、 プロトタイプ宣言を使う。

void B(int x, double y){ Bの処理 } void A(void){ B( 引数 ); } void A(void){ B( 引数 ); }

void B(int x, double y){ Bの処理

(25)

 ex.)A()からB()を呼び出したいが、

B()をA ()より下に書きたい場合

これでAからBが呼べます。

int B(int x, double y);

void A(void){

B( 引数 ); }

int B(int x, double y){ Bの処理

}

戻り値の型、関数名、引数は 同じにする。

(26)

 問題: 自然数a,bを受け取り aからbまでの数の和(int型)を返す 関数を作成し、 その関数を使って実行例のような結果を 出力するプログラムを作ってください。 但し入力は a<b とします。

(27)

 問題: 順列 nPm 求める関数を作成し、 それを利用して 組み合わせ nCm を求める関数を作成し、 それらの関数を使って実行例のように 組み合わせを求める プログラムを作ってください。 順列 nPm = n! / (n-m)! 組み合わせ nCmnPm / m!

(28)

 ex.)A()からB()を呼び出したいが、

B()をA ()より下に書きたい場合

int B(int x, double y);

void A(void){

B( 引数 ); }

int B(int x, double y){ Bの処理

}

int B(int, double);

void A(void){

B( 引数 ); }

int B(int x, double y){ Bの処理

}

(29)
(30)

 構造体: 複数の変数を要素としてまとめた 新たな型を作りだすもの。  一般的に、配列と組み合わせて 大量のデータの扱いに用いる。  またまた言葉では分かりにくいので 具体例で見ていきましょう。

(31)

 ex.)プログラムで名簿を作るとすると

名簿の要素に

「名前」「出席番号」「年齢」 を持つとすれば

(32)

 では「名前・出席番号・年齢」 を格納する変数をまとめた構造体を profile型と名付けて定義してみましょう。 ここでは名前の変数を name、 出席番号の変数を number、 年齢の変数を ageと名付けるとします。

(33)

 これで要素に char型の配列name、 int型の変数number、age を持った型である "struct profile型"を定義することができました。 struct profile{ char name[128]; int number; int age; };

(34)

struct profile{ char name[128]; int number; int age; }; 構造体の名前 構造体に持たせる要素

(35)

 定義は関数のブロックの外側に書きましょう。 (スコープが絡むためです。) #include <stdio.h> struct profile{ char name[128]; int number; int age; }; main(){ …

(36)

 この状態ではまだ型として定義しただけ なので、構造体の実態が宣言されたわけ ではありません。 struct profile{ char name[128]; int number; int age; };

(37)

 実態の宣言はこれまでの変数通り、型 変数名;です。

ここでは変数名を"person"と名付け、 配列で宣言しています。

 "struct profile"が、"int"や"double"のように、

型の名前となっています。 struct profile型の変数の宣言です。 struct profile{ char name[128]; int number; int age; };

(38)

 配列で宣言しているのは、

複数のデータを扱うためです。 そうしないと1人分しか書けない 名簿になってしまいます。

 ↓と同じですが、数が多い場合は配列を使いましょう。

struct profile person[10];

(39)

 これで10人分の 「名前」「出席番号」「年齢」 を格納する構造体 を作ることができました。 struct profile{ char name[128]; int number; int age; }; int main(){

struct profile person[10]; …

(40)

 ここで注意。

 定義は、宣言より先に書く必要があります。

 理由は関数の宣言のときと同じ。

struct profile person[10];

struct profile{

char name[128]; int number;

int age; };

(41)

 構造体の中の要素を使うには、 . を使います。 例えば、配列3番目の要素の age に代入する場合は、 といった感じです。 struct profile{ char name[128]; int number; int age; };

struct profile person[10];

(42)

 引数として使う場合も、同様です。  要は、これまでの変数と同じ使い方です。 struct profile{ char name[128]; int number; int age; };

struct profile person[10];

printf("0人目の出席番号を入力 >> "); scanf("%d", &person[0].number );

printf("0人目の名前を入力 >> "); scanf("%s", person[0].name );

(43)

name(名前) number(出席番号) age(年齢) person[0]. Taro 25 20 person[1]. Ichiro 3 19 person[2]. Hanako 61 19 struct profile{ char name[128]; int number; int age; };

struct profile person[10];

・ ・ ・  あとは宣言した構造体の変数を使って、 こんな感じにデータを格納できるわけです。 個別に変数を宣言するよりずっと楽。

(44)

 入力部を作るとすればこんな感じ。

int i;

for( i=0 ; i<10 ; i++ ){

printf("%d 人目のデータ入力 >> ¥n", i); printf("名前 >> ");

scanf("%s", person[i].name ); printf("出席番号 >> ");

scanf("%d", &person[i].number ); printf("年齢 >> ");

scanf("%d", &person[i].age ); }

(45)

 ちなみにVisual C++では

宣言済みの構造体を. まで打つと 中身を表示してくれます。

(46)

 問題: 3人分の名前と3教科の成績(0-100)を 「名前」・「国語の成績」 「数学の成績」・「英語の成績」 の4つを要素に持つ構造体に格納し、 各教科で最も成績が良い人の名前を 出力するプログラムを作ってください。

(47)
(48)

 値を記号化することができる。 #include <stdio.h> #define NUMBER 100 int main(){ int a=2,b=5,c=7; a=NUMBER+a; b=NUMBER/b; c=NUMBER*c; printf(“a=%d , b=%d , c=%d¥n”,a,b,c); return 0; } a=102 , b=20 , c=700 続行するには何かキーを押してくだ

(49)
(50)

 変数の一種。  よくポインタ変数と呼んだりする。  メモリ上である変数が配置された場所を 示す変数。  ポインタには変数などの定数を直接代入 するのではなく、メモリアドレスを代入 する。

(51)

 変数は値をメモリのどこかに一時的に保存す るもの。  あらゆるデータにはそれを管理するアドレス (番地)が割り当てられている。  変数を指定するのはアドレスを指定するのと 同じ。  一つの変数が複数のアドレスにまたがること もある。変数の種類によって異なる。

(52)

 変数の前に&(アンパサンド:アドレス演 算子)をつけるとその変数のアドレスをあら わす。  scanf()も変数のアドレスを関数に渡していた! int main(){ int a; scanf(“%d”,&a); printf(“%dのメモリアドレスは%x”,a,&a); return 0; } 10のメモリアドレスは23fce4です。 続行するには何かキーを押してくだ %d :10進数で表示(int) %x :16進数で表示(int) %o : 8進数で表示(int) %p :ポインター値を表示(void*)

(53)

int main(){

char str[3] = {'a','g','k'}; for(int i=0 ; i<3 ; i++){

printf(“str[%d]=%cのメモリアドレスは%xです。 ¥n",i,str[i],&str[i]); } return 0; } str[0]=aのメモリアドレスは2cfb84です。 str[1]=gのメモリアドレスは2cfb85です。 str[2]=kのメモリアドレスは2cfb86です。 続行するには何かキーを押してください .

(54)

 ポインタとはメモリアドレスを格納する

ことができる変数。

 アドレスを代入すればポインタ変数は直

(55)

 通常の変数の宣言とほとんど同じ。  ポインタ変数にも型がある。

型名

*変数名

↑(アスタリスク)

ex)

int *a;

// 整数型変数へのポインタ

double *b;

// 実数型変数へのポインタ

char *str;

// 文字型変数へのポインタ

(56)

int main(){ int *po , var; var = 1234; po = &var; //ポインタに格納されているアドレスの内容 printf(“メモリアドレスの内容 = %d¥n" , *po); //ポインタに格納されているアドレス printf(“メモリアドレス = %x¥n" , po); return 0; } メモリアドレスの内容 = 1234 メモリアドレス = 28fc0c 続行するには何かキーを押してくだ

(57)

*po  ポインタ変数名の前に*(アスタリスク、 ポインタ演算子)をつけるとポインタ変 数が格納しているメモリアドレスの内容 を意味する。  ポインタ変数の定義時とは意味が違う。  元の変数をポインタを通して参照するこ とを間接参照または逆参照と呼ぶ。  &のことをアドレス演算子という。

(58)

int main(){ int *po , var; var = 100; po = &var; *po = 77; printf("変数varの値 = %d" , var); return 0; } 変数varの値 = 77 続行するには何かキーを押してください . . .

(59)

 ポインタも変数と同じように式を使った

算術ができる。

(60)

int main(){ int ary[2] = { 1000 , 2000 }; int *po; po = &ary[0]; printf("po¥t = %x¥n" , po); printf("*po¥t = %d¥n" , *po); po++; printf("po++¥t = %x¥n" , po);

printf("*po++¥t = %d¥n" , *po); return 0; } po = 14fb50 *po = 1000 po++ = 14fb54 *po++ = 2000 続行するには何かキー

(61)

 ポインタのデータ型がint型(4バイト) であり、配列を宣言したので二つの整数 が隣同士に並んでいたため、ポインタを 1増やすと4バイト分メモリアドレスが 増えた。  型によってメモリアドレスの増え方が違 う。

(62)

 *po++; →ポインタの値がインクリメントされて アドレスを参照する。つまり、もともと の場所の次の場所を参照する。 = *(po++);  (*po)++; →ポインタが指す値をインクリメントす る。 int x,ary[2] = { 10,20 }; int *ary_p; ary_p = ary; *ary_p++; x = *ary_p; printf("x=%d¥n", x); ary_p = ary; // 初期化 (*ary_p)++; x = *ary_p; printf("x=%d¥n", x); x=20 x=11 続行す

(63)

Input Num >> 5 25 続行するには何かキーを押してください . . .  整数を入力して、その整数のアドレスを ポインタに代入し、そのポインタを操作 して入力した整数を5倍するプログラム。 int main(){ int a,*po; printf("Input Num >> "); scanf("%d",&a); po = &a; *po = *po * 5; printf("%d¥n",a); return 0;}

(64)
(65)

 実は配列というものはC言語では存在しな い。  添え字を省略して配列名だけを指定する と、配列の先頭要素(要素番号0の要 素)を指すポインタとして扱われる。  配列名にポインタ変数を代入することは できない。配列名は定数のような扱い。 int array[5]; int *po; po = array; po = &array[0]; int main() { char str[256] = “programming"; printf(“str[0]の内容¥t¥t= %c¥n” , *str); printf(“str[0]のアドレス¥t= %x" , str); return 0; } // ¥tは水平タブ(だいたい4文字分スペース) str[0]の内容 = p str[0]のアドレス = 12ff54 続行するには何かキーを押してください . . .

(66)

 scanf(“%d”,&a);  変数のアドレスを渡していた。 int main(){ char str[256]; scanf("%s",str); printf("%s¥n",str); return 0; } int main(){ char str[256]; scanf("%s",&str[0]); printf("%s¥n",&str[0]); return 0; } Hello! Hello! 続行するには何かキーを押してください . . .

(67)

int array[n];

array[0] = *array = *(array+0)

array[1] = *(array+1) array[2] = *(array+2) array[3] = *(array+3) ・ ・ ・ array[n] = *(array+n)

(68)

 ポインタに格納されているアドレスを演 算することによってポインタから配列の 特定要素にアクセスできるはず。  ポインタからアドレスを演算して配列に 間接参照(逆参照)できる。 int ary[3] = { 10,20,30 }; int *ary_p; ary_p = ary;

printf("間接参照¥t=%d,%d,%d¥n", *ary_p , *(ary_p + 1) , *(ary_p + 2)); printf("添え字指定¥t=%d,%d,%d¥n" , ary[0] , ary[1] , ary[2]);

間接参照 =10,20,30 添え字指定 =10,20,30 続行するには何かキーを押し

(69)

printf("間接参照¥t=%d,%d,%d¥n", *ary_p , *(ary_p + 1) , *(ary_p + 2));  ary_pの内容自体は変化しない。ary_pの次 の内容を参照する、といった感じ。 int ary[3] = { 10,20,30 }; int *ary_p; ary_p = ary;

printf("間接参照¥t=%d,%d¥n", *ary_p , *(ary_p + 1)); printf("間接参照¥t=%d,%d¥n", *ary_p , *(ary_p + 1)); ary_p++;

printf("間接参照¥t=%d,%d¥n", *ary_p , *(ary_p + 1));

間接参照 =10,20 間接参照 =10,20 間接参照 =20,30

(70)

 配列を定義すると、配列要素のための領 域が確保される。  ポインタを定義しても、指し示す先の領 域は確保されない。矢印ができるだけ。 初期化しないとどこを指しているのかわ からん。 int main(){ int *po,array[5]; array[3] = 123; // OK *(po+3) =123; // NG po = array; *(po+3)=123 // OK array++; // NG return 0; }

(71)

 ポインタを使って2つの文字列を結合す

るプログラム。(最初に入力した文字列 に結合する。)

Input String_A >> aaa Input String_B >> bbbb aaabbbb

(72)

#include <stdio.h> int main(){

char str_a[256] , str_b[256]; char *po_a , *po_b;

printf("Input String_A >> "); scanf("%s",str_a); printf("Input String_B >> "); scanf("%s",str_b); po_a = str_a; po_b = str_b;

for( ; *po_a != '¥0' ; po_a++);

for( ; *po_b != '¥0' ; po_a++ , po_b++){ *po_a = *po_b; } *po_a = '¥0'; printf("%s¥n",str_a); return 0; }

(73)
(74)

int function(int a , int b){ int sum; a = a*a; b = b*b; sum = a+b; return sum;} int main(){ int a=5,b=10,ans; ans = function(a,b); printf(“a=%d , b=%d , a^2+b^2=%d¥n”,a,b,ans); return 0;} ← 値だけがコピーされるだけ。 このような引数の受け渡し方を 値渡しという。 ← 関数内で仮引数の値を変更しても 呼び出し側の変数には影響がない。 a=5 , b=10 , a^2+b^2=125 続行するには何かキーを押

(75)

 値渡しでは戻り値として返したものが2

つ以上ある場合は返すことができない。

 そこで、ポインタ渡しというものを利用

する。

int function(int a , int b){ int sum; a = a*a; b = b*b; sum = a+b; return sum; } ← 1個しか返せない。

int int function(int a , int b){ int sum_1,sum_2; sum_1 = a+b;

a = a*a;b = b*b; sum_2 = a+b;

return sum_1 , sum_2; }

(76)

 ポインタ渡しとは、関数を呼び出す際、 引数として変数のアドレスを渡す引数の 受け渡し方法のこと。  実引数として、呼び出し側の変数のアド レスを指定する。  呼び出された関数内で呼び出し側の変数 を直接読み書きできる。

(77)

void function(int *a_po){ *a_po = *a_po * 2; return; } int main(){ int a=5; function(&a); printf("a=%d¥n",a); return 0; } a=10 続行するには何かキーを ← ポインタに 変数のアドレスをコピーする。 ← 関数に変数のアドレスを渡す。

(78)

呼び出し側

関数側

&a ・

a □

□ a_po

関数呼び出し 関数側のポインタは、呼び出し側で指定した アドレスと同様に扱うことができる。

(79)

void function(int a , int b , int *mul , int *div){ *mul = a*b;

*div = a/b; return;}

int main(){

int a=50 , b=10 , mul, div;

function(a , b , &mul , &div);

printf(“%d*%d=%d¥n",a,b,mul); printf(“%d/%d=%d¥n",a,b,div); return 0;} 50*10=500 50/10=5 続行するには何かキーを

(80)

 ひとつの関数内でふたつの整数の 和・差・積・商を計算してmain関数内で 結果を表示するプログラム。  ただし、a>b で b≠0 とする。 Input Num >> 6 3 和:9 差:3 積:18 商:2 続行するには何かキーを押してください . . .

(81)

#include <stdio.h>

void func(int a , int b , int *sum , int *sub , int *mul , int *div){ *sum = a+b; *sub = a-b; *mul = a*b; *div = a/b; return; } int main(){ int a,b,sum,sub,mul,div; printf("Input Num >> "); scanf(“%d %d",&a,&b); func(a,b,&sum,&sub,&mul,&div); printf("和:%d¥n差:%d¥n積:%d¥n商:%d¥n",sum,sub,mul,div); return 0; }

(82)
(83)

 配列もポインタ渡しを使って受け渡すこ とができる。  呼び出し側で配列の先頭アドレスを渡す。  関数側ではポインタ引数にアドレスをも らい、配列とみなす。  配列の大きさは受け渡されない。

(84)

void function(int array[]){ array[1] = 50; return; } int main(){ int array[3] = {0,1,2}; function(array); printf("[0]=%d , [1]=%d , [2]=%d¥n" ,array[0],array[1],array[2]); return 0; } [0]=0 , [1]=50 , [2]=2 続行するには何かキーを ← int *array と同じ意味。 ← 配列の2番目の要素に50を代入。 * (array+1) = 50 と同じ意味。 配列の先頭アドレス。 ← &array[0] と同じ。

(85)

void function(int []); int main(){

~省略~ }

void function(int array[]){ array[1] = 50;

return; }

(86)

 実際に配列が渡されているのではなく、 配列の先頭アドレスが渡されているだけ。  値がコピーされる値渡しではない。  関数内で配列の要素の値を変更すると、 呼び出し側の配列の要素の値も変更され る。

(87)

 関数に2つの文字列を渡し、その関数内

で最初の文字列に結合し、main関数内で 結合した文字列を表示するプログラム。

Input String_A >> aaa Input String_B >> bbbb aaabbbb

(88)

void func(char str_a[] , char str_b[]){ int i,j;

for(i=0 ; str_a[i] != '¥0' ; i++);

for(j=0 ; str_b[j] != '¥0' ; i++,j++){ str_a[i] = str_b[j]; } str_a[i] = '¥0'; return;} int main(){ char str_a[256],str_b[256]; printf("Input String_A >> "); scanf("%s",str_a); printf("Input String_B >> "); scanf("%s",str_b); func(str_a,str_b); printf("%s¥n",str_a); return 0;}

char *str_a , char *str_b と同じ意味。

*(str_a+i) != ‘¥0’ と同じ意味。

*(str_b+j) != ‘¥0’ と同じ意味。

*(str_a+i) = *(str_b+j); と同じ意味。

*(str_a+i) = ‘¥0’ と同じ意味。

(89)

参照

関連したドキュメント

編﹁新しき命﹂の最後の一節である︒この作品は弥生子が次男︵茂吉

ここで, C ijkl は弾性定数テンソルと呼ばれるものであり,以下の対称性を持つ.... (20)

、肩 かた 深 ふかさ を掛け合わせて、ある定数で 割り、積石数を算出する近似計算法が 使われるようになりました。この定数は船

・蹴り糸の高さを 40cm 以上に設定する ことで、ウリ坊 ※ やタヌキ等の中型動物

本論文での分析は、叙述関係の Subject であれば、 Predicate に対して分配される ことが可能というものである。そして o

つまり、p 型の語が p 型の語を修飾するという関係になっている。しかし、p 型の語同士の Merge

本プログラム受講生が新しい価値観を持つことができ、自身の今後進むべき道の一助になることを心から願って

に至ったことである︒