情報基礎A 「Cプログラミング」(ステップ2・式と計算)
このステップの目標
- Cプログラムの定型的なパターンを元に、「直線的」な流れのプログラムが記述できる
- printf()関数, scanf()関数を使った基本的な入出力動作が記述できる
- 変数宣言、式の計算と代入を組み合わせ、変数の値の変化を把握しながら、プログラムを構成できる
1. プログラムの書き方
このステップからは、アルゴリズムを具体的なCのプログラムに翻訳する方法について学んでいこう。
まずは、世界で一番簡単なCのプログラムを以下に示す。
main( ) { }
実際にこのプログラムは「何もしない」(a.outを実行してもなにも起こらない)。 それは、コンピュータに何も具体的な指示を与えていないからだ。 何か仕事をさせるためには、 { と } の括弧の間に、コンピュータへの指示を箇条書きに並べる。 C言語の世界で中括弧は、コンピュータへの指令をひとつのまとまり(文章の段落に相当する単位)を表すために使われる。 そして、最初の main( ) は、段落の表題に相当する部分で、「それに続く{ }の中身がプログラムのメイン(main)の部分である」ことを表している。
以下の実習では、これをひな形として、中括弧のあいだに指令を書き加える形で、プログラムを記述する。 文章にも「文の終わりには句点。を付ける」などのルールがあるように、Cのプログラムにも基本的なルールがあるので、ここでそのいくつか確認しておこう。
ここは結構大切
-
プログラムは原則的に半角英数字で記述する。プログラムに日本語の文字が混じっているだけでエラーになる(コメントなど、一部、日本語を書いてよい箇所もあります)。特に日本語のスペースには注意すること。TurtleEditで日本語文字はオレンジ色の破線で囲まれて表示されるので、確認の際に参考にするとよい。
良い例: if (x==y) z = x ; 悪い例: if(x==y)z=x;
- C言語では大文字と小文字は区別して扱われる。例題などで小文字のところを大文字に書き換えてはいけない。
また、括弧
( ), { }, [ ]
はそれぞれ用途が異なり、正しく使い分ける必要がある。 - プログラムはたくさんの文 (statement)の集まりである。文は必ずセミコロン(;)で終わる。
例: int x=3 ; x = x + 3 ;
- 文章の段落に相当するものをブロック(block)と呼ぶ。ブロックは括弧
{
で始まり、括弧}
で終わる。 ブロックの最後のセミコロンは省略できる。例: { int x,y ; int z = x + y ; printf("%d+%d=%d\n",x,y,z) ; }
- コンピュータープログラムの文法は厳密に定義されている。コンマ1つ、大文字と小文字を一カ所、括弧を1つ間違えただけでも、エラーになったり、意図しない動作をする。ただし、「語」と「語」の間に空白や改行を入れるのは自由である(C言語はfree-form languageのひとつである)。
ここで、C言語に限らず、全てのプログラミング言語の共通した「構造」あるいは「パターン」を知っておこう。 基本的に、コンピュータプログラムは
宣言部(以下に記述するプログラム本体に必要な設定など) 計算処理手順の記述1 計算処理手順の記述2 ... |
という形式で記述される。この節の始めの「世界で一番簡単なプログラム」では、宣言部が空で、計算処理手順の記述がmain(){ }
に対応する。
2. 文字の出力
では早速、中身のあるプログラムの例に進んでいきたい。 以下のプログラムをエディタで入力し、コンパイル・実行してみなさい。"Hello !" の部分は、自分の好きな言葉などに変えても構わない。 ファイル名は ex1.c で統一しておこう。
例題1(ex1.c)
「写経」のマークのあるコードはコピー&ペーストできません。学習のため、手で打ち込んでください。
/* Example 1 */ #include <stdio.h> main( ) { printf("Hello !\n") ; }
例題1のアルゴリズム
Input:
Output: "Hello !"の文字列
1: "Hello !"と出力し、改行する
2: 終了する
このプログラムの主な見所は以下の通り:
/* Example 1 */
- /* で囲まれている箇所 */ はコメントで、プログラムの実行には一切関係しない。
// Example 1
のような書きかたもできる。この場合、//
から行末までがコメントと解釈される。
#include <stdio.h>
動作:ヘッダーファイルの組み込み- このプログラムの宣言部。必要なヘッダファイルを「組み込む(includeする)」。 この例の stdio.h は標準的(standard)な入出力(i/o:input/output)を用いる際のおまじない。コンピュータの世界で「入出力(アイ・オー)」とは、キーボードから文字を入れたり、画面に文字などを出したりするような、外界とのやり取りの総称である。
main( ) { ・・・・ }
動作:関数の定義- { から } までをmainという名前の関数(処理のひとまとまり)として定義する。ここでいうところの「関数」とは機能(function)と解するのが適当で、数学的な意味での「函数」とはちょっと異なる。
printf("何々・・・") ;
動作:文字列の出力- 画面に「何々・・・」を表示する。「何々」は日本語でも構わない。例題中の
\n
(バックスラッシュ n)は、2文字が1セットで「改行」を表す。改行とは、エディタやワープロでEnterキーを押した際の「あの」動作。
バックスラッシュ($\backslash$) は ¥ キーで
バックスラッシュ $\backslash$ をタイプする際には、日本語キーボードでは¥記号を押す。 その昔、米国のコンピュータを日本に輸入する際、 金額処理にどうしても必要な¥記号を、$\$$記号と入れ替えるわけにもいかなかったため、使用頻度の低かった$\backslash$の文字コードに¥を割り当ててしまった名残である。
アスキーアートは、いかにも ローテクな気もするが、それでいて、結構味わい深い。 ここでの例を参考にすれば、絵心のある人は、アスキーアートを出力するプログラム(UNIXコマンド)を作成することができるはずだ。 その際、日本語文字を用いると、エディターや端末エミュレータの設定によっては、「文字化け」が起こるかもしれないので、注意すること。
練習:「花文字」のプリント
このプログラムを改造して,画面に以下のような「花文字のC」を出してみなさい。
*** ** ** ** ** ** *** |
解説
プログラムのひな形 main() { 何々; }
の何々のところに,セミコロン;で区切りながら 順に指令を書き並べるのが,最も基本的なプログラムの書き方であった。
例えば、
#include <stdio.h> main( ) { printf("1\n") ; printf("2\n") ; printf("3\n") ; }
を実行すると,画面には
1 2 3
と出力される。この一見当たり前にも思える動作を,まず飲み込んでおこう。プログラムの「流れ」の様子を図にすると,左のように描けるだろう。このような図を流れ図(フローチャート)と呼ぶ。 特に指定しなければ,プログラムというのは「上から下へ順番に」実行される。大切なのは、前の文の処理が完了してから次の文の処理に移る、という点だ。
亀場で練習:グラフィックスで文字を描く
補助教材の「亀場」のイントロダクションのページを開き、 同じページのかなり下の辺りにある練習課題 のうち、アルファベットのCをグラフィックスで描画するプログラムを完成させなさい。
3.変数を使った式の計算
以下の例題をエディタで入力して、コンパイル、実行してみなさい(ファイル名は ex2.c にしよう):
例題2(ex2.c)
/* Example 2 */ #include <stdio.h> main( ) { int a,b,s ; printf("2つの整数を入力してください-->") ; scanf("%d %d",&a,&b) ; s = a + b ; printf("合計 = %d\n",s) ; }
例題2のアルゴリズム
Input: 2つの整数 $a,b$
Output: 整数の和 $a+b$
1: 整数用の変数 $a, b, s$ を用意する
2: "2つの整数を入力してください-->" と出力する
3: キーボードから $a, b$ にそれぞれ値をセットする
4: $a+b$ の値を変数 $s$ にセットする
5: "合計= "に続いて、$s$ の値を出力し、改行して、終了する
TurtleEditを使っている場合は、マウスで下側の区画(コンソール)をクリックし、赤くて四角いカーソルが表示されている状態で、キーボード入力を始めること。
プログラムを実行すると、画面に 2つの整数を入力してください-->
と出るので、半角文字で、二つの整数をスペースを区切って入力し、Enterキーを押す。例えば "7 2 <enter>
" 。すると、二つの数の和(9)が出力されるはずだ。
解説
C言語の主な変数の型
整数は int
実数は float
文字は char
数式を記述する際の注意点
・乗算記号 * は省略できない。a*b を a b と略すことはできない。
・演算の優先度を表す際に使えるのは丸括弧のみ。a*(b+c)をa*[b+c]等と書いてはいけない。
・整数のみの演算の結果は整数値。1/2 は 0.5 ではなく 0。1.0/2.0 は 0.5。
プログラムの見所は以下のとおり:
int a,b,s ;
動作:整数型の変数 a,b,sの宣言-
コンピュータプログラムが動作する際に、計算の結果などを記憶しておくためのデータの保管場所(メモリー装置の中の区画。以下では単にメモリーと呼ぶことにする)が必要である。
C言語では、このメモリーを、あたかも数学で登場する変数 $x$ や $y$ などのように、記号として扱うことができる。
こうしたメモリー(変数)は、名前(変数名)を付けて区別・操作する。
変数名は a, abc, g23 など,英数字を組み合わせて自由に命名できるが、
日本語文字を含んでいたり(例:変数1)、数字で始まっていたり(311data),特殊な文字が含まれていたり(you&me)、
予約語(int, float, char, if, else, for, 等)と重複してはならない。
少々厄介なのは、C言語では、扱うデータの種類ごとに変数のタイプ(データ型と呼ぶ)を区別する習わしになっている点だ。 データ型には、整数、実数、文字、などがある。 例えば、int
は integer(整数) を、float
はfloating-point number(浮動小数表現の実数)を、char
はcharacter(文字)を、それぞれ表す。
以上を踏まえると、この行は、『整数(int)を記憶するための変数(メモリー)を3つ用意し、それぞれに a, b, s という名前を付けよ』 という指示になる。
-
printf("2つの整数を入力してください-->") ;
動作:画面出力 -
printf("何々");
で、ダブルクォーテーションで囲まれた箇所の文字列が端末画面に出力される。 なお、この例のように、キーボードからのデータ入力を促すための記号(文)をプロンプトという。
scanf("%d %d",&a,&b) ;
動作:キーボードから変数の値をセット-
scanf("%d",&整数型の変数名) ;
と書くと,変数にキーボードから入力した値がセットされる。 その発展形として、この例題では、二つの変数(aとb)に「まとめて」値をセットしている。%d
は、セットする先の変数が整数型であることを表している。
s = a + b ;
動作:計算と代入-
変数 a の内容と b の内容を加算して、その結果を変数 s に代入(セット)する。
ここで、
=
は代入操作を表しており、等号ではない点を十分意識すること。 数学的な計算に使う記号(演算子)には、+
(加算)のほかに、-
(減算)、*
(乗算)、/
(除算)、%
(剰余)などがある。 また、計算順序は、通常の数式と同様、乗除が優先され、丸括弧( )
を使ってコントロールすることができる。 基本的に、数式は数学の教科書のように記述できるが、乗算記号*
は省略できない点に注意。 例えばCの計算式としてs=(a+b)(c+d);
は誤り(括弧の間に*
が必要)である。
printf("合計 = %d\n",s) ;
動作:結果の出力-
printf("何々 %d 何々\n", 整数型の変数名) ;
と書くと,「何々」の中の%d
で指定した位置に変数の値(上の例ではsの値)が表示される。%d
は、出力の書式を10進数(decimal notation)とするための指示。
このプログラムの一連の動作を図で示すと,以下のようになる:
例題2の動作の流れ
変数について再確認
プログラミングにおいて、変数を的確に操作できるようになるのはとても重要である。 変数とは、データを入れる「箱」のようなもので、その箱に付けられたラベルが変数名に対応する。 少し違った比喩としては、机上にカードの置き場があって、置き場に付けられた名称が変数名、置かれたカードが変数の値、とイメージしても良いかもしれない。 ここでは、カードの比喩に沿って、変数の使い方と動作について今一度確認しておこう:
- カードの置き場は宣言(int 何々; float 何々; 等)することではじめて使えるようになる。
- カード置き場には整数(int)、実数(float)、文字(char)等のタイプ(型)があって、そのタイプのカードしか置けない。
- 演算結果の代入操作
x = 式;
は、式の値のカードを、場所 x に置く操作。 - C言語では、異なるタイプのカードを置こうとすると、置き場のタイプに「変換」された別のカードが置かれる。例えば、整数型の置き場所に実数を置こうとすると、値が整数に丸められる。
- 代入操作
x = y;
は、置き場所 y のカードのコピーを x に置く操作。このとき場所 y のカードは変化しない。 - 代入操作
x = x + 1 ;
は、置き場所 x カードの値に1を加えた新たなカードを、同じ x に置き直す操作。
練習:べき乗の計算
ある実数を8乗した値を、以下の手順で求めたい。この手順を「素直に」Cのプログラムに翻訳し、動作を確認してみなさい。
Input: 実数x
Output:x
を8乗した値 1: 実数型の変数x
を用意する 2:x
に、キーボードから実数値をセットする 3:x
の二乗(x*x
)を計算し、その結果を改めてxにセットする(x
には、入力値の2乗がセットされた) 4:x
をさらに二乗し、その結果を改めてx
にセットする(x
には、入力値の4乗がセットされた) 5:x
をさらに二乗し、その結果を改めてxにセットする(x
には、入力値の8乗がセットされた) 6:x
の値(答え)を出力する
x
の8乗を求めるには、7回のかけ算(x*x*x*x*x*x*x*x
)を行うのが愚直な
計算方法と言えるが、上の手順ではこれが3回のかけ算に「節約」できている。変数を用いて、中間的な結果を使いながら
計算を進めるところがポイントだ。
ヒント
例題2(ex2.c)をひな形にして作業をはじめるのが良いだろう。
この場合は実数で計算したいので、それに用いる変数は実数型(float
)で宣言する必要がある。
実数(float)型の変数を扱う際の、 scanf()とprintf()のフォーマット書式
実数型の変数x
に、キーボードから値をセットするには、scanf("%f",&x);
と記述する。
実数型の変数x
の値を、コンソール画面に出力するには、printf("ANSWER= %f\n",x) ;
のように記述する。
練習:三角形の面積
三角形の三辺の長さ(ここではa, b, cとしよう)を入力すると、その面積を計算して画面に表示するプログラムを作成しなさい。
ヒント
ヘロンの公式(Heron's formula)によれば、 s = (a+b+c)/2 とすると、三角形の面積 S は$S = \sqrt{s (s-a) (s-b) (s-c)}$ で与えられる。この公式を活用しよう。
面積は実数値を取るので、整数(int)を使った計算では不十分である。そこで、実数型(float型)の変数の登場となる。float型を使うにあたっての注意点を以下にまとめる:
-
実数型の変数宣言は、
float a,b,c ;
のようにして行う。 -
実数型の変数に(キーボードから)値をセットするには、
scanf("%f", &a);
のように記述する。3つの変数にまとめてセットする方法scanf("%f%f%f",&a,&b,&c);
も可能。整数型の変数を使う場合には%d
であった箇所が、 実数型では%f
になる点を意識せよ。 - 実数値を出力するには printf("%f \n",S) ; のようにする。こちらも、整数型では%dであった箇所が、%fになる点に注意。
平方根(square root)を計算するには、C言語に標準の追加機能として用意されているsqrt( )
関数を使う。例えば、z = sqrt(x*x + y*y) ;
と記述すると、x*x+y*y(xの二乗とyの二乗の和)の平方根がzに代入される。sqrt()
等の数学関数を使うには、プログラムの宣言部に以下のひな形ように #include <math.h>
の1行を追加する必要がある(sin(),cos(),tan(),exp(),log()
などについても同様)。
TurtleEditを使わず、コマンドラインで作業している場合は、数学関数を含むプログラムをコンパイルする際に
cc ファイル名.c -lm
のようにして、ccコマンドに "-lm" オプションを付けること。
まずは、以下のプログラムのひな形から出発してみなさい:
#include <stdio.h> #include <math.h> main() { float a,b,c ; float s,S ; ????? ????? ????? }
練習:桁の入れ替え
以下の動作を行なうプログラムを、C言語でコーディングしなさい(教科書の例題2.1に対応):
- 10進で2桁の自然数$n$を入力する
- $n$ の10の位と1の位を入れ替えた数を出力する(例えば$n=19$ならば、$91$を出力する)。
ヒント
1の位の数値は『10で割った余り(剰余)』で得られる。
C言語で整数の剰余を求める演算子は %
である。
『nを10で割った余りを変数oneに代入する』という動作は、C言語では
となる。
2桁目の数字(34なら3)は、(整数同士の計算として)n
を10で割れば得られる。
ひな形
#include <stdio.h> main() { int n,one,ten ; printf("2桁の自然数:") ; scanf("%d",&n) ; ???? ???? ???? printf("入れ替え後の数= %d\n",n) ; }
亀場で練習:三角形の描画
3辺の長さa,b,cを与えると、辺がちょうどその長さになっている三角形を亀場に描くプログラムを作成しなさい。
ヒント:
基本的な手順は
- (方向はともかく)距離aだけ進む。
- $\pi$から辺ab間の角度を引いた角度だけ左に回転する。
そうすると、回転角度は $\theta_1 = \pi - \arccos\left(\frac{a^2+b^2-c^2}{2ab}\right)$ラジアン。 - 距離bだけ前進する。
- $\pi$から辺bc間の角度を引いた角度だけ左に回転する。
そうすると、回転角度は $\theta_2 = \pi - \arccos\left(\frac{b^2+c^2-a^2}{2bc}\right)$ラジアン。 - 距離cだけ前進する。
逆余弦関数は、C言語ではacos( )
を用いる。
亀を回転させる際には、角度は(ラジアンではなくて)度で指定する必要がある点にも注意
(ラジアン値*180.0/M_PI
のように計算すればよい。ここで、記号M_PI
には円周率3.141592...があらかじめセットされている)。
「亀場」のタートルグラフィックスのコーナーも参照のこと。 Cプログラムと同じ場所にファイルturtle.hをダウンロードしておくことを忘れずに。
ひな形として、長さa,b,cの辺を120度ずつ左回りしながら描くプログラム例を示す。 このプログラムでは、辺の長さが等しい場合のみ三角形が構成される。
#include <stdio.h> #include <math.h> #include "turtle.h" main() { float a,b,c ; CON("localhost") ; CLR() ; RST() ; printf("a b c ? ") ; scanf("%f%f%f",&a,&b,&c) ; PD() ; FD(a) ; LT(120.0) ; FD(b) ; LT(120.0) ; FD(c) ; }