A問題(50点)
下記のマクロ関数を使ったプログラムは、引数の与え方によって正しく動作しない場合がある。
#include <stdio.h> #define func(x) x*x-2 int main() { int a = 2; double b = 1.5; printf("a=%d, b=%f\n", a, b); printf("func(a) = %d\n", func(a)); printf("func(a+2) = %d\n", func(a + 2)); printf("func(a)*4 = %d\n", func(a) * 4); printf("func(b) = %f\n", func(b)); printf("func(b+2.5) = %f\n", func(b + 2.5)); return 0; }実行結果(一部の結果は正しくない)
% ./a.out a=2, b=1.500000 func(a) = 2 func(a+2) = 6正しくない func(a)*4 = -4正しくない func(b) = 0.250000 func(b+2.5) = 5.750000正しくない
func(a)
は a*a-2
に置き換えられる。
func(a+2)
やfunc(a)*4
で正しくない実行結果が出力される理由を理解するために、
func(a+2)とfunc(a)*4の置き換え結果がどのようになるのかを考えて prog01.txt に記述しなさい
(書式は以下に示すものを使うこと)。
func(a)
、func(a+2)
やfunc(a)*4
の
置き換え結果も prog01.txt に記述しなさい。修正前 #define func(x) x*x-2の場合 func(a)の置き換え結果: a*a-2 func(a+2)の置き換え結果: ________________ func(a)*4の置き換え結果: ________________ 修正後 #define func(x) ________________ の場合 func(a)の置き換え結果: ________________ func(a+2)の置き換え結果: ________________ func(a)*4の置き換え結果: ________________
% ./a.out a=2, b=1.500000 func(a) = 2 func(a+2) = 14 func(a)*4 = 8 func(b) = 0.250000 func(b+2.5) = 14.000000
問題の前に、講義Lec12-12~16 (p140~141)で学んだこと(条件コンパイル)の確認を行う。
こちらのプログラムcompile_sample.c
は、キーボードから奇数を入力すると、
その数以下の正の奇数の和を計算して表示するプログラムである。
(例えば、9 を入力すると 1 + 3 + 5 + 7 + 9 = 25 を表示する)
このプログラムをファイル名compile_sample.c
として保存し、以下の4通りのやり方でコンパイルして実行し、動作の違いを確かめなさい。
JAPANESE
の値を 0
に変えて保存し、コンパイルして実行する
JAPANESE
の値を 0
に変えて保存し、さらに -DDEBUG オプションを付けてコンパイルして実行する
1の場合 % gcc compile_sample.c % ./a.out 奇数を入力してください: 9 1 から 9 までの奇数の和 = 25 2の場合 % gcc -DDEBUG compile_sample.c % ./a.out 奇数を入力してください: 9 i = 1, sum = 1 i = 3, sum = 4 i = 5, sum = 9 i = 7, sum = 16 i = 9, sum = 25 1 から 9 までの奇数の和 = 25 3の場合(マクロの値を変えた後) % gcc compile_sample.c % ./a.out Please input an odd number: 9 Sum of odd numbers from 1 to 9 = 25 4の場合(マクロの値を変えた後) % gcc -DDEBUG compile_sample.c % ./a.out Please input an odd number: 9 i = 1, sum = 1 i = 3, sum = 4 i = 5, sum = 9 i = 7, sum = 16 i = 9, sum = 25 Sum of odd numbers from 1 to 9 = 25
他の3つの場合も同じように試し、それぞれの表示結果とプログラムを見比べ、
コンパイル時にプログラムのどの部分が有効になっているか確認すること。
#if
, #ifdef
, #else
等の機能を理解できたら、以下の問題に進みなさい。
プログラム lec12-6a.c
、
lec12-6b.c
とヘッダファイル lec12-4.h
は第12回のハンドアウト(p142~143)に出てきた分割コンパイルの例である。
まず、各ファイルを
prog02.txt
に書きなさい。なお、
結合テスト(最終的な実行ファイルの動作確認)向けのコンパイル方法は、複数コマンドで順次行う場合と
一度に行う場合の2通りが考えられる。両方を記載すること。
prog02.txtの全体の記述内容は以下の通りである。下線部を記入すること。
単体テスト prog02main.c: ________________ prog02calc.c: ________________ 結合テスト/最終的な実行ファイルの動作確認 複数コマンドで順次行う場合: ________________ ________________ ________________ 一度に行う場合: ________________
(解答ファイル名:prog02header.h, prog02main.c, prog02calc.c, prog02.txt)
実行例(prog02main.cの単体テスト)% ./a.out 0 0 0 1 1 0 0 0 0 1 1 0 Area = 0.000000実行例(prog02calc.cの単体テスト)
% ./a.out Area = 0.500000実行例(結合テスト/最終的な実行ファイルの動作確認)
% ./a.out 0 0 0 1 1 0 Area = 0.500000 % ./a.out 1 0 2 0 3 3 Area = 1.500000
B問題(50点)
read_planets
によって太陽系の惑星のデータ planet.txt
を Planet型の構造体配列 planets[] に読み込み、表示を行うためのものである。#include <stdio.h> #include <string.h> #include <stdlib.h> #include "prog03header.h" int main() { int i; Planet planets[NPLANETS]; if( read_planets( planets ) != 0 ){ fprintf(stderr, "Input Error!\n"); exit(3); } for (i = 0; i < NPLANETS; i++) { printf("Name: %s\n", planets[i].name); printf(" Radius: %.0f [km]\n", planets[i].radius); /* 半径 */ printf(" Density: %.2f [g/cm3]\n", planets[i].density); /* 密度 */ printf(" Semi-Major Axis: %.3e [km]\n", planets[i].semi_major_axis); /* 軌道長半径 */ } return 0; } #ifdef TEST0 /* 単体テスト用 read_planets 正常動作(戻り値 0 )の場合 */ int read_planets(Planet *planets) { int i; for (i = 0; i < NPLANETS; i++) { strcpy(planets[i].name, "Sample"); /* テスト用のサンプルデータ */ planets[i].radius = 1000; planets[i].density = 1.00; planets[i].semi_major_axis = 100000000; } return 0; } #elif TEST1 /* 単体テスト用 read_planets 異常動作(戻り値 -1 )の場合 */ int read_planets(Planet *planets) { return -1; } #endif正しくデータを読み込めたときに想定される実行結果:
Name: Mercury Radius: 2440 [km] Density: 5.43 [g/cm3] Semi-Major Axis: 5.790e+07 [km] Name: Venus Radius: 6052 [km] Density: 5.24 [g/cm3] Semi-Major Axis: 1.082e+08 [km] Name: Earth Radius: 6378 [km] Density: 5.52 [g/cm3] Semi-Major Axis: 1.496e+08 [km] Name: Mars Radius: 3396 [km] Density: 3.93 [g/cm3] Semi-Major Axis: 2.279e+08 [km] Name: Jupiter Radius: 71492 [km] Density: 1.33 [g/cm3] Semi-Major Axis: 7.783e+08 [km] Name: Saturn Radius: 60268 [km] Density: 0.69 [g/cm3] Semi-Major Axis: 1.429e+09 [km] Name: Uranus Radius: 25559 [km] Density: 1.27 [g/cm3] Semi-Major Axis: 2.875e+09 [km] Name: Neptune Radius: 24764 [km] Density: 1.64 [g/cm3] Semi-Major Axis: 4.504e+09 [km]
しかし、関数 read_planets
はまだ実装されておらず、
代わりに単体テスト用のダミーのコード(#ifdef
以降の部分;
実際に読み込みはしていない)が prog03main.c の末尾に付加されている。
0.(準備)
まず、ファイル prog03main.c 、 prog03header.h 、
planet.txt を自分の作業ディレクトリにコピーしておくこと。
1.
コンパイル時のマクロ定義を利用して、prog03main.c 単体で動作テストを行う際のコンパイル方法を
prog03.txt
に書け。その際、関数 read_planets
の
戻り値が 0 の場合(正常)と -1 の場合(異常)の二通りの動作
を確認する方法を調べること。
プログラム中で使われている #elif
などの条件コンパイルのための
マクロ・プリプロセッサについては、ハンドアウト Lec12-12, 13や
C言語入門講座 9.プリプロセッサとメイクファイル
などを参考にすること。
% ./a.out Name: Sample Radius: 1000 [km] Density: 1.00 [g/cm3] Semi-Major Axis: 1.000e+08 [km] (以下同じ出力の繰り返し)コンパイルできた時の実行例(関数read_planetsの戻り値 -1 の場合)
% ./a.out Input Error! %
2.
prog03read.c
に、下記の仕様に基づいてファイルからデータ読み込みを行う正規の関数
read_planets
を作成せよ。
さらに、prog03main.c と prog03read.c を組み合わせて全体での動作テストを行う際のコンパイル方法
(複数コマンドで順次行う場合と一度に行う場合の2通りを
prog03.txt
に書け
read_planets
の引数や戻り値については prog03header.h を見て確認すること
prog03main.c単体の動作テスト 戻り値が 0 の場合(正常): ________________ 戻り値が -1 の場合(異常): ________________ prog03main.c と prog03read.cを組み合わせた結合テスト/最終的な実行ファイルの動作確認 複数コマンドで順次行う場合: ________________ ________________ ________________ 一度に行う場合: ________________(解答ファイル名:prog03read.c, prog03.txt)
Extra問題
#include <stdio.h> #include "prog04header.h" /* (1) */ /* 必要に応じて追加、修正してもよい */ int main() { /* (2) */ /* 必要に応じてコードを追加 */ /* 1つ目の三角形の3頂点の座標を入力する */ InputTriangle( ); /* 引数・戻り値は適宜設計せよ (3) */ /* 1つ目の三角形の面積を求める */ CalcArea( ); /* 引数・戻り値を入れる変数等は適宜設計せよ (4) */ /* CalcAreaの結果をもとに、先へ進むか,再入力を求める */ /* 2つ目の三角形の3頂点の座標を入力する */ InputTriangle( ); /* 引数・戻り値は適宜設計せよ (3) */ /* 2つ目の三角形の面積を求める */ CalcArea( ); /* 引数・戻り値を入れる変数等は適宜設計せよ (4) */ /* CalcAreaの結果をもとに、先へ進むか,再入力を求める */ printf( /* CalcAreaで求めた三角形の面積を2つ表示 */ ); /* 面積の大きい方を判定して、表示 */ /* 1つ目の三角形の重心を求める */ CalcCentroid( ); /* 引数・戻り値等は適宜設計せよ (5) */ /* 2つ目の三角形の重心を求める */ CalcCentroid( ); /* 引数・戻り値等は適宜設計せよ (5) */ printf( /* CalcCentroidで求めた三角形の重心2つを表示 */ ); return 0; }実行例(出力内容を厳密にこの例に合わせる必要はない)
% ./a.out Input 1st triangle: Vertex 1: 2.0 2.0 Vertex 2: 2.0 8.0 Vertex 3: 2.0 12.0 These points do not form a triangle! Again, input 1st triangle: Vertex 1: 1.0 3.0 Vertex 2: 4.0 9.0 Vertex 3: 7.0 2.0 Input 2nd triangle: Vertex 1: 0.0 0.0 Vertex 2: 4.0 0.0 Vertex 3: 2.0 9.0 Area of 1st one: 19.500 Area of 2nd one: 18.000 The 1st one is larger than the 2nd one! Centroid of 1st one: (4.000, 4.667) Centroid of 2nd one: (2.000, 3.000)必要な作業は以下の通りである。
prog04header.h
の作成
prog04_2.c
の完成。
(上記未完成プログラムに含まれている関数の削除や外部変数の使用は禁止する。
関数の実行位置の変更や引数、戻り値は任意に決めてよい。
また、必要な変数・if や for などの制御文の追加、他の関数の追加も自由にしてよい)
prog04_3.c
)
prog04_4.c
)
prog04_5.c
)
prog04.txt
の作成
(解答ファイル名:prog04header.h, prog04_2.c, prog04_3.c, prog04_4.c, prog04_5.c, prog04.txt)