「Springs of C」 サンプルページ この本の定価・判型などは,以下の URL からご覧いただけます. http://www.morikita.co.jp/books/mid/084951 ※このサンプルページの内容は,初版 1 刷発行当時のものです. ᧄᦠߢ⚫ߒߚࠨࡦࡊ࡞ࡊࡠࠣࡓ╬ߪ㧘 ਅ⸥ߩർ ߩࡎࡓࡍࠫ߆ࠄ࠳࠙ࡦ ࡠ࠼ߢ߈߹ߔ㧚 ޓhttp://www.morikita.co.jp/soft/84951/ i はじめに 本書は高校や高専,大学などでプログラミングに初めて触れる人を対象としたプロ グラミング言語 C の入門書です.実際の開発ではオブジェクト指向言語が幅をきかせ ていますが,プログラミングの考え方やコンピュータの動作などをより深く理解する ためには,いまだ C 言語はもっとも重要なプログラミング言語の一つであると思いま す.開発効率やセキュリティなどの点で,C 言語が現代的なソフトウェア開発の要求 を満たせるかというと正直なところそうではないのですが,あえて C 言語のような 「古典」を身につけ,不備をよくわかったうえでオブジェクト指向言語に進むことで, それらの現代的な言語のありがたみがさらに深く理解できるようになるともいえます. また,筆者は教育現場でプログラミングを教える立場にありますが,いざ講義を担 当してみると,C 言語の入門書はそれこそ星の数ほどあるものの,自身の講義にはど れも一長一短で教科書の選択に悩むところでありました.そこで,自分の好みに合う 教科書を自分で書いてしまったのが本書です.アカデミックな座学で端から順番に身 につける C 言語というよりは,筆者自身が子供の頃に,雑誌に掲載されたゲームの プログラムを入力して遊びながらプログラミングを身につけた過程を思い起こしなが ら,執筆しました.また,機種依存の部分はできるだけ排除し,本書を読み終えたあ とに,どのような新しい技術に出会ったとしても役に立つであろう本質を多く採り入 れるように努めました. 1 章では,読者の皆さんの手元のパソコンでプログラミングを行う準備を整えます. 統合開発環境などをあえて使わず,コマンドラインから手でコマンドを入力してもら うようにしています.一見面倒で非合理的な選択ですが,読者の皆さんにはぜひコマ ンドライン上での操作や UNIX コマンドに慣れていってほしいと思います.これから 先,OS のもっと深いところを学習するときや,サーバーの管理などをする際に,コ マンドラインの経験は必ず役に立ちます.1 章で準備したこの環境下でプログラムを 作成して実行するまでの手順については,2 章で説明しています. 3 章から 13 章では,まず C 言語そのものについて説明します.自分自身が C 言語 を学習したときのことを思い出しながら,ひっかかりそうな部分はなるべく先回りし て解説するようにしました.流れに沿って,一つひとつ実行して確かめながら進むよ うにしてください.本書では,最終的に小さなゲームを完成させることを目標にして います.それを念頭において,これらの前半の章でも各種の具体例はゲームに関連す ii はじめに るものを中心に取り上げています.基本の内容がクリアできたらどんどんと改造して 遊んでください.章末の「練習問題」には,基本的で一度は触れておいてほしい問題 を選んで入れました.基本事項が身についているかを確認するため,なるべくすべて の問題に取り組むことを推奨します.解答例は本書のサポート web ページ (→ ix ペー ジ) にて公開しています.章末の「研究課題」には,本文で触れられなかった発展的 な知識が得られるような内容を選んで入れてあります.そのような「脇の知識」をた くさんもっていることで,ほかの勉強をするときにも役に立ちますし,巡り巡って C 言語やコンピュータそのものの理解を深めることにもつながります.練習問題を解き 終えたら,さらなるレベルアップのためにもぜひ積極的に取り組んでみてください. 14 章から 19 章では,より実践的なプログラミングとして,小さなゲームを作るこ とに挑戦します.ゲームは,本格的なビジネスソフトなどからすれば低級にみられが ちな存在ですが,決してそうではありません.本格的なゲームは,画像処理やリアル タイム処理,割り込み処理など,高度な技術のかたまりです.それに,作っていて楽 しく,もっと改良したいと思えるというのは重要なポイントでしょう.本書では,画 面上にウィンドウや画像を表示する用途で GLUT と glpng というライブラリを用い ます.GLUT や glpng の使い方をマスターすることは本書の目的ではありません.も ちろん,これらの関数名や引数の意味を覚えていればすらすらとプログラムが書ける ようになるのでしょうが,これではいざ GLUT+glpng 以外の環境に足を踏み入れた ときにまったく応用が利きません.そうではなく,皆さんが普段使っているような, ウィンドウを開いてマウスで操作するプログラムはどういう仕組みで動いているの か,という本質を理解してほしいと思います.GLUT や glpng は,これらの本質を説 明する際の道具にすぎません.本書では,まずアナログ時計を作ります.ウィンドウ を開く,線を引くなど,ウィンドウアプリケーションのプログラミングにおけるひと とおりの流儀をここで身につけます.アナログ時計のプログラムができてしまえば, ここにマウスやキーボードからの入力を加えることで小さなゲームを作ることができ ます.ゲームに必要な割り込み処理やダブルバッファリングなどの方法は本書で説明 しています.これを出発点にして,自分の作品を作りあげるという楽しさを味わって ください. 「源泉」や「原動力」といっ 書名の“spring”には,春やバネといった意味のほかに, た意味があります.読者の皆さんが C 言語を身につける原動力として,そして C 言 語を身につけたあとのジャンプアップのためのバネとして,本書がお役に立てること を期待しています. 2012 年 1 月 筆 者 iii 目 次 1章 環境構築 1 1.1 Windows を使う場合の環境構築 ................................................. 1.2 Mac OS X を使う場合の環境構築 ............................................... 2 1.3 Linux を使う場合の環境構築 ...................................................... 3 2章 プログラムを書いて実行するまで 2.1 プログラムを記述する .............................................................. 2.2 コンパイルする ....................................................................... 2.3 HelloWolrd.c をよくみると… .................................................... 2.4 これからは… .......................................................................... 練習問題・研究課題 ......................................................................... 3章 変 数 1 5 5 6 7 8 8 10 3.1 宣 言 ................................................................................... 10 3.2 代入と演算 ............................................................................. 12 3.3 もう少しややこしい計算 ........................................................... 13 3.4 結果を表示する ....................................................................... 14 3.5 定 数 ................................................................................... 16 3.6 数の表現 ................................................................................ 17 3.7 情報の単位 ............................................................................. 18 3.8 論理演算 ................................................................................ 18 練習問題・研究課題 ......................................................................... 19 4章 文字列の「使い方」 4.1 文字と文字列 .......................................................................... 4.2 準 備 ................................................................................... 4.3 文字列の代入 .......................................................................... 4.4 文字列の表示 .......................................................................... 4.5 文字列の結合 .......................................................................... 4.6 文字列の長さ .......................................................................... 21 21 21 22 22 23 23 iv 目 次 4.7 文字列の比較 .......................................................................... 23 4.8 文字列操作のサンプル .............................................................. 23 4.9 文字列から数値への変換 ........................................................... 25 練習問題・研究課題 ......................................................................... 25 5章 簡単な入力 26 5.1 標準入力と標準出力 ................................................................. 26 5.2 数値の入力 ............................................................................. 26 5.3 複数のデータを一気に読み取る .................................................. 28 5.4 文字列の入力 .......................................................................... 29 5.5 scanf() の落とし穴 ................................................................. 29 5.6 作ってみよう−○○度判断プログラム (変数のキャスト) ................. 31 練習問題・研究課題 ......................................................................... 33 6章 条件判断 6.1 if 文 ....................................................................................... 6.2 if 文の短縮形 (条件演算子) ......................................................... 6.3 処理が 1 行だけのとき .............................................................. 6.4 switch∼case 文 ....................................................................... 練習問題・研究課題 ......................................................................... 7章 繰り返し 35 35 37 37 38 38 40 7.1 決まった回数の繰り返し ........................................................... 40 7.2 決まっていない回数の繰り返し .................................................. 41 7.3 乱数の発生− random() ............................................................. 43 7.4 作ってみよう−宝くじのシミュレーション ................................... 45 練習問題・研究課題 ......................................................................... 47 8章 関 数 49 8.1 関数の必要性 .......................................................................... 49 8.2 関数の使い方 .......................................................................... 49 8.3 プロトタイプ宣言 .................................................................... 53 8.4 ヘッダーファイル .................................................................... 53 8.5 ライブラリ関数 ....................................................................... 54 8.6 ローカル変数とグローバル変数 .................................................. 55 8.7 変数のスコープ ....................................................................... 56 目 次 v 練習問題・研究課題 ......................................................................... 57 9章 1 次元配列変数 59 9.1 配列変数がなかったら .............................................................. 59 9.2 配列変数の基本的な使い方 ........................................................ 59 9.3 配列変数の初期化 .................................................................... 60 9.4 配列変数の魔法 ....................................................................... 61 9.5 文字列とはなんだったのか ........................................................ 62 9.6 配列変数を関数に渡す .............................................................. 62 9.7 配列変数とセキュリティホール .................................................. 64 練習問題・研究課題 ......................................................................... 65 10 章 2 次元配列変数 67 10.1 多次元の配列変数 .................................................................... 67 10.2 2 次元配列変数の初期化 ............................................................ 68 10.3 作ってみよう−ロボットプログラミングゲーム ............................. 68 10.4 マップを作る .......................................................................... 69 10.5 マップを表示する .................................................................... 70 10.6 ロボットの向きを決める ........................................................... 72 10.7 プレイヤーにプログラムを入力させる ......................................... 73 10.8 ロボットを動かす .................................................................... 73 10.9 一定時間のウェイト− sleep() .................................................. 74 10.10 さらに拡張してみる ................................................................. 75 練習問題・研究課題 ............................................................................ 76 11 章 ファイル 78 11.1 ファイルへの書き込み .............................................................. 78 11.2 ファイルからの読み込み ........................................................... 80 11.3 1 バイトずつの読み書き ............................................................ 82 11.4 EOF の判定 ............................................................................ 83 11.5 一歩進んだファイル操作 ........................................................... 84 11.6 コマンドライン引数 ................................................................. 84 練習問題・研究課題 .......................................................................... 86 vi 12 章 目 次 構造体 87 12.1 作ってみよう− RPG の戦闘プログラム ....................................... 87 12.2 構造体を使ってデータを管理 ..................................................... 88 12.3 構造体の初期化方法いろいろ ..................................................... 90 12.4 基本的な戦闘プログラムのしあげ ............................................... 91 12.5 さらに拡張してみる ................................................................. 92 12.6 ファイルバトラーの作成 ........................................................... 93 12.7 FILE∗ の謎 ............................................................................. 94 練習問題・研究課題 .......................................................................... 95 13 章 ポインタ 96 13.1 アドレス ................................................................................ 96 13.2 変数が格納されているアドレスを知る ......................................... 97 13.3 アドレスを記憶する変数 ........................................................... 98 13.4 アドレスから間接的に変数を操作する ......................................... 99 13.5 関数の引数とポインタ .............................................................. 100 13.6 配列変数とポインタ ................................................................. 102 13.7 ポインタに 1 を足す ................................................................. 102 13.8 構造体とポインタ .................................................................... 104 練習問題・研究課題 .......................................................................... 106 14 章 アナログ時計の制作 107 14.1 コマンドライン版の時計 ........................................................... 107 14.2 GLUT によるプログラミングの基本 ........................................... 109 14.3 GLUT を用いたアナログ時計の制作 ........................................... 114 練習問題・研究課題 .......................................................................... 132 15 章 文字を描く 134 15.1 ビットマップフォントとストロークフォント ................................ 134 15.2 ビットマップフォントを使う ..................................................... 136 15.3 得点のようなものを表示してみる ............................................... 137 15.4 文字表示にひと工夫する ........................................................... 138 練習問題・研究課題 .......................................................................... 139 目 次 vii キャラクターを表示する 140 16.1 glpng について ........................................................................ 140 16.2 glpng の使用法 ........................................................................ 140 16.3 PutSprite() 関数 .................................................................... 145 16.4 半透明を制御する .................................................................... 146 16.5 2 次元のゲームマップを作る ...................................................... 147 練習問題・研究課題 .......................................................................... 148 16 章 17 章 マウス入力を扱う 149 17.1 マウス入力のサンプルプログラム ............................................... 149 17.2 クリックされた場所を知る ........................................................ 152 17.3 マウスカーソルが移動したことを知る ......................................... 152 17.4 マウスカーソルの出入りを知る .................................................. 153 17.5 マウスカーソルの場所を変更する ............................................... 154 17.6 マウスカーソルの形状を変更する ............................................... 154 17.7 作ってみよう−ライツアウト ..................................................... 155 練習問題・研究課題 .......................................................................... 157 18 章 キー入力を扱う 159 18.1 通常のキー入力の処理 .............................................................. 159 18.2 修飾キーの処理 ....................................................................... 161 18.3 glutGetModifiers() に関する補足 ............................................ 162 18.4 修飾キーの謎 .......................................................................... 163 18.5 特殊キーの処理 ....................................................................... 163 練習問題・研究課題 .......................................................................... 165 19 章 ミニゲームの制作 167 19.1 スケルトンを用意する .............................................................. 167 19.2 マップパーツを用意する ........................................................... 167 19.3 マップを描画する .................................................................... 168 19.4 背景を動かす .......................................................................... 169 19.5 キャラクターを乗せる .............................................................. 170 19.6 壁の判定をする (基本編) ........................................................... 170 19.7 壁の判定をする (上級編) ........................................................... 170 19.8 宝物の判定をする .................................................................... 174 19.9 マップ処理の応用 .................................................................... 175 viii 目 次 19.10 処理を軽くする ....................................................................... 175 19.11 敵を追加する .......................................................................... 177 19.12 敵をランダムに動かす .............................................................. 177 19.13 敵の移動を制限する ................................................................. 178 19.14 主人公と敵の衝突判定をする ..................................................... 178 19.15 敵に個性をもたせる ................................................................. 179 19.16 まとめ ................................................................................... 180 練習問題・研究課題 ............................................................................ 180 付録 GIMP でグラフィックパターンを作成する 182 A.1 GIMP のインストール .............................................................. 182 A.2 とりあえず一つ作ってみる ........................................................ 184 参考文献 188 索 引 189 ix 本書では以下のような表記法を用いています. ファイル名は file.c のようにイタリック体で表します. 変数名,関数名は printf() のようにタイプライタ体で表します. コマンド名は gzip のようにサンセリフ体で表します. コマンドラインからの入力は % cp a.txt b.txt のように表します.行頭の ‘%’ はプロンプト (ここに入力してくださいという目 印) なので,入力の必要はありません.環境によっては ‘$’ など,‘%’ 以外の記号 が使われていることがあります. 本書に記載されているバックスラッシュ ‘\’ は,Windows 環境では円記号 ‘Y =’ と して表示されることがあります. キーボード上の特定のキーを表すときは, Alt のように表します.たとえば, Ctrl + X は, Ctrl キーを押しながら X キーを押すことを表します. 本書で紹介したサンプルプログラム等は,下記の森北出版ホームページからダウン ロードできるようになっているので,ご利用ください. http/www.morikita.co.jp/soft/84951/ 本書のプログラムは,次の環境で動作確認しました. Windows: Windows 7 SP1/ Windows Vista SP2 / Windows XP SP3 Mac: Mac OS X 10.7.2 / Mac OS X 10.6.7 / Mac OS X 10.5.8 Linux : Ubuntu 10.10 / Ubuntu 11.04 / Ubuntu 11.10 5 2 章 プログラムを書いて 実行するまで 本書では 3 章から C 言語の解説に入っていきますが,その際に C 言語で書かれたプ ログラムをどこでどのように入力してどうやって実行させるかは,実は使用する環境に よって微妙に異なります.本章では,それぞれの環境でプログラムを書いて実行するま でについて説明します.コマンド入力による作業が中心となるので,本書で紹介してい る命令以外にも,ls, cd といった基本的な UNIX コマンドをいくつか学習する必要があ るかもしれません.このあたりは各自で UNIX の参考書 ∗1 をあたってください.最初の うちはかなり難解に感じるかもしれませんが,のちのち必ず役に立つことを保証します. 2.1 プログラムを記述する まずはプログラムを記述するところからで,これはどの環境でも共通なところです. 適当なテキストエディタ ∗2 を起動して次の枠内の内容を入力し,HelloWorld.c という 名前で適当なフォルダに保存してください ∗3 .入力にあたっては,次の点に注意が必 要です. 大文字と小文字は区別されます. 画面表示用の文字列やコメント文を除き,すべて半角文字で入力します. ファイル名の最後 (拡張子) は必ず “.c” にします. プログラムリストの一番左の列の数字は,本文での説明のための行番号なので, 入力の必要はありません. 8∼10 行目の行頭のスペースの数は何個でもかまいません (なくてもよい).頭を そろえておけば読みやすいというだけのものです.ちなみに, Tab キーを使う と簡単にそろえることができます. ∗1 手始めの 1 冊として,文献 [2] をおすすめします. ∗2 ワープロのように文書の見た目を操作する機能はないものの,文字の並びを素早く編集する機能に重点をお いた文書編集ソフトのことをテキストエディタ (もしくは単にエディタ) とよびます.Windows 付属のも のなら「メモ帳」があります.そのほか,Emacs,vi,秀丸エディタ[3] ,TeraPad[4] など高機能なエディ タが数多く存在するので,自分にあったエディタを見つけてください.プログラムを書く人間にとって,エ ディタほど重要な道具はありません. ∗3 Cygwin の場合は, 「C:\Cygwin\home\ ユーザ名」がホームディレクトリ (Windows のマイドキュメン トにあたる個人用のフォルダ) なので,ここに保存してください. 6 2 章 プログラムを書いて実行するまで ● 1 HelloWorld.c ● // 基本のプログラム 2 3 #include <stdio.h> 4 5 int main(int argc, char **argv) { 8 printf("Hello, World.\n"); 6 7 9 return(0); // 10 11 終了 } 2.2 コンパイルする HelloWorld.c のように,C 言語でプログラムを書いて保存した「なんとか.c」のこ とを「ソースプログラム」とか「ソースコード」といいます.ソース (source) とは 「源」の意味であって,食べ物にかける黒い調味料 (sauce) のことではありません. ソースプログラムは C 言語という人間が勝手に作った言葉で書かれているので,コ ンピュータの中の回路には何を言っているのかがわかりません.そこで,なんとか人 間が理解できる C 言語という言葉で書かれたソースプログラムを,コンピュータの中 の回路が処理できる形に翻訳してやる必要があります.この作業のことを「コンパイ ル (compile)」といい,コンパイルを行ってくれるプログラムのことを「コンパイラ (compiler) 」といいます.ソースプログラムは,コンパイルすることによって初めて コンピュータで実行できるようになるのです.コンパイルを行うには,コマンドライ ン上 ∗4 で次のように入力します.大文字小文字やスペースも正しく入力するよう気を つけてください. % gcc -Wall -o HelloWorld.exe HelloWorld.c これは,HelloWorld.c というソースプログラムを gcc というコンパイラでコンパイ ルして,コンピュータが実行可能な形式で HelloWorld.exe というファイルに出力す ∗4 Cygwin であれば,スタートメニューから [すべてのプログラム]→[Cygwin] →[Cygwin Bash Shell] の 順にクリックすると,Cygwin のコマンドラインが起動します. Mac OS X であれば,アプリケーション フォルダの中にユーティリティフォルダがあるので,その中の「ターミナル」を,Ubuntu であれば,1.3 節を参考にして「端末」を,それぞれ起動してください. 2.3 HelloWolrd.c をよくみると… 7 るという意味です ∗5 .画面に何も表示されないことがありますが,それは裏を返せば ソースプログラムに何も問題がなかったという意味なので,心配しなくても大丈夫で す.もし何かメッセージが表示されたら,まずメッセージをよく読み ∗6 ,ソースプロ グラムを修正してもう一度コンパイルをし直す必要があります.無事コンパイルでき たプログラムを実行するには, % ./HelloWorld.exe のように,先ほど出力したファイル (ここでは HelloWorld.exe) の名前の冒頭に “./” とつけます.次のように画面上に “Hello, World.” と表示されれば成功です. % gcc -Wall -o HelloWorld.exe HelloWorld.c % ./HelloWorld.exe Hello, World. 2.3 ← 表示された HelloWolrd.c をよくみると… ではもう一度,HelloWolrd.c をよくみてみましょう.C 言語では,文の終わりにセ ミコロン (;) をつけるというルールがあります.HelloWolrd.c でいうと 8 行目と 10 行目です.見た目上の行の終わりではなく,文法的な意味の区切りである文の終わり につけます.たとえば,for 文 (7 章参照) の中にはセミコロンがいくつかありますが, これはループのための変数を初期化したりカウントするための「文」が並んでいるた めです.難しいことはさておいて,セミコロンの有無はプログラムを作りながら覚え ていってもらえれば問題ありません. HelloWorld.c の 1 行目と 10 行目の “//” からその行の終わりまでは,注釈 (メモ書 き) です.“//” からその行の終わりまでは,何を書いてもコンパイルするときには無 視されるので,HelloWorld.c のように,自分がプログラムを作ったときのメモなどを 残しておくのに使います.注釈はプログラムのどこに書いてもかまいません.プログ ラムを書いている最中はすべての変数の役割や流れが頭の中に入っていると思います が,数週間後には忘れているはずなので,注釈をたくさん残しておくことをおすすめ します.ただし,慣れないうちは,たとえば, ∗5 コンパイル時に使用する-Wall -o のような付加的な指示 (コンパイルオプション) については,14.2.2 項 (→111 ページ) で説明します. ∗6 英語で表示されるかもしれませんが,知らない単語は読み飛ばしてでも全体に目を通し,おおまかな意味を くみ取るようにしましょう. 8 2 章 プログラムを書いて実行するまで x++; // x に 1 を足す といった注釈をつけてしまいがちです.このような「みればわかる」レベルの内容を こまめに書いてもあまり注釈の意味がありませんので,ある程度の命令の塊に, 「a の 平均値を b に代入する」くらいの注釈をつけておくのがよいと思います. 2.4 これからは… C 言語のプログラムを書いて実行するまでの流れはこれだけで,あとはエディタと コマンドラインを往復する作業となります.今後,自分でプログラムを作るときは, 次の start.c を適当なファイル名でコピーして,必要な部分をどんどんと書き足して いく形になります.このプログラムに書かれているのは,すべてのプログラムで共通 で,毎回同じことを書かなければならない部分です. ● 1 start.c ● #include <stdio.h> 2 3 int main(int argc, char **argv) { 6 // この部分に命令を並べる (ほかは毎回同じ) 4 5 7 return(0); 8 9 } 練習問題 1. ワープロソフトとテキストエディタの違いについて,説明してください. 2. 作成したプログラムを実行するために,なぜコンパイルという手順が必要なのか説明して ください. 3. 自分のエディタで,文字の検索や置換をしてみてください.たとえば,HelloWorld.c の 中の arg という文字列を bb という文字列に一括置換してみましょう.また,任意の行番 号にジャンプしてみてください.これらの作業は頻繁に使うことになりますが,プログラ ムが長くなってくると目視+手作業では大変です. 4. 多くのエディタには,プログラムの編集を後方支援するための機能があります.たとえ ば,C 言語の命令を強調表示してみやすくする機能などです.自分のエディタでこのよう な機能がないか,探して使ってみてください. 練習問題・研究課題 9 研究課題 1. 「コンパイラ型言語」と「インタープリタ型言語」について調べてみましょう.それぞれ どのようなメリット・デメリットがあるでしょうか. 2. C 言語はコンパイラ型言語とインタープリタ型言語のどちらに属するでしょうか. 3. Java や.NET Framework などは,仮想マシンとよばれる仕掛けを使って動作します.C 言語のプログラムを動作させる場合と,どのようなところが違うのでしょうか. 10 3 章 変 数 数学でおなじみの x や y といった変数は,C 言語でも重要な位置を占めています.コ ンピュータならではの事情もありますので,順を追って説明します. 3.1 宣 言 C 言語で x という変数を使いたければ,「x という変数を使いますよ」と事前にコ ンピュータに伝えておかなければなりません.これを「宣言 (declaration)」といいま す.また,その変数の「型 (type)」も同時に指定しなければなりません.どちらも数 学にはない考え方です.変数の名前は, 使える文字は A∼Z,a∼z,0∼9,アンダースコア (_) だけ 大文字と小文字は区別される 先頭に数字は使えない 長さは無制限 ∗1 というルールさえ守れば,自由につけることができます. 変数の型には何通りかありますが,とりあえず int 型だけ覚えておきましょう.int とは整数 (integer) のことです.この型の変数には 5 や −3 などの整数しか入れること ができず,2.5 や −0.81 などの実数は入れることができませんが,最初はこれだけで も十分です.具体的には表 3.1 の型が使えます.表 3.1 は代表的な例であり,扱える 数値の範囲や変数のサイズは環境ごとに異なるので,注意が必要です. 変数はいきなり使えるわけではなく,使う前にその変数の型と名前を int x; のように書いておく必要があります.これで int 型の変数 x を宣言したことになり,使 う準備が整います.この段階では「x を使います」と宣言をしただけであって,x の中 身には何が入っているかわかりません.宣言をした直後はゼロになっていると暗黙の うちに仮定してしまいがちですが,そうとは限らないので気をつける必要があります. ∗1 現在のコンパイラでは,一般に長さに制限はありませんが,古いコンパイラの中には,32 文字目以降を無 視するものもあります. 11 3.1 宣 言 変数を宣言する場所は,その変数を使うよりも前であれば,基本的にどこでもかま いません.しかし,プログラムのあちこちに宣言が散らかっているとプログラムが読 みにくくなってしまうので,処理の本体に入る前に,これから使う変数の宣言をすべ てまとめてしまうのがおすすめです.たとえば,strtest.c (→24 ページ) の 7∼8 行目 では,そのあとで使う変数の宣言をすべて main() の冒頭で済ませてしまっています. 表 3.1 ● 変数の型と扱える数値の範囲 型 その型の変数 x で扱える数値の範囲 サイズ (バイト) −2, 147, 483, 648 x 2, 147, 483, 647 の整数 4 −2, 147, 483, 648 x 2, 147, 483, 647 の整数 (長整数) 4 short −32, 768 x 32, 767 の整数 2 char −128 x 127 の整数 1 0 x 4, 294, 967, 295 の整数 4 unsigned long 0 x 4, 294, 967, 295 の整数 (長整数) 4 unsigned short 0 x 65, 535 の整数 2 unsigned char 0 x 255 の整数 1 1.17 × 10−38 |x| 3.40 × 1038 (有効数字 7 桁程度) 4 2.23 × 10−308 |x| 1.80 × 10308 (有効数字 15 桁程度) 8 int long unsigned int float double 表 3.1 の上四つの型の扱える数値の範囲をみると,プラス側とマイナス側で値の上 限 (下限) が 1 だけ違うことを不思議に思うかもしれません.これは,ゼロをプラス側 に含めているからです.たとえば,char 型であれば,プラス側とマイナス側のどち らも 128 通りの整数を数えることができますが,マイナス側が −1, −2, · · · , −128 の 128 通りであることに対して,プラス側が 0, 1, 2, · · · , 127 の 128 通りを割り当ててい るので,このようなことになるのです. float 型と double 型は,3.14 などの実数を扱うことができます. 1 と書いたら整 数,1.0 と書いたら実数として扱われることに気をつけてください.float 型のほう が扱える数値の範囲が狭いですが,処理はその分高速です.ゲームなどではそれ ほどの細かさは必要ないですし,少しでも速いほうがよいので float 型を使い,精 密な計算が必要になるプログラムでは double 型を使う,といったように,用途に 応じて使い分けるとよいでしょう.表 3.1 の double 型の説明には,数値の範囲は 「2.23 × 10−308 |x| 1.80 × 10308 (有効数字 15 桁程度)」と書かれていますが,こ れは 300 桁以上の数値のすべての桁が正確に計算できるというわけではなくて,最初 の 15 桁前後が正確で残りは誤差を含んでいる,という意味です. 12 3 章 変 数 int 型と long 型は,遠い昔は float 型と double 型のようなすみ分けをしていました が,32 ビットコンピュータが主流になり,ほとんどのコンパイラで同じサイズで扱わ れるという状況になりました. 3.2 代入と演算 変数への値の代入は, x = 5; のように行います.このイコールは「等しい」という意味ではなくて,変数 x に 5 を 代入するという意味で使われます.変数を宣言してすぐに値を代入することがわかっ ているのであれば, int y = 514; のように,ひとまとめにしてしまうこともできます. x に 2 を足すという場合は,次のように書きます.いったん右辺で (古い) x の中身 に 2 を足したものを計算し,その結果を左辺の (新しい) x に代入するという意味です. x = x + 2; C 言語ではこれを省略して, x += 2; のように書くこともできます.+ や − のように値を操作する記号のことを演算子と いい,多くの種類があります.基本的な演算子を表 3.2 に示します.前述の省略形は 表 3.2 ● 基本的な演算子 記号 +, −, ∗, / % 意味 それぞれ,+, −, ×, ÷ 剰余 (割り算のあまり).たとえば 10 % 3 の結果は 1. << 左ビットシフト >> 右ビットシフト.符号ビットの扱いは環境依存. & ビットごとの論理積 | ビットごとの論理和 ˆ ビットごとの排他的論理和 3.3 もう少しややこしい計算 13 表 3.2 の演算子に対しても使うことができ,たとえば x から 2 を引く操作を x -= 2; のように書くこともできます. 現実のプログラミングでは,1 を足したり引いたりする場面が多いので,その場合 は特別に, a++; b--; // // a = a + 1; と同じ b = b - 1; と同じ のように書くことができます.++を「インクリメント演算子 (increment operator)」 , −− を「デクリメント演算子 (decrement operator)」といいます. 3.3 もう少しややこしい計算 平方根や三角関数など,もう少し高度な数学関数もあらかじめ用意されています. 主な数学関数を表 3.3 に示します.これらを使うためには,プログラムの冒頭に #include <math.h> という 1 行を付け加えておき (この操作を「math.h をインクルードする」ということ があります),コンパイルするときに-lm というオプションを付け加えておきます. % gcc ... file.c ... -lm 表 3.3 ● 主な数学関数 (引数 ∗2 と戻り値 ∗2 はともに double 型) 関数名 意味 sin(), cos(), tan() sqrt() y = sin(x); とすると,y に sin x を返す.cos(), tan() も同様.角度はラジアン単位で与える. √ y = sqrt(x); とすると,y に x を返す. pow() z = pow(x, y); とすると,z に xy を返す. log(), log10() exp() y = log(x); とすると,y に x の自然対数を返す. log10(x) は 10 を底とする x の対数を返す. y = exp(x); とすると,y に ex を返す (e = 2.7182818... は自然対数の底). ∗2 表 3.3 の x のように関数に与える値のことを「引数」といいます.また,表 3.3 の y のように関数から戻っ てくる結果のことを「戻り値」といいます.詳しくは 8.2 節 (→52 ページ) を参照してください. 14 3.4 3 章 変 数 結果を表示する プログラムの中で計算した結果を画面に表示してみましょう.C 言語には printf() という便利な関数が用意されています.基本的な使い方は以下のとおりです.ダブル クォーテーション " " で囲んだ文字列がそのまま画面に表示されます. printf("生麦生米生卵\n"); 末尾の ‘\n’ はこの 2 文字で「改行する」という意味の特別な 1 文字 (エスケープシー ケンス) です.C 言語の入出力命令で使える代表的なエスケープシーケンスを表 3.4 に示します.これを明示的につけないと,改行がされないことになるので,次の 2 行 は先ほどの 1 行と同じ結果になります. printf("生麦生米"); printf("生卵\n"); 表 3.4 ● C 言語の入出力命令で使える 代表的なエスケープシーケンス 文字 意味 \n 改行 \t タブ記号 \\ \ 記号そのもの \" ”記号そのもの \’ ’ 記号そのもの \0 文字列の終端 (ヌル文字) さて,printf() が便利なのはここからです.変数に入っている値なども簡単に表 示させることができます.たとえば,int 型の変数 m に所持金が入っているとして, m = 1337; printf("所持金は%d 円です.\n", m); とすれば, 所持金は 1337 円です. のようになります.%d というところがそっくり m の中身に置き換わっていることが わかりますね.これを使えば,ゲームの得点や状態表示も簡単に行えそうです.d の ことを「変換指定子 (conversion specifier)」といいます.ほかに使える変換指定子に 3.4 結果を表示する 15 ついて,表 3.5 に示します.次の例のように,このような指定はいくつでも追加する ことができます. printf("鶴は%d 年,亀は%d 年.\n", x, y); 表 3.5 ● 変換指定子 文字 意味 対象の型 d 符号つき 10 進整数 int u 符号なし 10 進整数 unsigned int ld 符号つき 10 進長整数 long lu 符号なし 10 進長整数 unsigned long x 符号なし 16 進数 (0∼9,a∼f) char, int X 符号なし 16 進数 (0∼9,A∼F) char, int o 符号なし 8 進数 char, int f 符号つき実数 float, double e 符号つき実数の指数表示 float, double c 文字 char s 文字列 char [ ] p ポインタのアドレスを 16 進表示 ポインタ型 % %%で%記号そのものを出力 変換指定子の前に数字を入れることで,表示の桁数をコントロールできます. printf("値は%7d です.", x); とすると,「値は」と「です.」の間に 7 文字分のス ペースをとって,その中に右詰で x の数値を当てはめます.printf("値は%07d で す.", x); とすると,先ほどと同じですが,7 文字分の余白部分をゼロで詰めます. printf("値は%-7d です.", x); とすると, 「値は」と「です. 」の間に 7 文字分のス ペースをとって,左詰で x の数値を当てはめます.x = 415 としたときのそれぞれの 実行例を次に示します.ここでは空白を ‘’ で表しています. 値は415 です. 値は 0000415 です. 値は 415です. ← %7d ← %07d ← %-7d 実数の場合,桁数指定は若干面倒です.printf("値は%8.4f です.", a); のよう にすると, 「値は」と「です. 」の間に 8 文字分のスペースをとって,その中で小数点以 下を 4 桁分確保するという意味になります.小数点そのものやマイナスの符号にも 1 文字分必要な点に注意が必要です.a = −1.602 としたときのそれぞれの実行例を次 に示します. 67 10 2 次元配列変数 章 配列変数は確かに便利なものですが,少し見渡してみると,普段われわれが接する データとしては表形式のものがとても多いことに気づきます.オセロや将棋などのボー ドゲーム,名簿,電話帳,…と,例を挙げればきりがありません.このような表形式の データを 9 章で勉強したような 1 次元配列で管理するのは少々面倒です.できれば,縦 3 行目横 5 列目のデータというように直接データに手を出せたほうが,直感的でわかり やすいですね.そこで本章では,小さなゲームを題材にして,表形式のデータを扱える 2 次元配列変数について勉強します. 10.1 多次元の配列変数 1 次元配列変数を int x[4]; と宣言すると,図 10.1(a) のように 0 から始まる添字 をもち,四つ分の領域をもつ配列変数 x が用意されます.ここで,x[0] は A,x[1] は B,…です (整数変数ですが,便宜上,中身はアルファベットで区別します). 2 次元配列変数の宣言は,1 次元配列変数の宣言にそのままもう一つ添字の指定をつ け加えた格好をしています.次のように 2 次元配列変数 y を宣言すると,図 10.1(b) のような縦方向に三つ,横方向に四つの領域をもつ配列変数 y が用意されます. int y[3][4]; どちらの方向にもゼロから始まる添字が割り当てられるので,図 10.1(b) で A が 入っているのは y[1][2] となります. (a) 1 次元配列 (b) 2 次元配列 図 10.1 ● 1 次元配列と 2 次元配列 68 10 章 2 次元配列変数 2 次元配列変数と同様にして,宣言のときに添字を増やせば,3 次元配列変数など の多次元配列変数も作ることができます.あまり使う場面は多くありませんが,数値 計算などで多次元配列変数を使う場面も出てくるでしょう. int v[2][9][5]; int w[7][7][3][6]; // // 3 次元配列 4 次元配列 10.2 2 次元配列変数の初期化 もちろん 2 次元配列変数も,宣言と同時に初期化を行うことができます.この場合 は,1 次元配列変数の初期化子を中括弧の中に並べた形で行います. int a[2][5] = { { 6, 28, 496, 8128, 33550336 }, { 12496, 14288, 15472, 14536, 14264 } }; 2 次元配列変数を「1 次元配列の配列」と考えれば,五つの要素をもつ 1 次元配列 変数のかたまりを二つ並べたものが a[2][5] です.int a[][5] = { · · · }; のように 最初の要素を省略することもできます. 10.3 作ってみよう−ロボットプログラミングゲーム 2 次元配列変数の勉強をかねて,ちょっとしたゲーム robo.c を作ってみましょう. まずは図 10.2 に動作例を示します.縦と横がそれぞれ 20 マスある空き地があり,次 のような作りになっています. 1. 四方は壁 (‘#’) で囲まれている. 2. 空き地内の所々にも障害物の壁がある. 3. 左上にスタート (‘S’) がある. 4. 右下にゴール (‘G’) がある. 5. そのほかは草原 (‘.’) になっている. 空き地にはお宝 (‘$’) がいくつかあるのでこれをとってきたいのですが,爆弾 (‘∗’) もいくつも埋められており,ちょっと怖いです.そこで,ロボット「アットロボ 1 号」 (‘@’) にお宝を回収させることにしました.このロボットはリモコン式ではなく,プ ログラム式です.つまり,スタート地点からロボットを投入する時点で,お宝をすべ て回収してゴール地点に出てくるよう,動きをすべてプログラムしておかなければな りません.困ったことに,このロボットはそのとき向いている方向に 1 歩前進するこ とと,その場で右 90 度に向きを変えるという 2 種類の動作しかできません.ゲーム 10.4 マップを作る 69 開始時にプレイヤーに前進 (F) と右回転 (R) だけのプログラムを入力させて,ロボッ トが無事お宝を回収してゴール地点に到達できるようにするのがゲームの目的です. つまり,最初にロボットにプログラムを与えてあとは見守るゲームです.図 10.2 は, 最初右を向いて S の上にいる状態から FRFFFFRRRFRFF... というプログラムを与 えた例で,ロボットの歩いた場所が空白になっています. 図 10.2 ● ロボットプログラミングゲームの実行画面 10.4 マップを作る まずはゲームマップを作ってみましょう.この部分はゲーム開始直後に一度使うだ けなので,main() とは別の関数として作っておいたほうが main() 自体の見通しがよ くなるでしょう.20 × 20 の配列を用意し,10.3 節の仕様に沿って記号を配置してい くことになります.数パターンの記号を使い分けるだけなので,char 型の配列で十分 でしょう.縦 20 と横 20 の char 型の 2 次元配列を用意します. char map[20][20]; とりあえず map[][] の内容をすべて草原 (‘ . ’) で埋めておき,四方を壁 (‘#’) で囲み ます. 次にお宝をマップ中にランダムに配置します.あまり多すぎてもゲームが難しく なってしまうので,三つくらいでよいでしょう.ランダムに場所を決めて,そこが草 原であればお宝の記号 (‘$’) を配置します. 87 12 構造体 章 「構造体 (structure)」とはなんとも難しそうな単語が出てきたわけですが,これは 関係のありそうな情報をひとまとめにしたブロックを作るための仕掛けです.読者の皆 さんが C 言語を卒業して Java や C++などオブジェクト指向言語とよばれる現代的な プログラミングのパラダイムに触れるとき,クラス (class) という名前でこの構造体を 激しく進化させたものが登場します.しっかり身につけておきましょう. 12.1 作ってみよう− RPG の戦闘プログラム 市販の RPG によくある,コマンド (命令) 選択式戦闘プログラムを作ってみましょ う.登場するプレイヤーと敵 (ドラゴン) の各パラメータは,表 12.1 のとおりとしま す.凝り出すときりがないので,まずは単純に,表 12.1 の四つだけを扱うとします. 表 12.1 ● プレイヤーと敵のパラメータ パラメータ プレイヤー 敵 名前 ともやん (自分の名前) ドラゴン 体力 (HP) 180 165 攻撃力 70 75 防御力 60 73 プログラムを起動すると敵に遭遇し,プレイヤーは「戦う」か「逃げる」のいずれ かのコマンド選択を求められます. ドラゴン が現れた! ともやん (HP:180) 1 -> 戦う 2 -> 逃げる 1 Enter コマンド? > ← コマンドを入力 「戦う」を選ぶとプレイヤーからの攻撃が行われ,相手からも攻撃を受けます.双 方の攻撃が 1 回ずつ終わったところで,再びコマンド選択に戻ります. 88 12 章 構造体 ともやん の攻撃! ドラゴン に 31 のダメージを与えた! ドラゴン の攻撃! ともやん は 42 のダメージを受けた! ともやん (HP:138) 1 -> 戦う 2 -> 逃げる コマンド? > これを繰り返して相手を倒すと,プログラムは終了となります. ともやん の攻撃! ドラゴン に 35 のダメージを与えた! ドラゴン を倒した! もし体力がゼロになって戦いに負けてしまうと,この場合もプログラムは終了とな ります. ともやん の攻撃! ドラゴン に 36 のダメージを与えた! ドラゴンの攻撃! ともやん は 44 のダメージを受けた! ともやん は死んでしまった! 12.2 構造体を使ってデータを管理 さて,表 12.1 のようなデータを管理する際,これまでだったらどのように書くで しょうか.一番素直なやり方は,すべてのパラメータを個別の変数として定義するや り方でしょう. int player_hp = 180; int player_kougeki = 70; int player_bougyo = 60; int teki_hp = 165; int teki_kougeki = 75; int teki_bougyo = 73; // // プレイヤー 敵 しかしこれでは,その後のプログラムの改良がきわめて難しいものになってしまう に違いありません.魔法や道具を使った場合の処理を追加した場合,それらは当然関 数化したいものですが,その際はこんな風に書くことになります. 12.2 構造体を使ってデータを管理 89 use_item(player_hp, player_kougeki, player_bougyo); ... use_kougeki_magic(player_hp, ..., teki_hp, ...); 魔法や道具の関数を一つ増やすたびにこれらの引数をいちいち書くのは大変です. さらに,表 12.1 に,たとえば「素早さ」や「運のよさ」のようなパラメータを追加し た場合,既存の関数との変数のやりとりをすべて見直す必要が出てきてしまい,これ またかなり大変な修正になってしまいますし,ついついバグを埋め込んでしまう原因 にもなります. そこで,構造体の出番です.次の例をみてみましょう. struct status { char name[20]; int hp; int kougeki; int bougyo; }; // // // // 名前 体力 攻撃力 防御力 struct というのが構造体を作るためのキーワードで,status というのがここで作る 構造体の定義名です.構造体の定義名のことを「タグ (tag) 」といいます.ここでは, 名前・体力・攻撃力・防御力をひとまとめにした構造体を status という名前で定義し ています.構造体がもっているそれぞれの変数のことを「メンバー (member)」とい います.ただし,この段階では,status 構造体はこういうデータをもっていますよと いうことをコンパイラに教えただけで,メモリに構造体のデータ領域を確保したわけ ではないので,具体的に何かを代入することはできません.int 型の変数などと同様 に,使う前に適当な名前をつけて宣言をしておく必要があります.次のようにすると, 「その 2」が teki という名前で使え 「status 構造体その 1」が player という名前で, るようになります. struct status player; struct status teki; 毎回,struct status なんとか,と書くのが面倒ならば,typedef を使ってみましょ う.構造体の定義を書いたあとで一度だけ typedef struct status tsuyosa; と書いておけば,以後はあたかも int 型や double 型と同列に,tsuyosa 型ができたか のように書くことができます. 90 12 章 構造体 tsuyosa player; tsuyosa teki; // // struct status player; と同じ struct status teki; と同じ typedef を使うことで,player や teki といった名前がそれぞれの強さを表している ことが明確になり,構造体を使う場合よりもさらにプログラムの見通しがよくなって います. 構造体がもっているそれぞれのメンバーに値を代入するには,構造体の名前のあと にピリオド (ドット演算子 ) をつけてメンバー名を書きます. strcpy(player.name, "ともやん"); player.hp = 180; player.kougeki = 70; player.bougyo = 60; 構造体を引数にして関数を呼び出すには, use_kougeki_magic(player, teki); のようにしておき,受け取る側では void use_kougeki_magic(struct status a, struct status b) { printf("%s は %s に攻撃魔法を使った!", a.name, b.name); } のように使います.これで,関数の中で構造体のメンバーを参照することができるよ うになります.関数の中で構造体のメンバーを書き換えるためには,構造体のアドレ スを関数に渡し,ポインタ経由で操作をする必要があります (13.8 節参照). 12.3 構造体の初期化方法いろいろ 12.2 節では基本的な構造体の宣言方法を紹介しましたが,構造体の宣言と初期化の 方法にはいくつかバリエーションがあるので,ここでそれらを紹介しておきます. 次のように構造体の変数を宣言すれば, curry 構造体の変数をいくつでも作ること ができます.しかし,構造体変数を一つしか使う予定がないのであれば,このやり方 は少々まわりくどいように感じるかもしれません. 12.4 基本的な戦闘プログラムのしあげ struct curry { int karasa; // int ryou; // int omake; // }; 91 辛さ (1...5) 量 おまけの有無 struct curry otona; struct curry kodomo; 構造体変数を一つしか使わない場合は,構造体の型宣言と構造体変数の宣言をひと まとめにしてしまえます.この場合はタグ名も省略可能です. struct { int karasa; int ryou; int omake; } jibun; 構造体の型宣言と構造体変数の宣言は同時に行うこともできます.次の例は curry 構造体を定義し,それと同時に構造体変数 otona も宣言しています.もちろん,curry 構造体はここできちんと定義されているので,あとから struct curry kodomo; のよ うにして別の構造体変数を宣言することができます. struct curry { int karasa; int ryou; int omake; } otona; 普通の変数も宣言時に値を代入して初期化しておくことができましたが,構造体の 宣言時にも同様のことができます.次の例では,構造体変数の宣言と同時に初期化も 済ませています.初期化の指定で足りない分 (ここでは otona の omake メンバー) に は,0 が代入されます. struct curry otona = { 5, 300}; struct curry kodomo = { 1, 150, 1}; 12.4 基本的な戦闘プログラムのしあげ それではここまでのプログラムを作ってみましょう.まず最初はプレイヤーと敵 (ドラゴン) の 1 対 1 の対決です.それぞれのキャラクターの状態を status 構造体で 保持するようにします. 92 12 章 構造体 攻撃を加えた際のダメージ値は, 基本値 A = 自分の攻撃力 − 相手の防御力 ÷ 2 (12.1) で計算することにします.簡単に計算できますが,これをそのまま使うと攻撃のたび に毎回同じダメージ値になってしまい面白味がないので,乱数を加えます.ぶれ幅 B を次式で計算しておいて,0∼B までの範囲で乱数 C を作り,最終的に A ± C をダ メージ値とします. B = A ÷ 16 + 1 (12.2) C = random() % (B + 1) (12.3) ダメージ値 = A ± C (12.4) この ± というのが面倒なところですが,やり方は二つあります.一つは,C を作っ たら適当な乱数をもう一つ発生させて符号を決める方法です.たとえば,0∼10 の乱 数を発生させて,5 より小さければマイナス符号,それ以外はプラス符号にするよう にします.もう一つは,C 自体の作り方をちょっと工夫して, C = (random() % (2 ∗ B + 1)) − B (12.5) ダメージ値 = A + C (12.6) とする方法です.なぜこの方法で式 (12.3),(12.4) と同じことが実現できるのか,考 えてみてください. 「逃げる」コマンドは,とりあえず乱数で成否を決めるようにしましょう.0∼10 の乱数を発生させ,3 より小さければ逃亡成功,というようにしてください. 12.5 さらに拡張してみる 基本的な部分が完成したところで,より本格的な戦闘プログラムに拡張してみま しょう. 「所持金」や「経験値」を追加してみて,戦いに勝ったらその分が手に入るよう にしてみる. 「素早さ」を追加し,敵よりも素早ければ素早いほど逃亡成功の確率が高くなる ようにしてみる. 敵に攻撃パターンをもたせてみる.たとえば,“KKKMMMMMMM” という 10 文字を攻撃パターンとして構造体に保持しておき,この中からランダムに選んで ‘K’ であれば攻撃,‘M’ であれば魔法,というようにする.この場合は,10 回に 著 者 略 歴 伊藤 祥一(いとう・しょういち) 1997 年 2000 年 2003 年 2003 年 2007 年 2008 年 新潟大学理学部物理学科卒業 金沢大学大学院自然科学研究科博士前期課程数物科学専攻修了 金沢大学大学院自然科学研究科博士後期課程物質構造科学専攻修了, 博士(理学) 長野工業高等専門学校電子情報工学科助手 長野工業高等専門学校電子情報工学科助教 長野工業高等専門学校電子情報工学科准教授 現在に至る 編集担当 上村紗帆(森北出版) 編集責任 石田昇司(森北出版) 組 版 アベリー 印 刷 モリモト印刷 製 本 協栄製本 Springs of C ― 楽しく身につくプログラミング ― 2012 年 2 月 27 日 第 1 版第 1 刷発行 © 伊藤祥一 2012 【本書の無断転載を禁ず】 著 者 伊藤祥一 発 行 者 森北博巳 発 行 所 森北出版株式会社 東京都千代田区富士見 1-4-11(〒 102-0071) 電話 03-3265-8341 / FAX 03-3264-8709 http://www.morikita.co.jp/ 日本書籍出版協会・自然科学書協会・工学書協会 会員 <(社)出版者著作権管理機構 委託出版物> 落丁・乱丁本はお取替えいたします. Printed in Japan / ISBN978-4-627-84951-8
© Copyright 2024 Paperzz