東京工芸大学 工学部  電子機械学科 2年 前期 

基礎プログラミング 第8回
 2017.5.30
藤木 文彦

fujiki.web.fc2.co.jp/t-kougei/kisoprog/
http://fujiki.tv/t-kougei/kisoprog/
fujiki.kougei@gmail.com  

 



 1.引数を持たない関数呼び出し

 一番簡単な関数の例として、単純に出力を出すだけのものを作って見ます。
関数の部分は、

int _tmain( )
の前に書かないとなりません。

  (例題1) 

Project0801

void line(){
printf("==================================\n");
}


int _tmain(int argc, _TCHAR* argv[])
{
for (int i = 0; i < 5; i++){
line();
printf("%d\n", i);
line();
}
getchar();
return 0;
}


 【演習問題1】 

 下記のような出力となるように、関数  line2( );

を追加し、実行して見なさい。




 2.単純に渡された数値を出力するプログラム

 渡された数値を力を出力する関数を作って見ます。
関数の部分は、

int _tmain( )
の前に書かないとなりません。


ここで、関数には、 myout( i )
のように、変数 i の値を渡して居ますが、関数の側では、 myout( int x )
のように、変数 x として受け取って使っています。受け取る側ではどのような名前の変数で受け取ってもかまいません。
 ただし、変数の型が一致しないとなりません。

また、関数の内部で使われている x と、関数を呼び出す前に宣言されていた x は別のものとして使われています。
そのため、main の一番初めに宣言された x=100 の値は、関数の中では変更されません。

  (例題2) 

Project0802


void myout( int x ){
int a=22;
printf("============= %d ===== %d ================\n",a,x );
}

int _tmain(int argc, _TCHAR* argv[])
{
int a = 100;
for (int i = 0; i < 5; i++){
myout(i);
printf("%d\n", a);
}
getchar();
return 0;
}



 【演習問題2】 

 下記のような出力となるように、関数  myout( );

を変更し、実行して見なさい。




 3.渡された値を入れるアドレスは関数内では別になる


  (例題3) 

Project0803


void myout2( int a ){
printf("アドレス %x : 値 %d \n",&a,a);
}


int _tmain(int argc, _TCHAR* argv[])
{
for (int i = 0; i < 10; i++){
printf("アドレス %x : 値 %d \n", &i, i);
myout2(i);
printf("\n");
}
getchar();
return 0;
}


 【演習問題3】 

 関数  myout2( );  に、次のように書き加えて、関数内で、 a の値を変更するとどうなるか、実行してみなさい。

 void myout2( int a ){
printf("アドレス %x : 値 %d \n",&a,a);
a++;
printf("アドレス %x : 値 %d \n",&a,a);
}


4.関数への値のアドレス渡し


  (例題4) 

Project0804

void myout3( int *a ){
printf("アドレス %x : 値 %d \n",a,*a);
}


int _tmain(int argc, _TCHAR* argv[])
{
for (int i = 0; i < 10; i++){
printf("アドレス %x : 値 %d \n", &i, i);
myout3(&i);
printf("\n");
}
getchar();
return 0;
}


 【演習問題4】 

関数 myout3( ) の内部を次のように変更して実行して見なさい。繰り返しの回数に変化があったかどうかを確認しなさい

 void myout2( int a ){
printf("アドレス %x : 値 %d \n",a,*a);
(*a)++;
printf("アドレス %x : 値 %d \n",a,*a);
}



5.関数プロトタイプ宣言


 関数は、main で使う前に、内容を書いておかないとなりませんが、これだと、mainが後回しになって分かりにくいので、次のように、あらかじめ、こういう形の関数を使うよ、と宣言しておいてから、あとでその内容を書く、という形式を使うことができます。
 これを、「プロトタイプ宣言」といいます。

  (例題5) 

Project0805


void nibai(int *a);

int _tmain(int argc, _TCHAR* argv[])
{
int x = 100;
for (int i = 0; i < 10; i++){
int x = i;
printf("関数の前 %d \n", x);
nibai(&x);
printf("関数の後 %d \n", x);
printf("\n");
}
getchar();
return 0;
}


void nibai(int *a){
(*a) *= 2;
// ( ) は無くてもかまわないが、誤解しないように付けた。
}


}


 【演習問題5】 

関数 nibai( ) を nijyou( ) というものに書き換え、 渡された数を、 2乗して返すものとして実行して見なさい。


6.関数から値を持ち帰る return


 今までの関数は、処理した値を返すために、アドレスで値の受け渡しをしましたが、結果が1つだけであるときは、値を関数の値として持ち帰ることができます。そのときは、「関数自体の型」を宣言する必要があるので、  int sanbai( ); のように宣言する必要があります。
 関数内部での計算結果は、関数内で、 return を用いて返します。

  (例題6) 

Project0806

int sanbai(int c);
// ここの int c の c という部分は何でもかまわない。以下で使う変数の名前と違っても良い。

int _tmain(int argc, _TCHAR* argv[])
{
int s = 100;
int t = 200;
for (int i = 0; i < 10; i++){
int s = i;
printf("関数の前 %d \n", s);
t=sanbai(s);
printf("関数の後 %d \n", t);
printf("\n");
}
getchar();
return 0;
}


int sanbai(int b){
b *= 3;
return b;
}


 【演習問題6】 

関数 sanbai( ) を sandewaru( ) というものに書き換え、 渡された数を、 3分の一にして返すものとして実行して見なさい。
なお、この場合、関数の型を int ではなく、 float と宣言する必要がある。また、関数内外で使用する変数も、必要に応じて float にする必要がある。

何箇所も修正しないとならないので、気をつけて変更して実行して見なさい。


  7.戻り値が2つ以上ある関数

 

  (例題7) 

Project0807

// 渡された数が奇数なら 100倍して返す関数。 return が2つある例
#include "stdafx.h"

int kiguu(int x);

int _tmain(int argc, _TCHAR* argv[])
{
int amin=0, amax=10;
int kekka=0;
for( int i=amin; i<amax; i++){
kekka=kiguu(i);
printf("%d : %d\n",i,kekka);
}
getchar();
return 0;
}

int kiguu( int x){
int y;
y = x/2; // これで、少数以下が切り捨てられる
y *= 2;
if( x == y ){
//偶数
return x;
}else{

//奇数
return x*100;
}
}



実行例




 

 【演習問題7】 

渡された数値が、

偶数なら2で割った数を

奇数なら3倍して、1足した数を

返す関数を作り、上記と同じように、0から9までの数値について出力しなさい。

 

 

 【応用演習問題7】 

 Project0807a

ある値を入力し、その値について、次のように操作します。

偶数なら2で割る

奇数なら3倍して、1足す

結果を表示する。

そうしてできた新しい数について、上記と同じ操作を繰り返し、1になったら終了する。

10回繰り返しても終了しなかったら、その時の数を表示して終了する。

このようなプログラムを作りなさい。


  8.関数への配列の渡し方

関数に配列を渡す方法は、少し特徴的な書き方をします。

 

まず、呼び出す側では、関数に、配列の先頭アドレスを渡します。

そこで、次のように書きます。配列を m[ ] とします。

func( int &m[0] );

ところで、配列の名前は、配列の先頭アドレスを示すものでした。そこで、次のような書き方ができます。

 

func( int m );

これだけだと、渡すものが、一般の数値なのか、配列の名前なのかわかりにくいのですが、このような書き方がよく使われますので、覚えておいてください。

 

また、受ける側の関数でも、書き方に特徴があります。

配列を受けるときには、「int 型 配列へのポインタを受け取るのだ」ということで、次のような書き方をします。

func( int a[ ] ) { }

 

 

  (例題8) 

Project0808


#include "stdafx.h"

#define MAX 10

void disp_ary( int size ,int m[]);

int _tmain(int argc, _TCHAR* argv[])
{
int m[MAX];
for(int i=0; i<MAX; i++){
m[i]=i+100;
}
disp_ary(MAX, m );
// ここの m は、 &m[0] と書いてもよい

getchar();
return 0;
}

void disp_ary( int size, int a[]){
// a[ ] の中身は空白にすること。
for(int i=0; i<size; i++){
printf(" %d : ",a[i]);
}
printf("\n");
}




 


 

 【演習問題8】 

上記のプログラムに、受け取った配列の順序を逆順にして返す関数 rev_ary( );

を作成して追加し、もとの配列と逆順にした配列を表示するプログラムを作りなさい。

 

 

 【応用演習問題8】 

 Project0808a

上のプログラムを改良し、初期値の入力を、キーボードから行うようにしなさい。(入力されるのは、扱える範囲の整数値とし、入力エラーは考えなくてよいとします。)


  9.関数への文字列の渡し方

関数に文字列を渡すのにも書き方に工夫が必要です。

まず、呼び出す側では、関数に、文字列の先頭アドレスを渡します。

文字列が、あらかじめポインタ型として、

char *s="---";

のように宣言されている場合には、 s が、この文字列の先頭アドレスですので、関数にアドレスを渡すのですから、

func( s );

のようになります。ただ、このように、あらかじめ内容の書かれている文字列は変更できませんから、次のように配列を宣言して使う場合も考える必要があります。

文字列が、配列として、次のように宣言されているときは、

char a[100];

配列の名前が、その配列の先頭のアドレスへのポインタですから、

 

func( a );

 

となります。

 どちらの場合も受け取る関数の側では、受け取ったものが 「文字列へのポインタ」であると考えますので、

 

void func( char *s ){ }

 

のように受け取り、内部では、配列として処理するのが一般的です。

 

 

  (例題9) 

Project0809

#include "stdafx.h"
#include "iostream"
#include "string"
using namespace std;

int count_char( char *s );

int _tmain(int argc, _TCHAR* argv[])
{
char *s="abcde" ;
int c;
c = count_char( s );
printf("文字列:%s\n",s);
printf("文字列の長さは %d\n",c );

// 下記にはエラーになる場所があるので修正実行すること
// 止まらなくなったら、「デバッグ」−>「終了」
// char ss[1000];
// printf("1000文字以内の文字列を入力してください。\n");
// cin >> s;
// c = count_char( s );
// printf("文字列:%s\n",s);
// printf("文字列の長さは %d\n",c );
// getchar();


getchar();
return 0;
}

int count_char( char *s ){
int num=0;

while( s[num]!='\0'){
num++;
}
return num;
}

 

 

 【演習問題9】 

上記のプログラムの // の部分を外して、キーボードから入力した文字列の長さを返すようにしなさい。なお、上記プログラムのコメント部分は、まだ未完成であり、このまま実行するとエラーになって、止まらなくなる。 コンパイル時にエラーにならずに、実行時にエラーになる、という、厄介なものである。 エラーになるのはどこか、実行前によく考えて、上記プログラムの一部を修正して、実行してみなさい。

実行例

 

 

 【応用演習問題9】 

 Project0809a

上のプログラムを改良し、入力された文字の中に、 "a" の文字が何文字含まれているかをカウントして出力するようにしなさい。


 

 

 

 この下は、余裕のある人だけやって下さい。                              

 

  11.バブルソート


これ以下は、次回行う予定のソーティングプログラムです。あらかじめプロジェクトとして入力しておきましょう。
メールで送らなくてかまいません。(次回送ってもらう。)

Project0901

 #include "stdafx.h"

void bubSort(int num[], int n);
void printData(int num[], int n);

#define MAX 10
int x[] = { 9, 4, 6, 2, 1, 8, 0, 3, 7, 5 };

int _tmain(int argc, _TCHAR* argv[])
{
printf("Before:\n");
printData(x, MAX);
printf("\n");

bubSort(x, MAX);

printf("After:\n");
printData(x, MAX);
printf("\n");

getchar();
return 0;
}


void printData(int x[], int n)
{
int i;

for (i = 0; i < n; i++)
printf("%d\t", x[i]);
printf("\n");
}

void bubSort(int x[], int n)
{
int i, j, temp;

for (i = 0; i < n - 1; i++) {
for (j = n - 1; j > i; j--) {
if (x[j - 1] > x[j]) {
temp = x[j];

x[j - 1] = temp;
}
}
printData(x, MAX);
}
}



実行結果の表示が次のように出れば成功です。


Before:
9 4 6 2 1 8 0 3 7 5
0 9 4 6 2 1 8 3 5 7
0 1 9 4 6 2 3 8 5 7
0 1 2 9 4 6 3 5 8 7
0 1 2 3 9 4 6 5 7 8
0 1 2 3 4 9 5 6 7 8
0 1 2 3 4 5 9 6 7 8
0 1 2 3 4 5 6 9 7 8
0 1 2 3 4 5 6 7 9 8
0 1 2 3 4 5 6 7 8 9
After:
0 1 2 3 4 5 6 7 8 9


  12.単純挿入ソート

Project0902


//この下は、オリジナルC言語用に書かれたプログラムです。若干改変しないと動きません。自分で書き直して見ましょう。


#include "stdafx.h"

#define MAX 8

void InsSort(int num[], int n);
void PrintData(int num[], int n);


void InsSort(int num[], int n)
{
int i, j, temp;

for (i = 1; i < n; i++) {

temp = num[i];

for (j = i; j > 0 && num[j - 1] > temp; j--)

num[j] = num[j - 1];

num[j] = temp;
PrintData(num, MAX);
}
}


void PrintData(int num[], int n)
{
while (n--)
printf("%d ", *num++);
printf("\n");
}

int _tmain(int argc, _TCHAR* argv[])
{

int num[] = { 3, 7, 1, 5, 4, 2, 6, 0 };


printf("Before:\n");
PrintData(num, MAX);
printf("\n");


InsSort(num, MAX);
printf("\n");


printf("After:\n");
PrintData(num, MAX);
printf("\n");

getchar();
return 0;

}


実行結果の表示が次のように出れば成功です。


Before:
3 7 1 5 4 2 6 0

3 7 1 5 4 2 6 0
1 3 7 5 4 2 6 0
1 3 5 7 4 2 6 0
1 3 4 5 7 2 6 0
1 2 3 4 5 7 6 0
1 2 3 4 5 6 7 0
0 1 2 3 4 5 6 7

After:
0 1 2 3 4 5 6 7


3.マージソート

Project0903



//この下は、オリジナルC言語用に書かれたプログラムです。若干改変しないと動きません。自分で書き直して見ましょう。


 #include <stdio.h>

#define MAX_DATA 10

int temp[MAX_DATA];

void MergeSort(int x[ ], int left, int right);
void main(void);

void MergeSort(int x[ ], int left, int right)
{
int mid, i, j, k;

if (left >= right)
return;
mid = (left + right) / 2;
MergeSort(x, left, mid);
MergeSort(x, mid + 1, right);

for (i = left; i <= mid; i++)
temp[i] = x[i];

for (i = mid + 1, j = right; i <= right; i++, j--)
temp[i] = x[j];

i = left;
j = right;

for (k = left; k <= right; k++)
if (temp[i] <= temp[j])
x[k] = temp[i++];
else
x[k] = temp[j--];
}

void main(void)
{
int i;
int x[ ] = {9, 4, 6, 2, 1, 8, 0, 3, 7, 5};

printf("Before:\n");
for (i = 0; i < MAX_DATA; i++)
printf("%d\t", x[i]);

MergeSort(x, 0, MAX_DATA - 1);

printf("After:\n");
for (i = 0; i < MAX_DATA; i++)
printf("%d\t", x[i]);
printf("\n");
}


実行結果が次のようになれば成功です


Before:
9 4 6 2 1 8 0 3 7 5
After:
0 1 2 3 4 5 6 7 8 9



4.クイックソート

Project0904



//この下は、オリジナルC言語用に書かれたプログラムです。若干改変しないと動きません。自分で書き直して見ましょう。


 #include <stdio.h>

void QSort(int x[ ], int left, int right);
void Swap(int x[ ], int i, int j);
void PrintData(int x[ ], int n);
void main(void);

void QSort(int x[ ], int left, int right)
{
int i, j;
int pivot;

i = left;
j = right;

pivot = x[(left + right) / 2];

while (1) {

while (x[i] < pivot)
i++;

while (pivot < x[j])
j--;
if (i >= j)
break;

Swap(x, i, j);
i++;
j--;
}
PrintData(x, 10);

if (left < i - 1)
QSort(x, left, i - 1);
if (j + 1 < right)
QSort(x, j + 1, right);
}

void Swap(int x[ ], int i, int j)
{
int temp;

temp = x[i];
x[i] = x[j];
x[j] = temp;
}


void PrintData(int x[ ], int n)
{
int i;

for (i = 0; i < n ; i++)
printf("%d ", x[i]);
printf("\n");
}

void main(void)
{
int x[ ] = {6, 3, 1, 7, 0, 4, 8, 5, 2, 9};
int n = 10;

printf("Before:\n");
PrintData(x, n);

printf("Progress:\n");
QSort(x, 0, n - 1);

printf("After:\n");
PrintData(x, n);
}






Before:
6 3 1 7 0 4 8 5 2 9
Progress:
0 3 1 7 6 4 8 5 2 9
0 3 1 2 4 6 8 5 7 9
0 1 3 2 4 6 8 5 7 9
0 1 2 3 4 6 8 5 7 9
0 1 2 3 4 6 8 5 7 9
0 1 2 3 4 5 8 6 7 9
0 1 2 3 4 5 6 8 7 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
After:
0 1 2 3 4 5 6 7 8 9








※翌年への覚え書き

 変数を渡して帰らない例題
 リターンで返すもの。
 変数のスコープ
 受け取り側で名前を変えて良いこと。
 アドレスで渡すこと。

の例題を作ること。