第2章 演算と型

テキスト『新・明解C言語 入門編』第2章の内容に関する補足説明を提供します.

2-1 演算

演算子とオペランド

List 2-1 についての説明は「printf 関数での % 文字の表示」のセクションまで続きます. テキスト p.24 の Column 2-1 も参照してください.

List 2-1 では printf 関数を使った類似の文が並びます. すべてを直接に入力するのは面倒ですね. コピーと貼り付けの方法を覚えましょう.

まず,GUI(上部のツールバー)を利用する方法を説明します. 一般的なワープロソフトと同様に, コピーしたい範囲(リージョン と呼ばれます)をマウスで選択します. 下図では printf("vx + vy = %d\n", vx + vy); という範囲を選択しています. 続いて,Emacs の上部にあるツールバーで, コピーのアイコン(下図では枠で囲っています)をマウスで左クリックします.

Copy Region

続いて, コピーを張り付けたい位置にカーソルを移動させます. カーソルを移動させたら,Emacs の上部にあるツールバーで, 貼り付けのアイコン(下図では枠で囲っています)をマウスで左クリックします. これで下図のように貼り付けができます.

yank

次に,キーボートだけを使う方法を説明します.

演習室の Mac にインストールされている Mac OS(El Capitan)では, 以下で説明する, コントロール [Ctrl] キーを押しながらスペース [Space] キーを押すという操作が, ショートカットとして使われてしまっています. これを無効にしておきます. 画面下の Dock にある「システム環境設定」アイコンをクリックして, システム環境設定のウィンドウを開きます. 「ショートカット」の設定で,「入力ソース」を選択します. 「前の入力ソースを選択」のチェックを外します.

macOS でのショートカット C-Space 無効化

下図での, printf("vx - vy = %d\n", vx - vy); という文をコピーしたいとしましょう. コピーをしたい範囲の先頭にカーソルを置きます. 下図では printf の p の上です. ここで,コントロール [Ctrl] キーを押しながら, スペース [Space] キーを押してください. この操作を C-Space と表記します. すると,Emacs の一番下にあるミニバッファに, Mark set(あるいは,Mark activated)と表示されます. コピーをする範囲の先頭位置が「マーク」されたという意味です.

Mark set

続いて,コピーをする範囲の末尾を指定します. コピーをする範囲のすぐ右側までカーソルを移動させてください. printf("vx - vy = %d\n", vx - vy); という範囲をコピーしたいのなら, この文の最後のセミコロン(;)の右側にカーソルを移動させます. これで範囲の指定がされたことになります. コントロール [Ctrl] キーを押しながら [e] キーを押すと, カーソルを行末に移動させることができます. この操作(C-e)は覚えておくとよいでしょう. ちなみに,行の先頭にカーソルを移動させる操作は,C-a です.

コピーしたい範囲(マークがセットされた位置から, カーソルの現在位置まで)を指定できたら, エスケープ [Esc] キーを押して指を離し, 続いて [w] キーを押してください. この操作を M-w と表記します. [Esc] キーを押しながら同時に [w] キーを押すのではなく, [Esc] キーを押したら指を離すことに注意してください. これで,指定した範囲がコピーされます. M-w でなく C-w([Ctrl] キーを押しながら [w] キーを押す)とすると, 「コピー」ではなく「切り取り」になります.

コピーを張り付けたい位置にカーソルを移動させます. カーソルを移動させたら,[Ctrl] キーを押しながら [y] キーを押します. これで下図のように貼り付けができます.

yank

乗除演算子と加減演算子

Table 2-1 で説明されているように, a を b で割った剰余を求める剰余演算子 (modulus operator )% は,a と b が整数でなければ使用できません. この点が他の乗除演算子および加減演算子とは異なりますので, 注意してください.

Column 2-1(p.24)で説明されているように, 負の数を含む割り算での計算結果は処理系に依存します. 負の数を含む割り算は避けた方が無難です.

printf 関数での % 文字の表示

半角の % ではなく,全角文字の%でしたら, printf 関数での書式文字列の中でそのまま使用できます. しかし,これは間違いのもととなるので避けた方が無難でしょう.

最下位桁の値を求める

このセクションについての補足説明は現在のところありません.

複数の変換指定

演習 2-1 での百分率(%)の計算は,式を


x / y * 100

と書いてしまうとうまくいかないことに注意してください. x と y はいずれも int 型の整数ですから,あまりは切り捨てられてしまいます. 例示されているように,x = 54,y = 84 とすると, x / y = 0 あまり 54 ですから, x / y という計算の結果は 0 になってしまいます. 0 を 100 倍すると 0 ですから, 出力は「x の値は y の値の0%です」となってしまいます. どうすれば正しい出力が得られるか考えてください.

このようなことを考えなければならないのは, x / y が整数値になってしまうからですね. 割り算 x / y の計算結果が実数値となるようにすれば, x / y * 100 と書いて問題ありません. このように改良したプログラムは演習 2-5 で書きます.

単項の算術演算子

このセクションについての補足説明は現在のところありません.

代入演算子

このセクションについての補足説明は現在のところありません.

式と代入式

このセクションについての補足説明は現在のところありません.

式文

このセクションについての補足説明は現在のところありません.

2-2 型

平均値を求める

List 2-5 は,2つの数値の平均値の,整数部分だけを表示します. 小数部分も表示させるプログラムを List 2-9 で書きます.

List 2-6 のプログラムは, printf 関数の書式文字列の中に空白が含まれています. これは,表示される4行のメッセージでの, 対応する文字の位置を揃えるためです. しかし,演習室の Mac で, Emacs を使って List 2-6 のとおりにプログラムを書こうとすると, 空白をうまく調整できません. テキストで印刷されているプログラムでは, 全角文字および半角文字それぞれにおいて文字幅が等しくなっています. さらに,全角文字は半角文字ちょうど2つ分の幅になっています. Emacs では文字幅がそのようになっていないので, テキストのとおりに空白を入れることができないのです.

フォントを変更します. モニターの最上段に表示されている Emacs のメニューで, Options をマウスでクリックしてください. Set Default Font... という項目が見えますので, これをマウスでクリックしてください.

Set Default Font...

Fonts という小さいウィンドウが現れます. 現在のフォントは Menlo になっていると思います.

Menlo Regular

日本語フォントの MS ゴシックか,MS 明朝にフォントを変更してください.

change font to MS Gothic

これで,テキストで印刷されているとおりに空白を入れて, List 2-6 を書くことができます. 下図はフォントを MS ゴシックに変更したものです.

List 2-6 Gothic font

List 2-6 のプログラムでは,変数 n は整数の int 型で宣言されているのに, 9.99 という実数が代入されています. そのため,このプログラムをコンパイルすると, 下図のように警告が出ます. この警告は,第1章の演習 1-4 で, int 型の変数を実数で初期化したときに出されたものと同じです. 実数(double)型の数 9.99 から整数(int)型の数 9 へと, 暗黙の型変換(implicit conversion)が行われたと言っています.

List 2-6 コンパイルでの Warning

コンパイルでは警告が出ますが,実行ファイルは作成されます. このプログラム(list0206.c)を保存したディレクトリの内容を, ターミナルで ls コマンドを使って表示すると, 実行ファイル(list0206)が作成されていることがわかります. ファイル名の後についているアスタリスク(*)は, そのファイルが実行ファイルであることを示しています.

List 2-6 の実行ファイル

実行してみると問題なく動作しますが, 暗黙の型変換が行われていることがわかります. 下図のように,int 型変数 n の値は 9 になっています. 実数 9.99 の小数部分が切り捨てられ, 整数部分だけが n に代入されたのです.

n / 2 の結果が 4 となっている理由はもう理解できますね. n = 9 ですから,n / 2 = 4 あまり 1 となります. 整数どうしの割り算では,剰余は切り捨てられますので, n / 2 の値は 4 となります.

List 2-6 の実行

型とオブジェクト

このセクションについての補足説明は現在のところありません.

整数定数と浮動小数点定数

このセクションについての補足説明は現在のところありません.

double 型の演算

List 2-1(p.22)で整数値の四則演算(と剰余の演算)を行いました. List 2-7(p.31)は,その実数バージョンです. 実数の四則演算を行います. 実数については剰余演算子 % は使用できないことに注意してください.

型と演算

List 2-8 で,d1 は double 型の変数として宣言されています.


d1 = 5 / 2;

という代入を行ったとき, 5 / 2 という計算の結果は, 2.5 ではなく 2 であるということに注意してください. d1 = 5 / 2; と書いても, d1 は演算をコントロールするわけではありません. しかし,2 (5 / 2 の結果)を d1 に代入するときは, 実数値として代入されます. d1 は実数の箱なので, この箱に入っているものは実数値として扱われます.

List 2-8 をいろいろ変えて実験してみてください. これは演習 2-4 になります.

キャスト

テキストでも述べているように, List 2-9 で整数の 2 でなく実数の 2.0 で割り算を行っているのは, 演算の結果を実数(double 型)にするためです. 整数の 2 で割り算を行うと,演算の結果は整数(int 型)になり, printf 関数での変換指定 %f と不一致が生じます. このため,コンパイルで下図のように警告が出されます.

List 2-9 のコンパイルでの Warning

コンパイルによって実行ファイルは作成されますが, 実行してみるとおかしな結果になることがわかります.

List 2-9 の実行

演習 2-5 で要求されているプログラムは,List 2-10 をまねて, 下図のように書けばよさそうに思うかもしれません,

演習 2-5 のプログラム

しかし,このプログラムは期待通りに動きません. 実行すると下図のようになります.

演習 2-5 の実行

何がいけないのでしょうか? 変数 a と b はともに整数(int)型なので, 割り算 a / b の結果も整数になってしまうことに注意してください. 小数部分は切り捨てられてしまうのです. 上図の実行例では,54 / 84 = 0.6428... ですから, a / b の結果は 0 ですね. 整数型の 0 を実数型にキャストしても,0 は 0 です. そのため,出力される結果は 0.000000% となってしまいます.

どのようにプログラムを書けばいいか,考えてみてください.

変換指定

List 2-11 をテキストのとおりに実行したら, 変換指定についてもう少し試してみましょう. まずは,3つの整数の合計の出力で,0 フラグを試してみましょう.


printf("それらの合計は%05dです。\n", sum);

としてみてください.最小フィールド幅は 5 ケタですから, 左側に2つ 0 をつめて, 00191 と出力されます(テキストの例にある3つの数値を入力した場合).

実数値を出力する場合の最小フィールド幅には, 小数点文字(.)もカウントされます. これは List 2-12 で確認してください.

次に,やはり3つの整数の合計の出力で, 精度(変換指定においてピリオドの次に書く数値)を指定してみましょう.


printf("それらの合計は%.5dです。\n", sum);

としてみてください.この結果も 00191 となります. テキスト p.354 からの,printf 関数の仕様を参照してください. 変換指定子が d 変換(整数を10進数で表示)の場合, 精度は「出力すべき最小の桁数」となっています(p.355). 最小フィールド幅を 5 桁と指定した場合と異なり, あまった桁には 0 が詰められます.

変換指定子が f 変換(実数を10進数で表示)の場合, 精度は「小数点文字の後ろに出力すべき桁数」を意味します(p.355). List 2-11 での,平均値の出力(63.7)を見てください. 精度の指定が 1 ですので,小数点以下第1位まで出力されています.

List 2-12 で,さまざまな変換指定の結果をよく吟味してください. よく使うパターンは自然に覚えてしまいますが, 意識的に覚える必要はありません. 変換指定の意味をよく理解し, 必要に応じて printf 関数の仕様を参照すれば, 自分の望む出力ができるようにしてください.

まとめ

キーボードから入力された数値を,scanf 関数を使って変数に入れるとき, これまでの例ではひとつの数値だけを扱ってきました. まとめに提示されているプログラムでは, 2つの数値の入力を受け付け,それぞれを変数 a および b に入れています. このように,scanf 関数は複数の値の入力を扱うことができます. 2つの整数を受け付け,int 型の変数 a および b にそれぞれ入れる場合, まとめのプログラムにあるように,


scanf("%d%d", &a, &b);

とします.キーボードから2つの数値を入力するときは, 2つの数値をスペースあるいはタブで区切り, 最後に [Return] キーを押します. あるいは,最初の数字を入力して [Return] キーを押し, 次の数字を入力して [Return] キーを押してもいいです.

入力の操作は明示的に指示した方がよいでしょう. すなわち,テキストにあるように.


printf("2つの整数 a と b の値:");

とするのではなく,


printf("2つの整数値を,スペースあるいはタブで区切って入力してください:");

とした方がよいでしょう.