情報処理 II − 第2回

講義に入る前に、先週作成した shori2 にカレントディレクトリを移しましょう。

% cd shori2[Enter]

1.式


前回は出力ストリーム(画面)に送る文字を "..." で挟みました。 実は数値に関しては "..." を付けずに書くこともできます。

#include <iostream.h>

main() {
  cout << 1 << endl;             // 1 が出力される
  cout << 0.5 << endl;           // 0.5 が出力される
  cout << .5 << endl;            // 0.5 が出力される
  cout << 123 << endl;           // 123 が出力される
  cout << 1 << 2 << 3 << endl;   // 123 が出力される
}

//から行末まではコメント(注釈)と呼ばれ、 ソースプログラムを人間が読んだときにヒントになるようなメモを書きます。 コメントはコンパイルの際には無視されますが、 ソフトウェアの保守という点では非常に重要なものですから、 自分でプログラムを書くときは手間を惜しまず積極的にコメントを付けておいてください。

数値の代わりにを書けば、計算もしてくれます。 コンピュータは「電子計算機」って言うくらいですから、 「計算」は本業みたいなものです。

#include <iostream.h>

main() {
  cout << "1+1=" << 1+1 << endl;  // 1+1=2 が出力される
}

上のプログラムをコンパイルして実行すると、 1+1=2 が画面に表示されます。 すなわち、プログラムの "1+1=" の部分はそのまま表示され、 1+1 の部分は計算されて 2 が表示されます。

C++ 言語において式は何らかの、 あるいはそれを演算子によって結合したものです。 この数には 0 や 15 といった定数の他、 変数関数があります。

式に用いる加減乗除の演算子は、 C++ では次のような文字で表します。 これらはほんの一例で、 C++ 言語には他にも いろいろな演算子が用意されています。

演算子
+加算
-減算
*乗算
/除算
%剰余(整数のみ)

( ) を使った、もっと複雑な計算もできます。

#include <iostream.h>

main() {
  cout << 3450 * (24.2 - 4936) / 33.6 << endl;
}

このプログラムをコンパイルして実行すると、 -504337 が表示されます(小数点以下が四捨五入されています)。

異なる演算子を組み合わせて使うときには、 演算子の優先順位に気を付けなければなりません。 同じ優先順位の演算子については左から順に計算されますが、 異なっているときは優先順位の高いものが先に計算されます。 次の例は左のものほど優先順位が高くなっています。 式全体を ( … ) でくくったものは、 一番優先順位が高くなります。

優先順位
( 式 ) * / % + -

課題2


23.66 * 33.244 / ( 21.22 - 15.7 ) を計算して答を表示するプログラムを作成してください。 メールにプログラムを実行したときに出力された内容を書き、 ソースプログラムを添付して tokoi まで送ってください。Subject:(件名)は kadai2 としてください。


2.変数


コンピュータの機能として、 「計算」とならんで重要なものに「記憶」があります。 コンピュータは記憶したデータを処理(計算など)することで結果を得ます。 プログラム中で一時的にデータを記憶しておくためには 変数を使います。

#include <iostream.h>

main() {
  int tate, yoko;

  tate = 4;    // 縦の長さ
  yoko = 5;    // 横の長さ

  cout << "縦 " << tate << " メートル、";
  cout << "横 " << yoko << " メートルの長方形の面積は ";
  cout << tate * yoko << " 平方メートル" << endl;
}

このプログラムをコンパイルして実行すると、 次のような出力を得ます。

縦 4 メートル、横 5 メートルの長方形の面積は 20 平方メートル

この tate、yoko が変数です。 tate や yoko はそれぞれ 4 と 5 という整数値と同じように使われています。 tate * yoko で面積を求めています。


課題3


直方体の体積は「縦×横×高さ」で求められます。 上の例にならって、tate = 4、yoko = 5、takasa = 6 の直方体の体積を計算するプログラムを作成してください。 メールにソースプログラムを添付して tokoi まで送ってください。Subject:(件名)は kadai3 としてください。


2.1.変数定義

変数を使用するには、プログラム中でその変数を使用する前に、 そのことを定義しなければなりません。 変数はデータを格納する入れ物のようなものですから、 変数定義の時に中に入れることのできるデータの種類を指定します。 これをデータ型と言います。

int tate, yoko;

この定義により、これより後で tate と yoko という二つの int 型の変数が使えるようになります。この int はあとに列挙された変数が int(整数)型であることを示します。


2.2.変数名

tate や yoko のような、 変数に付けた名前のことを変数名と言います。 変数名は好きなように決めてもらえばいいんですが、 決め方にはいくつかの規則があります。

次の名前は変数名として使えます。

average// データの平均
pai// 円周率
number_of_students// 学生数
DayOfTheWeek// 曜日

次の名前は変数名として使えません。

3rd_entry// 数字ではじまっている
all$done// 使えない文字($)がある
the end// 空白が入っている
int// キーワード

アンダースコア "_"もアルファベットと同様に使用できますが、 コンパイラやリンカ、あるいは標準ライブラリで使用する名前との衝突を避けるため、 それを先頭に置いたり二つ以上続けたりしないでください。

_list// 先頭がアンダースコアなので×
next__word// アンダースコアが二つ続いているので×
third_entry// これは○
the_end_// これも○

キーワードには次のようなものがあります。

asm auto break case char catch
class const continue default delete do
double else enum extern float friend
for goto if inline int long
new operator private protected public register
return short signed sizeof static struct
switch template this throw try typedef
union unsigned virtual void volatile while

また変数名は、 その変数がいったい何のために使われているのかが分かるように付けてください。 これもソフトウェアの保守の点でとても重要です。


2.3.データ型

基本的なデータ型には次のようなものがあります。

char
文字型。実は整数型の一種で、演習で使用しているコンピュータでは -128〜127 の間(すなわち 8 ビット)の整数値を格納できます。 普通は文字(コード)の格納に用います。
short
短精度整数型。これも整数型の一種で、演習で使用しているコンピュータでは -32768〜32767 の間(すなわち 16 ビット)の整数値を格納できます。
long
長精度整数型。これも整数型の一種で、演習で使用しているコンピュータでは -2147483648〜2147483647 の間(すなわち 32 ビット)の整数値を格納できます。 64 ビットの整数値を表現する long long 型というのも用意されています。
int
整数型。整数値を格納します。 値の範囲はコンピュータ (オペレーティングシステムあるいはコンパイラ) によって異なりますが、演習で使用しているコンピュータでは と同じ -2147483648〜2147483647の間(すなわち 32 ビット)の整数値を格納できます。 8 ビットや 16 ビットのコンピュータでは short と同じである場合もあります。
float
実数型。小数点を含む実数を格納します。演習で使用しているコンピュータでは -3.4×1038〜3.4×1038の間の実数値を格納できます。 また表現可能な数値の最小値は ±3.4×10-38 で、これより絶対値の小さな数値は 0 になります。 精度(有効数字)は 7 桁程度です。
double
倍精度実数型。これも小数点を含む実数を格納しますが、float よりも高精度です。演習で使用しているコンピュータでは -1.7×10308〜1.7×10308の間の実数値を格納できます。 また表現可能な数値の最小値は ±1.7×10-308 で、これより絶対値の小さな数値は 0 になります。 精度(有効数字)は 16 桁程度です。 なお、これよりさらに精度の高い long double 型というのも用意されています。

整数型(char, short, long, int)のデータ型には、 signed(符号付き)と unsigned(符号なし) という修飾子を付けることができます。 符号なし整数では負の数を取り扱うことができません。 signed/unsigned を付けない場合、演習で使用しているコンピュータでは signed として扱われます。 また short や long も実は int に対する修飾子で、int を省略したものです。 これらを列挙するとこの表のようになります。


2.4.代入

変数に値を入れることを代入と言います。

tate = 4;                   // tate に 4 を代入
yoko = 5;                   // yoko に 5 を代入
menseki = tate * yoko;      // menseki には 20 が代入される

したがって次のような代入はエラーになります。

tate * yoko = 20;           // エラー:tate * yoko は変数ではない

一方、代入自体も式なので、 次のような代入は可能です。

x = y = 20;                 // OK: x と y に 20 が代入される

y = 20 という式のは代入した値 (20) なので、それを x に代入します。

x = y + z = 20;             // エラー: y + z は変数ではない
x = y + (z = 20);           // OK:z = 20 が先に処理される

上の例の一つ目はエラーになってしまいます。 これは + の優先順位が = より高いために、 先に y + z を処理しようとするからです。 二つ目は ( ... ) によって z = 20 が先に処理され、 この式の値を y に加算した結果を x に代入します。


2.5.初期化

変数を定義するときに、 その変数に値を設定する(初期値を与える)ことを 初期化と言います。

double pai = 3.14159265359; // 円周率

代入はその文を実行する度に行われますが、 初期化はその変数が生成されたときにのみ行われます。 変数が生成されるタイミングについては後述します。


2.6.精度

変数には格納できる範囲を越えた値を代入することはできません。

#include <iostream.h>

main()
{
  short x, y, z;                // short 型の変数

  x = 200;                      // x に 200 を代入
  y = 300;                      // y に 300 を代入

  z = x * y;                    // z には 60000 が入るはず

  cout << z << endl;            // z を出力
}

上の例では z には 60000 が入るはずですが、 これをコンパイルして実行すると -5536 という値が出力されます。 これは z が short 型のために 200 * 300 という計算の結果を格納できず (short の範囲は -32768〜32767)、 桁溢れ(オーバーフロー)が発生しています。

実数はコンピュータの内部で指数表記で表されるので、 このような問題は発生しにくいのですが、 逆に実数には次のような性質があります。

#include <iostream.h>

main()
{
  float f;                      // float 型の変数

  f = 0.1;                      // f は 0.1
  cout.precision(16);           // 実数の出力精度を 16 桁に設定
  cout << f << endl;            // f を出力
}

上の例で使用している cout.precision(16) は実数を出力ストリームに出力する際に、 16 桁の精度で数字に変換するよう指定します。 このプログラムをコンパイルして実行すると、0.1 ではなく 0.1000000014901161 が出力されます。 この理由は 0.1 を二進数で表そうとすると循環小数になってしまうからです。 コンピュータにおいて 0.1 という実数値は、 実は 0.1 の近似値なのです。

次に、この f に 0.000000001 を足してみましょう。

#include <iostream.h>

main()
{
  float f, g;                   // float 型の変数

  f = 0.1;                      // f は 0.1
  g = f + 0.000000001;          // g は f より 0.000000001 多いはず
  cout.precision(16);           // 実数の出力精度を 16 桁に設定
  cout << f << endl;            // f を出力
  cout << g << endl;            // g を出力
}

これをコンパイルして実行すると、 f も g もともに 0.1000000014901161 であることがわかります。 float 型の精度は 7 桁程度しかないので、 格納されている値にそれより 8 桁以上小さな実数値を加えても 表現できないのです。


課題4


半径 r の球の体積 v は v = 4π×r3÷3 で求められます。 円周率πを pai = 3.14159265359 とし、r = 4.5 の時の v を計算して、 それを出力するプログラムを作成してください。 なお v の計算式を C++ のプログラムの形式で書くと、 たとえば次のようになります。

v = 4.0 * pai * r * r * r / 3.0;

メールにソースプログラムを添付して tokoi まで送ってください。Subject:(件名)は kadai4 としてください。


3.定数


1 とか 'a' などはある定まった値を持っているので、 定数と呼ばれます。


3.1.整数定数

整数定数は 1 とか 123 のように数字のみから構成されたもので、 int 型の定数です。 もし値が int 型の範囲に収まらなければ、 long あるいは unsigned long 型になります。

long 型の定数には 1l や 123L のように、 数字の後に lL という接尾辞を付けます。 また unsigned 型の定数には uU という接尾辞を付けます。 unsigned long の定数の接尾辞は ul または UL です(uL, Ul lu, Lu, lU, LU でも構いません)。

なお、最初の数字が 0 の場合は八進数になります。014 は十進数の 12 です。 最初の2文字が 0x または 0X の場合は十六進数になります。 0xc は十進数の 12 です。

整数定数
10進数 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
8進数 000 001 002 003 004 005 006 007 010 011 012 013 014 015 016 017
16進数 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf

3.2.文字定数

'1' や 'a' のように1文字を '...' (シングルクォーテーションマーク) ではさんだものを文字定数と呼びます。 これはchar 型の定数です。 文字リテラルとも呼ばれます。 この値は文字コードと呼ばれる整数値で、 そのコンピュータにおいてその文字に割り当てられた 番号に相当します。

#include <iostream.h>

main() {
  char c, d;
  int i;

  c = 'A';         // c に A の文字コード (65) を代入
  d = c + 1;       // d は 66、すなわち B の文字コードが入る
  i = d;           // i も 66

  cout << d << endl;  // B が出力される
  cout << i << endl;  // 66 が出力される
}

上の例では、char 型の変数 c に代入した 'A' という文字の文字コードに 1 を加えたものを、char 型の変数 d に代入しています。さらに d を int 型の変数 i に代入しています。d と i には同じ値が入っていますが、 char 型の変数を出力ストリームに出力すると、 その値の文字コードの文字が出力されるのに対して、 int 型の変数ではそれを数値と見なして数字の列に直してから出力されます。

改行を示す '\n' なども一つの文字です。他に次のようなものがあります。

制御文字定数
\r復帰
\n改行
\f改ページ(紙送り)
\t水平タブ
\v垂直タブ
\hバックスペース
\a注意喚起(ブザー)
\\\(バックスラッシュ1文字)
\''(一重引用符1文字)
\""(二重引用符1文字)
\??(疑問符1文字)

文字コードを直接指定するときは '\012' のように \ に続けて八進数を書くか、 '\xa0' のように \x に続けて十六進数を書きます。


3.3.文字列定数

"abcde" のように "..." (ダブルクォーテーションマーク) の間に0文字以上の文字の列を置いたものを 文字列定数と呼びます。 これは文字列リテラルとも呼ばれます。 これに含まれる個々の文字は前述の文字定数ですが、 文字列全体のデータ型は char * 型 (char 型のポインタ、 ポインタについては後述します)で、 整数などとは性格の異なるものです。


3.4.実数定数

1.0 とか 3. とか .5 のように小数点を含んだ数値や、 4e23(4×1023)のように 指数表記された数値は double 型の定数です。

1.3f や 2.2e-10f(2.2×10-10)のように f または F という接尾辞を付けたときは float 型の定数になります。 また 1.3l のように実数表記に l または L という接尾辞を付けたときは long double 型になります。


3.5.数字と数値

ちょっと次のようなプログラムを作成してみてください。 ファイル名は calc.cc とでもしておきましょう。

#include <iostream.h>

main() {
  cout << 1 << endl;
  cout << '1' << endl;
  cout << "1" << endl;
}

これをコンパイルしたあと実行してみてください。 プログラムの出力を確かめたら、これを次のように書き替えてください。 これをコンパイルして実行したらどういう出力が得られるでしょうか。

#include <iostream.h>

main() {
  cout << 1+1 << endl;
  cout << '1'+1 << endl;
  cout << "1"+1 << endl;
}

結果を見ると分かると思いますが、 '1' や "1" は 1 という整数値を表しているのではないのです。

1
これは整数定数です。
'1'
これは文字定数です。
"1"
これは文字列定数です。

コンピュータが内部では2進数でデータを表していることは知っていると思います。 しかし、画面に表示された 1 は数字という文字であって、 アルファベットなどと同じ種類のものです。 コンピュータの内部にある整数のデータは、 それを文字に置き換えることによって画面表示や印刷を行うことができます。

cout << 1 と言う処理は、1 という整数値をそれを表す十進数の文字(列)に変換して、 出力ストリームに送ります。これに対して cout << '1' と言う処理は、'1' という文字1文字をそのまま出力ストリームに送ります。 cout << "1" は "..." に挟まれた文字の列を続けて出力ストリームに送ります。

文字は実はコンピュータの内部では 文字コードと呼ばれる整数値で表されています。 '0' の文字コードは十進数で 48、'1' は 49、また 'A' は 65、'B' は 66 です。 したがって、'1'+1 という処理は、実際には 49+1 という整数計算を行っていることになります。 ところが '1' という文字定数に 1 という整数定数を加算したために、 答の 50 のデータ型は char ではなく int になってしまいます。 このため演算子 << によって整数から文字列への変換が行われてしまい、 50 が表示されてしまいます。

一方 "..." で挟まれた文字列は、複数の文字が並んだデータです。 これは一つのデータではないので "..." のは 並んだデータの先頭の文字が格納されている場所を示しています (ポインタ)。 したがって "1"+1 はこの "1" という文字が格納されている場所の次の文字の場所を示していることになります。 ところが次の文字はもうないので何も表示されないことになるのです。 なお、文字列に対する整数の加算や減算は可能ですが、 乗算や除算はできません。

このように、C++ では同じ演算子を使った処理でも、 実際の処理内容がデータ型によって異なります。 このことに常に気を配っておかないと混乱の元になりますから、 気をつけてください。


課題5


次のプログラムをコンパイルして実行すると、 何が出力されるでしょうか。 答とその理由を考察をメールで tokoi まで送ってください。Subject:(件名)は kadai5 としてください。 ソースプログラムを送る必要はありません。

#include <iostream.h>

main() {
  cout << "Wakayama University" + 9 << endl;
}