読者です 読者をやめる 読者になる 読者になる

joho1-2016の日記

情報処理実習1の解説ブログです.皆さんが課題を解く時の助けになれば幸いです.

第12回 構造体と共用体

以下,5組の課題のヒントです.

5組

 各課題のヒントの前に,学生の皆さんがプログラムを書いている姿を見て気になった事があるので,それについて言及したいと思います.
あくまで個人的な意見ですので,少し長くC言語プログラムに触れている先輩からのアドバイス程度に捉えて下さい.

皆さんが課題を解くとき,以下の手順でやっていくと思います.

  1. プログラムを書き,保存
  2. コンパイル
  3. 実行

このうち,問題を1問解くときに1~3の手順を何回繰り返していますか?
本講義が始まって間もない頃は,1つのプログラムで書くコードはシンプルで短いものが多かったので,上記手順を1回だけやって終わったと思います.しかし,最近はどうでしょう?1問につき,複数個の課題(例えば,ファイルを開いて,表示して,文字を種類毎にカウントする,等)がありますよね.このような問題を解くのに,一気にプログラムを書いてしまってから,コンパイルと実行が1回だけで果たして大丈夫でしょうか?もちろん,それでコンパイルが通り,実行結果もうまく表示されているのなら何も言うことはありません.
しかし,もしコンパイルエラーが起こったらどうでしょう?コンパイルは通ったものの,実行結果が期待したものでは無かった場合はどうでしょう?
一体プログラムコードのどこに間違い(バグ)があるのか,見つけられますか?

このような事態にならないようにするために,皆さんにはコンパイルと実行はこまめに行って欲しいです.
今回の課題1を例に考えてみます.

1.このファイル document.txt に書かれた文章を,まずそのまま画面に表示し, その後に,同文章を構成するアルファベット,数字,記号の個数を表示せよ. なお,英文字は大文字と小文字は区別せずにカウントすること.

//実行例
Ubuntu is a Debian-based Linux operating system,
with Unity as its default desktop environment.
It is based on free software and named after
the Southern African philosophy of ubuntu (literally, "human-ness"),
which often is translated as "humanity towards others" or
"the belief in a universal bond of sharing that connects all humanity".
...........


a は 153 個ありました.
b は  37 個ありました.
c は  76 個ありました.
d は  62 個ありました.
e は 217 個ありました.
...........

この課題は,問題文を読んだだけでも,

  • ファイルの中身を画面に表示
  • 文章を構成するアルファベット,数字,記号の個数を表示

の2つがやるべき事としてありますね.こういった場合,まずはファイルを読み込んで表示するところまでコードを書き終えたら,コンパイルをして実行してみます.ここまでうまく実行できている事が確認できれば,もし後半の個数を表示するところまで書き終えてコンパイル・実行した時にうまくいかなくても,前半部分がうまく機能している事は確認できているので,ダメな部分は後半書いたコードのどこかに絞れるわけです.このようにこまめにコンパイル・実行をする事で,一見無駄に時間をかけているように思えても,結果的には効率的にプログラムコーディングが出来るのです.騙されたと思って,ぜひ一度試してみてください!

 では,課題のヒントに入ります.

課題1の問題文は上で記述しているので,省略します.この課題のポイントは,
 ①ファイルの中身を読み込んだ際の処理
 ②アルファベット,数字,記号のカウント方法
です.①に関しては,もし表示するだけならfgetc( )やfgets( ),fscanf( )をそのままprintf関数で表示するだけでいいですが,今回は文章中の文字についてカウントする処理が②にあります.なので,文字列(char型の配列)に代入しておくのが良いでしょう.
②に関しては,①で文章を格納した文字列に対して,繰り返し処理でアルファベット,数字,記号のそれぞれどれに属するのかを1文字ずつ条件判定させていき,個数をカウントする変数をprintf関数で表示させると,この問題は解決です.

2.以下のリンクからテキストファイルinputdata.txtをダウンロードせよ.
このファイルを読み込み,一行ごとの数値(5個のデータ)の平均値を行の最後に追加し,別のファイルに書き込むプログラムを作成せよ.
出力するファイル名は,outputdata.txt とする.

【ヒント】上記ファイルには,1行に5個の数値データが格納されている.今回は,データ個数が事前に分かっているとしてよい.
【ヒント】最初に画面に表示するようにプログラムをつくり,うまく出来るようになったら同じ内容をファイルに書くようにすれば比較的短時間でできる.

この課題のポイントは,
 ①ファイルの中身を読み込んだ際の処理
 ②代入する配列のサイズ
です.課題1と同様,読み込んだファイルの中身をただ表示するわけではないので,①に関して,配列に代入した方が良いでしょう.
②に関して,用意する配列の要素数はどうするべきでしょう?ヒントの通り,1行5個のデータ個数だから,要素数も5個で良いでしょうか?問題文を見ると,「一行ごとの数値の平均値を行の最後に追加」とありますね.それなら,初めから6個分(読み込むデータ個数+1個)の要素数の配列を宣言するのが良いかと思います.

3.このファイルは test_result.csv 内には,各行に生徒の名前と3科目のテストの点数がカンマ( , )区切りで記述されている.
このファイルの中身を読み取り,各生徒の合計点数を算出してその値が大きい順に並べてファイル test_rank.csv に出力するプログラムを作成せよ.
ヒント:fscanf関数を用いて1行分のデータを読み込むには,ファイル内のデータに合わせて書式指定文字列に,"%s,%d,%d,%d" を使用すればよい. (test_rank.csvの各行に,名前と合計点をカンマ区切りで記し,最後の行に平均点を追加すること.)

//出力ファイルの中身
Yuma,285
Alicia,276
Clarice,274
Jean,273
《中略》
Flora,214
Elena,206
Kate,165
average,247.13

この課題のポイントは,
 ①fscanf関数を使う際の代入する配列
 ②合計点数の大きい順に並び替え
です.課題1,2でも処理したように,この問題でもやはりファイルの中身を読み込む時に代入する配列を用意するのが適当です.しかし,今回は1列目が文字列,2列目以降は整数と,型が違うため,配列1つでは足りません.それぞれに対応した配列を用意しましょう.②に関して,これまでも並び替えを行うswap関数を自作したと思います.そのため,詳しい処理は省きますが,並び変えるのは名前の配列と合計点の配列の2つがあるので注意しましょう.

4.ファイル lyrics.txt を読み込み,"*iller"が含まれる行全体だけを抜き出して表示するプログラムを作成せよ(*は任意の文字).

//出力の例
'Cause this is thriller, thriller night
You know it's thriller, thriller night
You're fighting for your life inside a killer, thriller tonight
・・・

この課題のポイントは,
 ①"*iller"を含む行の判定
です.この課題は行単位で判断していけばいいので,読み込みにはfgets( )を使えば良いと思います.そして①に関して,1文字ずつ判定するのも悪くはないのですが,どうせなら文字列処理関数の中の検索できる関数を使いましょう!どんな関数があるのかは,「C言語 文字列処理関数」で検索してみよう.

第11回 ポインタ2

5組
今回の課題を解くにあたって,おそらく学生の皆さんが理解しにくい,あるいは混乱しやすいと思うことをまずは解説したいと思います.
今週の講義のメインであるポインタは,アドレス(住所)を表す変数です.今まで皆さんが扱ってきたintやfloat,charといった変数はコンパイル時に自動的にPCメモリのどこか(その場所のことをアドレスと言う)に値が格納されます.
ポインタはこのアドレスを値として持つ変数ということです.
つまり,

char  c='Z';    //文字型変数
char* p;        //変数pをポインタ変数として宣言
p = &c;         //変数cのアドレスをpに代入
p*;             //pのアドレス内に格納された値

というように,ポインタ変数pそのものが示すのはアドレス,そしてpのアドレスに格納された中身を示すのは*pであるということに注意が必要です.以上の知識を持って課題に取り組んでみましょう!

1.以下のルールに沿ってある文字列(「TCznemhs!^lsngehJmAjr^UEbdadfkp 」)を解読しその答えを表示するプログラムを作成しなさい.

  • main文は変更してはならない.
  • 関数 decode を作成する.
  • 関数 decode は受け取った文字列の偶数番目の文字のみ文字コードをインクリメントして表示する.
void decode(const char *s)
{
    /* ここを作成 */
}

int main(void)
{

    decode("TCznemhs!^lsngehJmAjr^UEbdadfkp ");

    return 0;

}
//実行結果
Dont_think_Feel!

この課題のポイントは,
 ①文字列の偶数番目の認識
 ②文字のインクリメント
です.文字列をdecode関数でみていくので,繰り返し処理をすると思います.その時には1文字ずつ見ていくので,現在のループ処理で何文字目を見ているのかを知るためには,カウンタ変数を使えば分かりますよね.
何ループ目の処理か=何文字目の解読か,と考えると,カウンタ変数の値自体が文字数に対応しますね!
②は,前回までの課題をしっかりやってきた皆さんなら文字自体のインクリメントは+1すればいいだけなのはわかりますよね.ここで注意するのは,ポインタ変数で示されたアドレス内の値に対してのインクリメント処理であることです.以下の2つに対して,実際にコンパイルして違いを確認してみましょう.

*(p++);
(*p)++;

2.入力した文字列の中から英数字以外の文字(記号)のみを表示する関数search_symbolsを追加し,プログラムを完成せよ.
配列の要素を示す括弧( [ ] ) は,下記の配列定義のとき以外で使用しないこと.

void search_symbols(char* p)
{
...
}

void input(char* p)
{
    printf("文字列を入力: ");
    scanf("%[^\n]", p);
}

int main(void)
{
    char str[100];

    input(str);
    search_symbols(str);

    return 0;
}
//実行例
文字列を入力: C++ & 'Oh-o!' "Meiji!" No.1;
記号: ++ & '-!' "!" .;

この課題のポイントは,
 ①英数字以外の探索
 ②次の文字のアドレスへのアクセス方法
です.問題の最終文の意味は,文字の探索の際に配列を使って探索するのではなく,ポインタを使って探索するという意味です.そのため,search_symbols( )の中で配列を扱うのはNGです.
①は,アスキーコード表から対応するもの以外の場合を表示するようにすれば,簡単に表示はできます.
②に関してですが,皆さんは,配列は隣り合った要素はアドレスも隣り合っていることを覚えていますか?(全く知らないぞ!という人,先生は確かに授業中に教えていましたよ!もう一度配列の講義ページを復習しましょう!)
つまり,次の文字のアドレスへアクセスしたければ,現在のポインタに+1すればアクセスできます.

3.整数値を3つキーボードより入力して配列に収め,それぞれを3乗して返す関数 cubic を作りなさい.
ただし関数 cubic の戻り値の型は void 型とし,N = 3 以外でも正しく動作すること.
すなわち,下記でNの宣言時に値を3以外に変更してもそのまま動作するようにすること.

void cubic(....)
{
    //ここを作成
}
                
int main(void)
{
    const int N = 3;
    int data[N];
                
    printf("整数値を %d 個入力して下さい\n", N);

    for(int i=0; i<N; i++){
        printf("data[%d]:", i);
        scanf("%d", &data[i]);
    }

    cubic(data, N);    /* 3乗の計算 */

    printf("計算結果\n");

    for(int i=0; i<N; i++) {
        printf("data[%d]:%d\n", i, data[i]);
    }

    return 0;
}
実行例:

整数値を 3 個入力して下さい
data[0]:2
data[1]:3
data[2]:4
計算結果
data[0]:8
data[1]:27
data[2]:64

この課題のポイントは,
 ①関数への引数
です.この課題は,実はポインタを使わずにプログラムできます.
ここでは,配列をそのまま引数として渡し,cubic関数内では配列を計算すればいいわけです.

4.入力された文字列を全て小文字に変換するmy_tolower関数を作成せよ.
ライブラリ関数(string.h内にある)tolower関数を使用しないこと.

void my_tolower(char* p)
{
    /* ここを作成 */
}

int main(void)
{
    char str[100];

    printf("文字列を入力:");
    scanf("%[^\n]", str);

    printf("変換前:%s\n", str);

    my_tolower(str);

    printf("変換後:%s\n", str);

    return 0;
}
実行例
文字列を入力:ABCdef12345!
変換前:ABCdef12345!
変換後:abcdef12345!

この課題のポイントは,
 ①大文字の認識
 ②次の文字のアドレスへのアクセス方法
です.①は「第9回 中間テスト」の記事で同じような問題の考え方のヒントを書いています.それを参考にしましょう.
②は,本記事の4.の②で考えたのと同じ方法でアクセスできるので,ここでは省略します.

5.2つの文字列をキーボードからそれぞれ str1 と str2 に入力し,

  • str1 からは大文字のみを抜き出し(find_capital関数を作成)
  • str2 からは小文字のみを抜き出し(find_small関数を作成)
  • それぞれを結合したものを文字列変数 str に順に結合し,

最後に str を表示するプログラムを作成せよ.
但し,配列へのアクセスは必ずポインタを用いること.
また,string.h に用意されたライブラリ関数を使用しないこと.

... find_capital(...)
{
    ...
}

... find_small(...)
{
...
}

void input(char* str)
{
    printf("文字列入力:");
    scanf("%[^\n]%*c",str);
}
                    
int main(void)
{
    const int N = 100;
    char str1[N], str2[N], str[2*N]; // 空の文字列を用意.

    input(str1);
    input(str2);
                    
    // ... ここで自作の関数を呼に出す.
                    
    printf("===> %s\n", str);
}
//実行例
str1入力:kaMjdEIsjfuaJusfhIjauxx
str2入力:KmeJACBAiVOADAKjACYBiAQ
===> MEIJImeiji

この課題のポイントは,
 ①大文字,小文字の認識
 ②各関数の型および引数
です.①おなじみアスキーコード表の大文字,小文字に対応する値の時にのみ処理をおこなえば良いだけです.
②は,まず①の処理を行うために,input( )で代入した文字列を格納した配列が引数で必要ですね.そして,①で認識した文字を格納する配列も必要です.つまり,各関数には最低2つの引数が必要です.そして,各関数の目的は配列への文字の格納です.つまり,返り値は必要ありません.ということは,関数の型はもう何を選択すればいいかわかりますね.

第10回 ポインタ1

更新が遅くなってしまって大変申し訳ありません.

今回の授業では,ポインタを扱いました.
更新が遅くなってしまったので,取り急ぎポインタのイメージだけ話して,課題の解説にはいりたいと思います.

ポインタとは,簡単にまとめると「変数等のメモリアドレスのこと」を言います.
イメージは,ホテルや旅館等の部屋番号がポインタ(アドレス)でその部屋の中身(お客さんなど)が変数の数字だと思ってください.

例えば,今の例を下のようなソースコードに当てはめて考えると,

int a = 0;    //int型の変数aを定義
int *p = &a;    //ポインタ変数を定義

と宣言した場合は,ある旅館で「0」さんが「int」というグレードの「&a(仲居さん達には「*p」の部屋と呼ばれている)」号室の部屋を「a」という名前で予約した.というイメージです.かなり乱暴ですがこのようなイメージを持っているとポインタについて理解しやすいと思います.
その他,授業で取り扱った内容は細かいテクニック的な部分が多いので,おいおい覚えて行くようにしてください.

では,課題の解説を行います.
1. 以下のルールに沿ってある文字列(「TCznemhs!^lsngehJmAjr^UEbdadfkp 」)を解読しその答えを表示するプログラムを作成しなさい.
main文は変更してはならない.
void型の関数decode用いること.
関数decodeは受け取った文字列の偶数番目の文字のみ文字コードをインクリメントして表示する.

void decode(const char *s){ 
    //ここを作成 
} 

int main(void){ 
    decode("TCznemhs!^lsngehJmAjr^UEbdadfkp "); 
}
//実行結果 
Dont_think_Feel!

この問題を解くポイントは,

  1. 偶数番目をどのように判定するか?
  2. 文字コードのインクリメントはどのようにやるか?

の2点です.
まず,偶数番目の判定は「2で割ったあまりに注目して判定すると良いでしょう」.
文字コードのインクリメントに関しては,例えば

char str[] = "meiji";
char *p = str;

のようにある文字列strがポインタ変数pを用いて表現できるとき,

*p++;
printf("%c", *p);

(*p)++;
printf("%c", *p);

の違いについて理解していれば解けるかと思います.


2. 入力した文字列の中から英数字以外の文字(記号)のみを表示するプログラムを作成せよ. 但し,配列の要素を示す括弧( [ ] ) は定義のとき以外で使用しないこと.

void input(char* st){ 
    printf("文字列を入力: "); 
    scanf("%[^\n]",st); 
} 

int main(void)
{
    char str[100];
    input(str);
    search_symbols(str); 
    return 0;
}
//実行例 
文字列を入力: C++ & 'Oh-o!' "Meiji!" No.1; 
記号: ++ & '-!' "!" .;

この問題のポイントは,

  1. 文字列を配列として扱わないこと
  2. 文字とそれ以外の記号をどのように区別するか

という2点です.
まず,文字列を配列として扱わないことに関してですが,これは「ポインタ変数」を利用することで解決できます.
次に,文字列とそれ以外の記号の区別の仕方ですが,これはアスキーコードを利用しましょう.

アスキーコードでは,大文字が16進数において「0x41から0x5a」に該当し,小文字が「0x61から0x7a」に該当します.この範囲外の文字を表示すれば問題は解けるはずです.


3. 整数値を3つキーボードより入力して配列に収め, それぞれを3乗して返す関数 cubic を作りなさい. ただし関数 cubic は void型とし,任意の大きさの配列を引数にとれるようにすること. main関数は変更してはならない.

void cubic(....) 
{
   //ここを作成 
} 

int main(void) 
{
    const int N = 3;
    int data[N]={0,0,0}; 

    printf("整数値を%dつ入力して下さい\n", N); 
    for(int i=0; i<N; i++){ 
        printf("data[%d]:", i); 
        scanf("%d", &data[i]); 
    } 
    
    cubic(data,N); 
    printf("計算結果\n"); 
    for(int i=0; i<N; i++){ 
        printf("data[%d]:%d\n",i, data[i]); 
    }
} 
//実行例: 
整数値を3つ入力して下さい 
data[0]:2 
data[1]:3 
data[2]:4 
計算結果 
data[0]:8 
data[1]:27 
data[2]:64

この問題のポイントは,

  1. 関数によって値を返さずにどのように元の変数の値を変更させるか?

という点だと思います.
これは,配列をポインタ変数として扱うことで元の配列の中身を直接いじることが出来るようになります.

ポインタ変数を利用して三乗を計算する方法は,単純ですが,

int list[3] = {1, 2, 3};
int *p = list;
for(int i=0;i<3;i++){
    *p = (*p) * (*p) * (*p);
    p++;
}

のようにして計算することが出来ます.この処理を上手く関数へと書き換えることで,void型の関数でも元の配列を直接いじることが出来るようになります.

更新が遅くなってしまいましたが,課題提出をがんばってください.

第9回 中間テスト

今週の6組は中間テストだったため,講義の解説はなしです.
また,課題も出ていないため,考え方のヒントは5組のみになります.

来週は5組は中間テストになります.
6組の中間テストの問題のページのURLを載せておきますので,参考にしてください.
http://www.isc.meiji.ac.jp/~re00104/mte2016.html
おおよそ同じ形式で中間テストの問題は出題されます.ただ,注意して欲しいのは,

 ①5組と6組で先生が違い,授業進度も異なるため問題が同じ事はない
 ②提出は実習課題と同様Oh-o! Meijiにテスト時間内に提出
 ③テスト時間中は授業ページとOh-o! Meiji以外のwebページは閲覧不可

という点です.今まで勉強してきたことを確かめるテストなので,ヤマをはって勉強してきても良い点は取れません.加えて,問題数が少ないため,全く解いていない問題があるというのは点数に大きく響きます.これまでの要点をしっかりとおさえておきましょう.
②に関しては,提出するまでを含めたテスト時間です.終わってから提出ではありません.テスト時間の最後の方は皆が一斉に提出するので,通信が重くなります.テスト期間中は何度でも提出できるので,こまめに提出しましょう.
③は,自分でまとめたもの(wordやpdf)は見る事が可能なので,テスト当日までに要点をまとめておきましょう.

明日6月17日(金)に5組の課題の考え方のヒントを更新します.(追記:更新済み)

5組
1.キーボードから1文字入力すると, その文字の文字コードを16進数で表示し, その文字が大文字か,小文字か,数値か,それ以外かを表示するプログラム作成しなさい.
入力は半角文字1文字のみとする.
ヒント:大文字,小文字,数値の区別は,文字コードの範囲によって判定可能.

実行例:

文字を入力して下さい: M
M の文字コードは 0x4d です.
M は大文字です.

文字を入力して下さい: 5
5 の文字コードは 0x35 です.
5 は数値です.

文字を入力して下さい: !
! の文字コードは 0x21 です.
! はそれ以外です.

この課題のポイントは,
 ①文字の16進数表示
 ②文字の分類
です.講義中に解説した通り,PC側は文字を文字として認識していません.アスキーコード表にあるように,対応する数を記憶しているだけです.つまり,①はその数を16進数表示すればいいだけであり,何か特別な関数や処理が必要なわけではありません.今まで表示するときには,printf( )を使ったと思いますが,その際,整数型なら%d,実数型なら%f,文字なら%cなど,それぞれの変換指定文字を使いますよね.つまり,文字の16進数表示も何か変換指定文字を使うだけで可能になるのです.
②はヒントにある通り,大文字,小文字,数字はアスキーコード表のある範囲内にありますね.つまり,その範囲内にあるかどうかを条件判定する事で解決しますね.

2.char型(1バイト)の引数をとり,その2進数表記(ビットパターンと言う)を表示する関数 void bitpattern1(char c) を作れ.
次に,この関数を用いて,キーボードより入力した任意の文字の16進表記および2進数表記を表示せよ.
(表示の後ろにかっこ付きで進数を表示する)

#include <stdio.h>

void bitpattern1(char c)
{

    /* ここを作成 */

}


int main(void)
{
    char c;       /* 1 文字分の変数 */

    printf("Input a char: ");
    scanf("%c", &c);      /* 1文字 入力 */

    bitpattern1(c);

    return 0;
}

この課題のポイントは,
 ①16進数と2進数の対応
です.char型は問題文の通り,1バイト(=8ビット)です.そのため,16進数なら2桁,2進数なら8桁で表現できます.2進数は0と1のみで数値を表現するため,2進数のn桁で表現出来る数のパターンは,{2^{n}}です.具体的には,1桁ならば0 or 1の2個,2桁ならば00,01,10,11の4個といったものになります.そして4桁で表現出来るのは,16個であり,16進数の0~Fまでと同じ数ですね!
8ビットは8桁なので,16進数を2つで表現するのです.そしてこれを2進数に変換するには,上記の説明の逆順を辿ればできますね.

3.以下のように「単語」を順に入力し,end と入力したら,それまで入力した単語をスペース区切りで連結し,すべて画面に表示するプログラムを作成しなさい.
「end」は連結しないとする.

実行例:
単語を入力: ABC
単語を入力: ZZZ
単語を入力: 123
単語を入力: end
全ての単語:ABC ZZZ 123

この課題のポイントは,
 ①文字列の結合
です.今回の講義で文字列を連結する関数strcatを習いましたね.つまり,これを使って入力された文字列がendではなかった場合はスペース区切りで連結していけば,課題は終了ですね.

4.入力された「単語」に含まれる大文字,小文字の数をカウントして表示するプログラムを作成せよ.

実行例:
単語を入力: AAAbbbCdeFG
大文字は 6 個
小文字は 5 個

この課題のポイントは,
 ①文字の種類判定
です.実は①は課題2の②を解いた方法と同じ方法で判別し,それをカウンタ変数で数えていくといいだけです.

5.入力された「単語」の大文字は小文字に,小文字は大文字に変換して表示せよ.

実行例:
単語を入力: AAAbbbCdeFG
変換後:aaaBBBcDEfg

この課題のポイントは,
 ①文字の種類判定
 ②大文字,小文字の変換
です.①は課題4の①と同じ方法で判定します.②に関しては,アスキーコード表を見ると,同じアルファベットの大文字と小文字は,16進数の1桁目は同じで,2桁目が2違うだけです.つまり,大文字→小文字なら+0x20,小文字→大文字なら-0x20をもとの変数に計算するだけで変換できます.

以上で5組の課題の考え方を終了します.

第8回 おさらい

今週は模擬テストだったので,授業解説はなしです.また,6組は課題もないので,5組のみ課題の考え方について記述します.

5組
1.練習問題と同様,キーボードから実数を10個入力し配列に格納し,最大値,最小値を画面に表示せよ.
ただし,最大値,最小値の計算は関数として作成すること.

/* 配列中の最大値を返す関数 */
float find_max(float data[], int n)   /* nは配列の要素数 */
{
    ...
    return ???;
}


/* 配列中の最小値を返す関数 */
float find_min(float data[], int n)   /* nは配列の要素数 */
{
    ...
    return ???;
}


int main(void)
{
    const int N = 10;
    float data[N];   /* 実数10個分の配列 */
    
    .../* データ入力 */
    for(...)
        ...
    
    printf("最大値は %f \n", find_max(data, N));
    printf("最小値は %f \n", find_min(data, N));

    return 0;
}

この課題のポイントは,
 ①データの入力方法
 ②関数内での処理方法
です.①は10個の実数を入力すればいいだけなので,上のヒントの通り繰り返し処理をすればいいでしょう.配列へのアクセス時は要素数番号は0~N-1であることにも注意しましょう.②も考え方としては①のように繰り返し処理が楽かと思います.この際,何を返り値にするのか,そこがやや考えるポイントになるかもしれませんね.

2.キーボードから正の整数を10個入力し配列に格納し,そのなかから奇数の数をカウントして画面に表示せよ.
正の整数以外が入力されたらエラーを表示して,正しい値が入力されるまで再入力させよ.
配列を受け取ると,奇数の個数を返す関数 int count_odd(int array[], int n) を必ず作成すること.

実行例:
a[0]= 10
a[1]= -3
入力エラー! 正の整数を入力
a[1]= 0
入力エラー! 正の整数を入力
a[1]= 5
a[2]= 100
a[3]= 2
  .
  .
  .
a[9]= 10

奇数は 4 個ありました.

この課題のポイントは,
 ①入力された数が正の整数か判定
 ②奇数の判定
です.①は先生が授業中にポロっと言ったように,do-while文で入力されるたびに判定すれば良いでしょう.
②は2で割り切れるか判定すれば良いだけですね.

3.前問と同様,キーボードから 2 以上の整数を10個入力し,その中の素数の個数を数え表示するプログラムを作成せよ.
配列を受け取り,素数の個数を返す関数 int count_prime(int array[], int n) を必ず作成すること.

実行例:
a[0]= 10
a[1]= 1
入力エラー! 2以上の整数を入力
a[1]= 5
a[2]= 11
a[3]= 2
  .
  .
  .
a[9]= 10;

素数は 3 個ありました.

この課題のポイントは,
 ①入力された数が2以上の整数か判定
 ②素数の判定
①は2と同様にdo-while文で判定できますね.②は第7回課題の4で作成した関数を配列用に変更すれば完成ですね.

4.キーボードから10個の整数を配列に入力し,1.まずその中の画面に表示,2.次に大きい順に並べ替えて表示せよ.
ヒント(各自で調査せよ):ソーティング,アルゴリズム,単純ソート,挿入ソート,バブルソートヒープソートなど

a[0]= -2
a[1]= 1
a[2]= 11
a[3]= 2
  .
  .
  .
a[9]= 10;

並べ替え前:-2 1 11 2 ... 10
並べ替え後:11 10 ... 2 1 -2

この課題のポイントは,
 ①配列の並べ替え
です.様々なやり方があると思いますが,配列の要素番号の隣同士を比較して,要素番号が小さい方の中身の数が小さければ大きい方と入れ替える,という作業を要素数だけ繰り返せば,最終的にできた配列は大きい順に並び替わります.これには繰り返し処理のネストが必要になりますね.

5.整数型配列の各要素を全て絶対値に変換する関数 abs_array を作成し, 変換前,後の配列の中身を表示せよ.
配列の中身を表示する関数 disp_array も作成せよ.

void abs_array(int data[], int N) /* Nは要素数 */
{
    ...
}


void disp_array(int data[], int N) /* Nは要素数 */
{
    .../* 配列の中身を表示 */
}

int main(void)
{
    const int N = 10;
    int a[N] = {3, -4, -9, 10, -5, 6, 2, 0, -9, 5};
		
    disp_array(...);
    abs_array(...);
    disp_array(...);
	
    return 0;
}

実行例:
変換前:3, -4, -9, 10, -5, 6, 2, 0, -9, 5
変換後:3, 4, 9, 10, 5, 6, 2, 0, 9, 5

この課題のポイントは,
 ①整数を絶対値にする
です.条件判定してもいいですが,C言語には絶対値を計算する関数というものがあります.
詳しくは「C言語 絶対値」などでググるとでてきます.この時,いつものstdio.h以外にも他のヘッダーファイルをインクルードしないといけないことにも注意しましょう.

以上で5組の課題の考え方を終わります.

6月9日16:20-19:30 5組のオフィスアワーについて

毎週木曜日16:20-19:30でいつもなら行っている5組のオフィスアワーですが,6月9日 (木)はTAが学会で不在になるため,申し訳ありませんが翌日の6月10日(金)16:20-19:30に6組とまとめて質問対応します.
宜しくお願いします.