プログラミングC
第1回 演習問題

演習の前に

必ずここを見て、ディレクトリ作成と提出方法などを確認して下さい






A問題(50点)

演習問題1

  1. ハンドアウトLec01-17 (p86)にあるサンプルプログラムlec01-5.cを参考にして、実行例のように「1文字+改行」の入力で、その文字のアスキーコードを10進数(Decimal)と16進数(Hexadecimal)で表示して終了するプログラムを作成せよ。
    (提出ファイル名: prog01a.c)

    補足: printfで16進数を表示するには、書式に %x を使用する。 また、10進数と区別するために、16進数の頭に 0x を付けている。 なお、数字表示の桁数の揃え方はハンドアウトのプログラミング入門パートLec03-14 (p16)を参照すること。
    実行例
    % ./a.out
    Enter a character: X
    'X':  88 (Decimal) / 0x58 (Hexadecimal)
    % ./a.out
    Enter a character: x
    'x': 120 (Decimal) / 0x78 (Hexadecimal)
    % ./a.out
    Enter a character: .
    '.':  46 (Decimal) / 0x2e (Hexadecimal)
    %
    
  2. 上で作ったプログラムを改良して、複数の文字のコードを出力できるようにする。 実行例のように「複数文字+改行」の入力で、その文字のアスキーコード(10進と16進)を順にすべて表示するプログラムを作成せよ。
    なお、ハンドアウトLec01-18 (p86)のサンプルプログラムlec01-6a.cを参考に、無限ループを使用し、複数文字に対応させること。 ただし、ループの脱出条件を「改行文字を読み込んだ場合」として、改行の入力で終わる1行分のアスキーコード出力だけを行うこと(サンプルプログラムでは終了条件が改行文字ではなくEOFとなっているので、Control+Dを入力するまで何度でも処理を繰り返すようになっていることに注意)。
    プログラム中での改行文字の表現についてはハンドアウトLec01-14 (p85) を参照すること。 なお、改行文字のASCIIコードは出力不要とする。
    (提出ファイル名: prog01b.c)

    補足:標準入力(キーボードからの入力)は、1文字入力されるたびにすぐプログラムのscanf関数には引き渡されるわけではない。 一旦バッファと呼ばれる領域に保存され、改行が入力されたところで初めてプログラムに渡され、処理が行われる。 今回のようにscanf関数が %c で文字入力を受け付けている場合は、バッファに保存された文字の並びの先頭から1文字づつscanf関数に渡され、 ループが一つ回るごとに処理が進行する。4文字+改行が入力された場合、 ループは5回まわることになる(改行も一文字にカウントされる)。
    実行例
    % ./a.out
    Enter any characters: char
    'c':  99 (Decimal) / 0x63 (Hexadecimal)
    'h': 104 (Decimal) / 0x68 (Hexadecimal)
    'a':  97 (Decimal) / 0x61 (Hexadecimal)
    'r': 114 (Decimal) / 0x72 (Hexadecimal)
    % ./a.out
    Enter any characters: NULL
    'N':  78 (Decimal) / 0x4e (Hexadecimal)
    'U':  85 (Decimal) / 0x55 (Hexadecimal)
    'L':  76 (Decimal) / 0x4c (Hexadecimal)
    'L':  76 (Decimal) / 0x4c (Hexadecimal)
    %
    

B問題(50点)

演習問題2

  1. 演習問題1で作成したプログラムをさらに改良し、今度はアスキーコードだけでなく、その文字がアルファベット文字(A~Z,a~z)かどうかを表示するようにする。
    そのために isAlphabetic という関数を作成して使用する (AlphabeticのAは大文字であることに注意すること)。
    isAlphabetic の関数定義は
    int isAlphabetic(char c)
    注意:これは関数本体の定義であり、プロトタイプ宣言では仮引数の変数名(この場合は c)は書かない(ハンドアウトのプログラミング入門パートLec11-13 (p59)も参照のこと。一般には変数名を書いてもコンパイルや動作に支障はないが、バグの原因になる場合もあるので推奨されない)。
    とし、さらに以下のマクロを定義する。
    #define TRUE 1
    #define FALSE 0
    関数 isAlphabetic は引数(文字型変数 c)がアルファベットかどうか 検査し(大文字、小文字は問わない)、戻り値としてアルファベットの場合は TRUE を、それ以外の場合は FALSE を返すようにする。
    (提出ファイル名: prog02a.c)

    注意:ASCIIコード表の中で英字の大文字と小文字は連続していない('Z'の次の文字は'a'ではない;付録3参照)ことに注意すること。
    実行例
    % ./a.out
    Enter any characters: 5W1H
    '5':  53 (Decimal) / 0x35 (Hexadecimal). It's a non-alphabetic character.
    'W':  87 (Decimal) / 0x57 (Hexadecimal). It's an alphabetic character.
    '1':  49 (Decimal) / 0x31 (Hexadecimal). It's a non-alphabetic character.
    'H':  72 (Decimal) / 0x48 (Hexadecimal). It's an alphabetic character.
    % ./a.out
    Enter any characters: e.g.
    'e': 101 (Decimal) / 0x65 (Hexadecimal). It's an alphabetic character.
    '.':  46 (Decimal) / 0x2e (Hexadecimal). It's a non-alphabetic character.
    'g': 103 (Decimal) / 0x67 (Hexadecimal). It's an alphabetic character.
    '.':  46 (Decimal) / 0x2e (Hexadecimal). It's a non-alphabetic character.
    % ./a.out
    Enter any characters: int a[0]
    'i': 105 (Decimal) / 0x69 (Hexadecimal). It's an alphabetic character.
    'n': 110 (Decimal) / 0x6e (Hexadecimal). It's an alphabetic character.
    't': 116 (Decimal) / 0x74 (Hexadecimal). It's an alphabetic character.
    ' ':  32 (Decimal) / 0x20 (Hexadecimal). It's a non-alphabetic character.
    'a':  97 (Decimal) / 0x61 (Hexadecimal). It's an alphabetic character.
    '[':  91 (Decimal) / 0x5b (Hexadecimal). It's a non-alphabetic character.
    '0':  48 (Decimal) / 0x30 (Hexadecimal). It's a non-alphabetic character.
    ']':  93 (Decimal) / 0x5d (Hexadecimal). It's a non-alphabetic character.
    %
    
  2. 更に isUppercase という関数を追加し、入力された文字がアルファベットの大文字(upper-case)か小文字(lower-case)か、あるいはアルファベット以外かを表示するプログラムを作成せよ (UppercaseのUは大文字であることに注意すること)。
    isUppercase の関数定義は
    int isUppercase(char c)
    
    とする。
    関数 isUppercase は引数(文字型変数 c)がアルファベット大文字かどうか検査し、戻り値として アルファベット大文字の場合は TRUE を、そうでない場合は FALSE を返すようにする。
    (提出ファイル名: prog02b.c)
    実行例
    % ./a.out
    Enter any characters: Ph.D.
    'P':  80 (Decimal) / 0x50 (Hexadecimal). It's an upper-case character.
    'h': 104 (Decimal) / 0x68 (Hexadecimal). It's a lower-case character.
    '.':  46 (Decimal) / 0x2e (Hexadecimal). It's a non-alphabetic character.
    'D':  68 (Decimal) / 0x44 (Hexadecimal). It's an upper-case character.
    '.':  46 (Decimal) / 0x2e (Hexadecimal). It's a non-alphabetic character.
    % ./a.out
    Enter any characters: Wi-Fi
    'W':  87 (Decimal) / 0x57 (Hexadecimal). It's an upper-case character.
    'i': 105 (Decimal) / 0x69 (Hexadecimal). It's a lower-case character.
    '-':  45 (Decimal) / 0x2d (Hexadecimal). It's a non-alphabetic character.
    'F':  70 (Decimal) / 0x46 (Hexadecimal). It's an upper-case character.
    'i': 105 (Decimal) / 0x69 (Hexadecimal). It's a lower-case character.
    % ./a.out
    Enter any characters: int A[0]
    'i': 105 (Decimal) / 0x69 (Hexadecimal). It's a lower-case character.
    'n': 110 (Decimal) / 0x6e (Hexadecimal). It's a lower-case character.
    't': 116 (Decimal) / 0x74 (Hexadecimal). It's a lower-case character.
    ' ':  32 (Decimal) / 0x20 (Hexadecimal). It's a non-alphabetic character.
    'A':  65 (Decimal) / 0x41 (Hexadecimal). It's an upper-case character.
    '[':  91 (Decimal) / 0x5b (Hexadecimal). It's a non-alphabetic character.
    '0':  48 (Decimal) / 0x30 (Hexadecimal). It's a non-alphabetic character.
    ']':  93 (Decimal) / 0x5d (Hexadecimal). It's a non-alphabetic character.
    %
    

演習問題3

会津大での評点(100点満点)と評価(A,B,C,D,F)並びに Grade Point(GP, 0-4点)は以下のように対応している。

この時、評点(0-100の整数)を渡すと、評価(A,B,C,D,Fのいずれかの文字)を返す関数
char ScoreToGrade(int score)
と、評価(A,B,C,D,F)を渡すと、Grade Point(0-4の整数)を返す関数
int GradeToPoint(char grade)
の2つの関数を作成し、 これらを使用して、各評点に対応する評価(A-F)と全体の Grade Point Average (GPA) を計算するプログラムを作成せよ。

なお、「Grade Point Average (GPA)」とは、それぞれの Grade Point に単位数で重みをつけた (GP*単位数)値の平均値で、下記実行例の 1 つ目の場合では、(4*3 + 3*2)/(3+2) = 3.600… となる。

main 内は無限ループとし、以下の例のように評点と単位数が入力されると、 関数 ScoreToGrade と GradeToPoint を用いて評価と Grade Point を表示する。
また、CTRL-D (EOF)が入力された場合、Grade Point Average (GPA) と受講単位数を表示してプログラムを終了する。

このプログラムにおけるGPAの計算方法はキャンパスガイドに掲載された本学におけるGPAの計算方法に準拠しているので、 自分の評点を入力する事で自分の GPA を容易に計算できる。

(提出ファイル名: prog03.c)
実行例
% ./a.out
Enter score and credit (Ctrl+D to stop): 80 3
Grade for 80 is A, Grade Point (GP) is 4. (3 credits)

Enter score and credit (Ctrl+D to stop): 65 2
Grade for 65 is B, Grade Point (GP) is 3. (2 credits)

Enter score and credit (Ctrl+D to stop): ^D
Grade Point Average (GPA): 3.600, (Total credits: 5)
% ./a.out
Enter score and credit (Ctrl+D to stop): 34 2
Grade for 34 is F, Grade Point (GP) is 0. (2 credits)

Enter score and credit (Ctrl+D to stop): 49 3
Grade for 49 is D, Grade Point (GP) is 0. (3 credits)

Enter score and credit (Ctrl+D to stop): 79 4
Grade for 79 is B, Grade Point (GP) is 3. (4 credits)

Enter score and credit (Ctrl+D to stop): ^D
Grade Point Average (GPA): 1.333, (Total credits: 9)
%

Extra問題

演習問題4

文章中の母音のみ、大文字→小文字、小文字→大文字と変換するプログラムを作成する。
この時、関数
char VowelChgCase(char) 
を作成して、入力された文字を一文字づつ変換する。この関数の仕様は以下の通り。
  1. 引数は文字(一文字)
  2. 戻り値も文字
  3. 引数として与えられた文字がアルファベットかつ小文字の母音(a,i,u,e,o)である場合、大文字に変換(例 a->A)
  4. 引数として与えられた文字がアルファベットかつ大文字の母音である場合、小文字に変換(例 E->e)
  5. 変換した文字を戻り値とする。
  6. それ以外の文字(数字記号等とアルファベット子音)はそのままの文字を戻り値とする。
main中では、無限ループを利用し、文字が入力されると関数 VowelChgCase を用いて入力された文字を変換して表示する。EOF が入力されたら無限ループを終了し、プログラムも終了する。
(提出ファイル名: prog04.c)

実行例 (赤字が入力分)

% ./a.out
The University of AIZU (1993)
ThE unIvErsIty Of aiZu (1993)
to Advance Knowledge for Humanity
tO advAncE KnOwlEdgE fOr HUmAnIty
^D
%

演習問題5

ハンドアウトLec01-6 (p83) の lec01-1.c を参考に、 char, short, int, long, float, double 型変数、および、 それぞれの型の要素数10の配列がメモリ上に占める大きさ(バイト数) を求めて表示するプログラムを作成しなさい。
(提出ファイル名: prog05.c)
実行例
% ./a.out
sizeof(  char): 1
sizeof( short): 2
sizeof(   int): 4
sizeof(  long): 8
sizeof( float): 4
sizeof(double): 8
sizeof(  char[10]): 10
sizeof( short[10]): 20
sizeof(   int[10]): 40
sizeof(  long[10]): 80
sizeof( float[10]): 40
sizeof(double[10]): 80
%
Macで使用しているgccは、文法等についてCentOS環境の場合より厳密にチェックされるため、 この問題で参考にしている lec01-1.c の
printf("sizeof(short ) : %d\n",sizeof(s));
に対して以下のようなwarning(警告)が出力されます。
lec01-1.c:14:34: warning: format specifies type 'int' 
but the argument has type 'unsigned long' [-Wformat]
  printf("sizeof(short ) : %d\n", sizeof(s));
                           ~~    ^~~~~~~~~
                           %lu
この警告の背景は以下のようなものです。
  1. sizeof 演算子の型(関数と同様、演算子も演算結果に対して型を持ちます)は unsigned long である
  2. printf 関数などで用いる書式指定子のうち、unsigned long 型に本来対応するのは %lu である
  3. 上記のプログラムでは書式指定子として %d を使っているので、書式指定が型と不一致であるとして警告が出力される
つまり、この場合はメッセージに沿って
printf("sizeof(short ) : %lu\n",sizeof(s));
とすれば警告は出力されなくなるはずです。

ただ、sizeofの結果を%dで表示させることはよくあり、実際CentOSではこの記述のままでも警告は出力されません。あまり深刻な問題ではなく、このまま実行しても意図通りに動作はしますので、無視しても問題ありません。

一般にコンパイル時のwarning(警告)は、本質的に間違っているわけではないが、何らかのリスクがる記述になっている場合に出力されます。error(エラー)はコンパイル自体が不可能であり、実行ファイルは出力されませんが、警告の場合は実行ファイルは出力されます。 警告の対象となる記述にはさまざまな種類があり、コンパイラの種類・設定によって実際に警告が出力されるかどうかは変わります。今回の例はあまり深刻な問題ではなかったということになります。

ただ、潜在的なバグにつながる問題を指摘してくれる警告もありますので、今後自分の書いたプログラムのコンパイル時に警告が出力された場合は、その内容をよく吟味する方がよいでしょう。