昔、BASICマガジンからプログラムの勉強をはじめました。今はコンピュータ系やプログラミングの雑誌も減って寂しいものです。そんな中、今私が定期的に購入しているのは「日経Linux」です。Linuxの勉強のためですが、それほど中身を読む時間がないのが残念です。
今月の記事の紹介とその感想です。
特集1
Ubuntuデスクトップでスタート!
サーバー超マスター100ステップ
私はFedoraをデスクトップで使っていますが、困ったことにあまりサーバーとしては使っていないです。なかなかサーバーとして使う用途が思いつかないので、その当たりでいい説明があると良いのですが・・・
特集2
激安ボードで即楽しめる!
知識ゼロから始めるLinuxモノ作り
Raspberry Pi、BeagleBone Black, Arduino, mbed,を使った電子工作の記事です。初心者向けで、LEDと温度センサーを使ったものです。RPiでLEDはやってみたのですが、すぐに飽きました(笑)。やはり使い道が必要ですね。
特集3
要注目のモバイルLinux!
最新Chrome OSで 軽量マシン
実際にはChrome OSでなく、オープンソース版のChrominum OSをビルドからインストールまでを行う記事です。WindowsXPの期限が切れた後のノートPCに入れると良さそうな感じがします。
特別連載
Windows ユーザーのためのLinux超入門
第3回 主要アプリケーションとパッケージ管理
WindowsXPの期限切れが迫る中、Linuxは選択肢の1つですが、移行は難しいですね。Linuxを初めて使ったとき、Windowsよりも便利な点があって驚いたのですがまずはインストールが難関です。
まつもとゆきひろの言語の世界
第7回 JavaScript
Rubyを開発したまつもとゆきひろさんの記事で楽しみな記事の1つです。色々なプログラミング言語の思想などがまとめられています。
#!シス管系女子Season2
第2話 鍵認証で安全にログインしよう
みんとちゃんのボケがいつも楽しみです(笑)
マンガでこういう風に分かりやすく説明できるのは羨ましいです。
2013年11月24日日曜日
C/C++ 演算
コンピュータで一番得意なのは足し算や掛け算などの演算でしょう。
まずはデータとして値を代入する必要がありますが、それを示すのがイコール(=)です。C/C++では以下の点が注意です。
残りの文はアスタリスク(*)が掛け算、スラッシュ(/)が割り算を示す演算を表すということ意外は数学的な記述とほとんど同じで意味が解ると思います。演算には優先順があり括弧内から計算して行きます。また加減算より乗除算の方を先に計算します。最初の記述では、4という結果がvalueに代入されます。演算を表す文字を演算子と言い、通常の計算に使う演算子は算術演算子と言い、他にも余り(剰余)の計算でパーセント(%)があります。
C/C++として特徴的な演算子として 代入と計算を一緒に出来る演算子があることです。これは別の書き方ができますが、よく使うことからソースコードを簡単に書くことができるようにしたものです。
++value; と++value;の違いは分かりにくいですが、a = ++value; a = value++;と他の演算と記述すると違いが分かります。前者では、aにvalue+1が代入され、後者ではaにvalueが代入されます。
C/C++らしい演算子としては、他に2進数での計算を行うビット演算子があります。00110を1のシフトの場合に01100と左にシフトする左シフト(<<)、00011と右にシフトする右シフト(>>)があります。またビットごと論理和、論理積、排他的論理和、論理否定をおこなう演算があります。論理和など聞きなれない言葉かもしれませんが、意外とよく使うため、覚えておいた方が良いです。
また用途は計算と全く違うのですが、C/C++では演算子となっているもので、比較演算子があります。下記は「もしaが3と等しかったらaを1引く」と言うプログラムですが、このa == 3 のような条件を指定する部分も比較を行う演算子となっています。
比較演算子には小なり(<)、小なりイコール(<=)大なり(>)、大なりイコール(>=)、非等価(!=)、等価(==)があります。これらの演算の結果はC++の場合にはbool型と言う型となり、trueまたはfalseのどちらかの演算結果となります。C言語の場合には、int型で比較結果が真(正しい)のとき1を偽(間違っている)のとき0の演算結果となります。
代入(=)も演算子であり、以下の文も記述としては間違いではないのでコンパイル時にエラーとなりません。等価(==)と間違えないように注意が必要です。(gccの場合、コンパイル時に-Wallというオプションを付けることで条件のところで代入(=)が使用されていた場合にで警告を出すことは可能です)
また、このような真偽の専用の演算子として、論理演算子の論理否定(!)、論理和(||)、 論理積(&&)があります。ビット演算子と違いビットごとの演算はせず、値が0であれば0と、値が0以外であれば1として計算をします。
また論理和(||)、 論理積(&&)は唯一演算を端折る場合がある演算子なので注意が必要です。例えば以下の式の場合、a == 1の比較演算を行い、偽だった場合には、b == 2 と c = 2の処理は行いません。左から演算していくときにその後の演算がどうであっても、結果が偽となるためです。しかし、b == 2 については問題ないとしてもその後、c = 2 の代入が行われないことは、その後cの値を使用するときに前の比較演算の影響が出ることになります。
他にもprintf()という関数の呼出をこれまで使っていましたが、この記述も関数呼出の演算子とみなされます。そのため、 以下のような記述も可能です。
上述のようにC/C++の記述は演算子の固まりで出来ています。これらの演算子は全て繋げて書くことができますが、全ての演算子には足し算と掛け算では掛け算を先に行うように優先順位と言うものが存在します。例えば以下の場合にはaにどのような値が入るでしょうか?
答えは12になります。左シフト(<<)より加算(+)の方が優先順位が高いため 3 << (1 + 1)と同等になるためです。
さらに優先順位が同じ場合には、結合規則が存在し、左から右に計算していくものと、右から左に計算していくものがあります。例えば加算(+)は左から右で a + b + c + d の記述は ((a + b) + c) + d と同等です。代入(=)は右から左で a = b = c = d は a = (b = (c = d))と同等になります。
詳しくはWikipediaなど他の資料を参考してください。 他にも様々な演算子があったり、演算子は意外と奥が深いですが、C言語は演算子の記述の固まりであることと、優先順位に困った時には括弧を付けて優先順位をコントロールすることなど、注意点を覚えておけば、まずは良いかと思います。
value = 3 * 4 + (-4) / (1 + 1);
まずはデータとして値を代入する必要がありますが、それを示すのがイコール(=)です。C/C++では以下の点が注意です。
- 数学的なイコールと意味が違い、代入を意味する。
- イコールの左側を左辺値といい、変数の必要があり、変数の値を書き換える。
- イコール自身も式となっており、代入値を返す。
value2 = (value = 3 * 4 - (-4) / (1 + 1));
残りの文はアスタリスク(*)が掛け算、スラッシュ(/)が割り算を示す演算を表すということ意外は数学的な記述とほとんど同じで意味が解ると思います。演算には優先順があり括弧内から計算して行きます。また加減算より乗除算の方を先に計算します。最初の記述では、4という結果がvalueに代入されます。演算を表す文字を演算子と言い、通常の計算に使う演算子は算術演算子と言い、他にも余り(剰余)の計算でパーセント(%)があります。
C/C++として特徴的な演算子として 代入と計算を一緒に出来る演算子があることです。これは別の書き方ができますが、よく使うことからソースコードを簡単に書くことができるようにしたものです。
例 | 同等の式 | 備考 |
---|---|---|
value++; | value = value + 1; | 全体の評価が終わった後に+1する。 |
++value; | value = value + 1; | 全体の評価を行う前に+1する。 |
value--; | value = value - 1; | 全体の評価が終わった後に-1する。 |
--value; | value = value - 1; | 全体の評価を行う前に-1する。 |
value += 10; | value = value + 10; | +=の他にも-=,*=,/=,%=,&=,|=,^=が使用できる。 |
++value; と++value;の違いは分かりにくいですが、a = ++value; a = value++;と他の演算と記述すると違いが分かります。前者では、aにvalue+1が代入され、後者ではaにvalueが代入されます。
C/C++らしい演算子としては、他に2進数での計算を行うビット演算子があります。00110を1のシフトの場合に01100と左にシフトする左シフト(<<)、00011と右にシフトする右シフト(>>)があります。またビットごと論理和、論理積、排他的論理和、論理否定をおこなう演算があります。論理和など聞きなれない言葉かもしれませんが、意外とよく使うため、覚えておいた方が良いです。
a | b | a | b (論理和) | a & b (論理積) | a ^ b (排他的論理) | ~a (論理否定) |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 |
0 | 1 | 1 | 0 | 1 | 1 |
1 | 0 | 1 | 0 | 1 | 0 |
1 | 1 | 1 | 1 | 0 | 0 |
また用途は計算と全く違うのですが、C/C++では演算子となっているもので、比較演算子があります。下記は「もしaが3と等しかったらaを1引く」と言うプログラムですが、このa == 3 のような条件を指定する部分も比較を行う演算子となっています。
if (a == 3) a--;
比較演算子には小なり(<)、小なりイコール(<=)大なり(>)、大なりイコール(>=)、非等価(!=)、等価(==)があります。これらの演算の結果はC++の場合にはbool型と言う型となり、trueまたはfalseのどちらかの演算結果となります。C言語の場合には、int型で比較結果が真(正しい)のとき1を偽(間違っている)のとき0の演算結果となります。
代入(=)も演算子であり、以下の文も記述としては間違いではないのでコンパイル時にエラーとなりません。等価(==)と間違えないように注意が必要です。(gccの場合、コンパイル時に-Wallというオプションを付けることで条件のところで代入(=)が使用されていた場合にで警告を出すことは可能です)
if (a = 3) a--;
また、このような真偽の専用の演算子として、論理演算子の論理否定(!)、論理和(||)、 論理積(&&)があります。ビット演算子と違いビットごとの演算はせず、値が0であれば0と、値が0以外であれば1として計算をします。
また論理和(||)、 論理積(&&)は唯一演算を端折る場合がある演算子なので注意が必要です。例えば以下の式の場合、a == 1の比較演算を行い、偽だった場合には、b == 2 と c = 2の処理は行いません。左から演算していくときにその後の演算がどうであっても、結果が偽となるためです。しかし、b == 2 については問題ないとしてもその後、c = 2 の代入が行われないことは、その後cの値を使用するときに前の比較演算の影響が出ることになります。
(a == 1) && ((b == 2) && (c = 2))
他にもprintf()という関数の呼出をこれまで使っていましたが、この記述も関数呼出の演算子とみなされます。そのため、 以下のような記述も可能です。
a = a + printf("Hello");
上述のようにC/C++の記述は演算子の固まりで出来ています。これらの演算子は全て繋げて書くことができますが、全ての演算子には足し算と掛け算では掛け算を先に行うように優先順位と言うものが存在します。例えば以下の場合にはaにどのような値が入るでしょうか?
a = 3 << 1 + 1;
答えは12になります。左シフト(<<)より加算(+)の方が優先順位が高いため 3 << (1 + 1)と同等になるためです。
さらに優先順位が同じ場合には、結合規則が存在し、左から右に計算していくものと、右から左に計算していくものがあります。例えば加算(+)は左から右で a + b + c + d の記述は ((a + b) + c) + d と同等です。代入(=)は右から左で a = b = c = d は a = (b = (c = d))と同等になります。
詳しくはWikipediaなど他の資料を参考してください。 他にも様々な演算子があったり、演算子は意外と奥が深いですが、C言語は演算子の記述の固まりであることと、優先順位に困った時には括弧を付けて優先順位をコントロールすることなど、注意点を覚えておけば、まずは良いかと思います。
2013年11月17日日曜日
Fedora 20 Beta (GNOME 3.10)
Fedora 20 の Beta版が出ました。でもあまり変わっているところは無い気がします。カーネルのバージョンも3.11でした。12/10に正式版が出る予定です。
Fedora 20 になって一番変わる所と言えばGNOME3.10かもしれません。そこで、簡単にレビューです。GNOME 3.10の紹介の詳細についてはここです。
まず、UbuntuでiBus1.5について問題になっていたので日本語入力についてです。Fedoraでは比較的問題は無いのですがやはり分かりにくいです。日本語キーボードを使うには入力ソースに「日本語」というものと「日本語(かな漢字)」の2つを入れる必要があります。一旦「日本語」に変えると「日本語(かな漢字)」もキーボードのレイアウトが切り替わるという不思議な仕様となっています。これはFedora 19と変わらずです。
見た目が大きく変わったのは、メニューとヘッダーバーです。よりグラフィカルに、より表示エリアを大きくということでしょうか。もともとツールバー部分もタイトルバーと同様に掴んで移動などができました。これはタッチインターフェースでは掴みやすく便利と思っていたのですが、狭くなってしまいました。表示エリアが大きくなって良かったのだろうか・・・。
新しいソフトウェアとして、「ソフトウェア」や「地図」があります。「ソフトウェア」は今までのGUIのパッケージインストーラに比べると使いやすそうですが、やはりコマンドラインのyumの方が便利な気がします。「地図」はGoogle Mapに比べれば全然使えないのでしょうが、日本でも市の検索ぐらいは使えそうです。ちなみにこの「地図」はJavascriptで作られているようです。
それと細かい所ですが、Gnome3.10ではスクロールバーを2秒ほど掴んでいると、スクロールバーの線が細くなり、より細かくスクロールできるようになっています。しかし、GTK+3のライブラリを使っているところだけのようで、GTK+2を使っていると思われるFirefoxなどでは使えなさそうでした。
Fedora 20 になって一番変わる所と言えばGNOME3.10かもしれません。そこで、簡単にレビューです。GNOME 3.10の紹介の詳細についてはここです。
まず、UbuntuでiBus1.5について問題になっていたので日本語入力についてです。Fedoraでは比較的問題は無いのですがやはり分かりにくいです。日本語キーボードを使うには入力ソースに「日本語」というものと「日本語(かな漢字)」の2つを入れる必要があります。一旦「日本語」に変えると「日本語(かな漢字)」もキーボードのレイアウトが切り替わるという不思議な仕様となっています。これはFedora 19と変わらずです。
見た目が大きく変わったのは、メニューとヘッダーバーです。よりグラフィカルに、より表示エリアを大きくということでしょうか。もともとツールバー部分もタイトルバーと同様に掴んで移動などができました。これはタッチインターフェースでは掴みやすく便利と思っていたのですが、狭くなってしまいました。表示エリアが大きくなって良かったのだろうか・・・。
新しいソフトウェアとして、「ソフトウェア」や「地図」があります。「ソフトウェア」は今までのGUIのパッケージインストーラに比べると使いやすそうですが、やはりコマンドラインのyumの方が便利な気がします。「地図」はGoogle Mapに比べれば全然使えないのでしょうが、日本でも市の検索ぐらいは使えそうです。ちなみにこの「地図」はJavascriptで作られているようです。
それと細かい所ですが、Gnome3.10ではスクロールバーを2秒ほど掴んでいると、スクロールバーの線が細くなり、より細かくスクロールできるようになっています。しかし、GTK+3のライブラリを使っているところだけのようで、GTK+2を使っていると思われるFirefoxなどでは使えなさそうでした。
C/C++ 数値と名前と変数
動画や画像に音楽などコンピュータは色々なことができますが、
コンピュータの基本は電気が流れる流れないの0と1の世界です。
0と1だけで表した数値を2進数と言います。通常私達がつかっているのは10進数で、
の十個目で二桁になります。2進数の場合は
と2個目で二桁になります。これらを10進数に割り当てれば数値が扱えることになります。 括弧内は2進数を10進数に変換するための計算式です。2の4乗とは 2×2×2×2→8です。
2進数の場合は、桁数が多くなりすぎますが、 10進数と2進数では桁上げのタイミングが違いすぎるので、 8進数や16進数が使われます。特に16進数がよく使われます。
そして2進数の1つの1と0のデータの単位をビット(bit)、16進数で2桁(2進数で8桁)の0〜FF(0〜255)の単位をバイト(byte)と呼びます。
プログラムで計算などを行うには、メモリを使いますが、コンピュータとしてはメモリも100番地と150番地のデータのように数値で区別して扱います。通常の場合は1つの番地に1バイトのデータを記憶できます。
しかし、こんな数値ばかりでメモリを管理していたら大変です。ソースコードでは大抵、メモリを管理するためには自分で名前を付けた「変数」を使用します。
階乗の計算のソースコードでは、value, fact, iが変数です。自由に名前を付けれますが、英字の大文字小文字、数字、アンダーバー(_)が使用できます。先頭に数字は使用できません。せっかく自由なので分かりやすい名前にしましょう。iは分かりにくく感じると思いますが、繰り返し処理に使用される因子(iterator)の意味から来ているもので、よく使われる定石みたいな物となっています。
この変数の前についているintは「型」といって、データの意味を表すものとなっています。main()内の先頭行ではvalue, fact, iがint型であることを宣言しています。
最近のPCでは、intは32bit(4バイト)で10進数だと−2,147,483,648 から 2,147,483,647の値のデータと言う意味です。計算結果がこの範囲を越えることをオーバーフローといい、計算結果が意図通りにならない原因の1つです。
小数点を扱いや、マイナスの扱いも2進数で扱います。どのように扱うかは、IEEE754や、2の補数を調べてみてください。
コンピュータの基本は電気が流れる流れないの0と1の世界です。
0と1だけで表した数値を2進数と言います。通常私達がつかっているのは10進数で、
1 2 3 4 5 6 7 8 9 10
の十個目で二桁になります。2進数の場合は
1 10
と2個目で二桁になります。これらを10進数に割り当てれば数値が扱えることになります。 括弧内は2進数を10進数に変換するための計算式です。2の4乗とは 2×2×2×2→8です。
1 → 1 ( 2の0乗 × 1) 10 → 2 ( 2の1乗 × 1 + 2の0乗 × 0) 11 → 3 ( 2の1乗 × 1 + 2の0乗 × 1) 100 → 4 (2の2乗 × 1 + 2の1乗 × 0 + 2の0乗 × 0) : : 10100 → 20 (2の4乗 × 1 + 2の3乗 × 0 + 2の2乗 × 1 + 2の1乗 × 0 + 2の0乗 × 0)
2進数の場合は、桁数が多くなりすぎますが、 10進数と2進数では桁上げのタイミングが違いすぎるので、 8進数や16進数が使われます。特に16進数がよく使われます。
10進数 | 2進数 | 8進数 | 16進数 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
2 | 10 | 2 | 2 |
3 | 11 | 3 | 3 |
4 | 100 | 4 | 4 |
5 | 101 | 5 | 5 |
6 | 110 | 6 | 6 |
7 | 111 | 7 | 7 |
8 | 1000 | 10 | 8 |
9 | 1001 | 11 | 9 |
10 | 1010 | 12 | A |
11 | 1011 | 13 | B |
12 | 1100 | 14 | C |
13 | 1101 | 15 | D |
14 | 1110 | 16 | E |
15 | 1111 | 17 | F |
16 | 10000 | 20 | 10 |
17 | 10001 | 21 | 11 |
: | : | : | : |
そして2進数の1つの1と0のデータの単位をビット(bit)、16進数で2桁(2進数で8桁)の0〜FF(0〜255)の単位をバイト(byte)と呼びます。
プログラムで計算などを行うには、メモリを使いますが、コンピュータとしてはメモリも100番地と150番地のデータのように数値で区別して扱います。通常の場合は1つの番地に1バイトのデータを記憶できます。
しかし、こんな数値ばかりでメモリを管理していたら大変です。ソースコードでは大抵、メモリを管理するためには自分で名前を付けた「変数」を使用します。
階乗の計算のソースコードでは、value, fact, iが変数です。自由に名前を付けれますが、英字の大文字小文字、数字、アンダーバー(_)が使用できます。先頭に数字は使用できません。せっかく自由なので分かりやすい名前にしましょう。iは分かりにくく感じると思いますが、繰り返し処理に使用される因子(iterator)の意味から来ているもので、よく使われる定石みたいな物となっています。
この変数の前についているintは「型」といって、データの意味を表すものとなっています。main()内の先頭行ではvalue, fact, iがint型であることを宣言しています。
最近のPCでは、intは32bit(4バイト)で10進数だと−2,147,483,648 から 2,147,483,647の値のデータと言う意味です。計算結果がこの範囲を越えることをオーバーフローといい、計算結果が意図通りにならない原因の1つです。
小数点を扱いや、マイナスの扱いも2進数で扱います。どのように扱うかは、IEEE754や、2の補数を調べてみてください。
1 | 準備(開発環境のインストール) |
2 | 準備(コマンドライン) |
3 | 準備(テキストエディタ) |
4 | Hello Worldと文法 |
5 | Hello World が動く仕組み |
6 | Hello World ウィンドウ表示(Windows) |
7 | Hello World ウィンドウ表示(GTK+) |
8 | 階乗の計算 |
9 | 数値と名前と変数 |
10 | 演算 |
2013年11月10日日曜日
C/C++ 階乗の計算
コンピュータと言えば計算が得意です。ですから計算のプログラムを作ってみます。計算は階乗(factorial)の計算で、nという数字を与えた時に、1×2×3×4×・・・・×(n-1)×nという1からnまでを掛けた値になる計算です。なお0の階乗は1となります。
まずはどのように動くかという仕様を決めます。これだけのプログラムでも色々と考える必要があります。
コンパイルし、実行を行うと以下のようになります。
ソースコードの解説を簡単にします。
複雑になりましたが、それは今回のプログラムがユーザーからのコマンドラインからの入力が増えたことが原因です。これでもオーバーフローを考慮しないなど、処理が少なくなるようにサボっています(17の階乗でなるはずの無いマイナス値となっているのはオーバーフローのためです)。
通常の場合、プログラムはユーザーの入力に対し、何かの処理を行います。ユーザーの入力は何があるか分からないので、入力を制限する処理を入れたり、どのような入力でもチェックによりエラーを返すなどの処理を入れなくてはいけません。そうしないと 想定外の問題が起こることになります。
この想定外の問題をいかにして少なくすることができるかが、プログラマの腕の見せ所の1つです。
それと、今回はCPUの処理(演算と制御)の部分とメモリ(データ領域の作成)の部分がHello Worldに比べて多く増えています。それらについてはまた次回。
まずはどのように動くかという仕様を決めます。これだけのプログラムでも色々と考える必要があります。
- コマンドライン引数で階乗の数値を入力する。
- コマンドライン引数で入力した数値から階乗を計算する。
- 計算した結果をコンソールに表示する。
- 引数では数値を入力する。数値以外は0と見なす。
- 数値がマイナスの場合はエラーを表示し、2を返す。
- 小数点がついていた場合は、小数点以下を無視する。
- 引数の数が1つでない場合はエラーを表示し、1を返す。
- 値のオーバーフローは考慮しない。
コンパイルし、実行を行うと以下のようになります。
ソースコードの解説を簡単にします。
ソース | 説明 |
---|---|
main(int argc, char *argv[]) | 今回はmain()関数にint argc, char *argv[]という2つの引数がついています。argcは整数(int)で、引数の数を表します。argvは引数の文字列の配列を意味します。*や[]の意味はまた機会があれば説明します。とりあえずargv[1]などが一つ目の引数の文字列と解釈してください。 |
int value, fact, i; | valueと、factと、iという名前の整数のデータ領域を作成しています。 |
if (argc != 2) {...} | 引数の数(argc)のチェックを行っています。引数の数にはコマンド自身の数も含まれるので引数が1つのときには2となります。!=2はargcが2で無いかどうかという計算で、その結果により、{...}内の処理を行います。 |
value = atoi(argv[1]); | 引数の文字列argv[1]を整数値に変換して、valueに代入しています。文字列のままでは計算ができないためです。atoi()というのは文字列を整数値に変換するライブラリ関数です。stdlib.hに入っているのでstdlib.hを#includeしています。 |
if (value < 0) {...} | 引数を整数値に変換したvalueが0より小さい場合に{...}の処理をします。 |
fact = 1; | factというデータ領域に1を代入します。 |
for (i = 1; i <= value; i++) {...} | 繰り返し処理を行い、階乗を求めています。(1)iのデータ領域に1を代入します。(2)iがvalue以下の値か調べ、iがvalue以下の値であれば、{...}の処理を実行し、そうでなければ{...}の次の行の処理に移ります。(3){...}内の処理が終わったらi++を実行します。i++はiの値をi = i+1と同じ意味でiの値に1つ値を足します。 |
fact = fact * i; | アスタリスク(*)は掛け算の意味です。iが1つずつ加算するのを繰り返すことで、階乗を計算します。 |
printf("factorial = %d¥n", fact); | 階乗の値を出力します。printf()で表示する文字列の指定内のパーセント(%)には意味があり、%dは整数(int)の表示を行うことを意味します。文字列の後ろの引数に%dに表示するデータを列挙します。 |
複雑になりましたが、それは今回のプログラムがユーザーからのコマンドラインからの入力が増えたことが原因です。これでもオーバーフローを考慮しないなど、処理が少なくなるようにサボっています(17の階乗でなるはずの無いマイナス値となっているのはオーバーフローのためです)。
通常の場合、プログラムはユーザーの入力に対し、何かの処理を行います。ユーザーの入力は何があるか分からないので、入力を制限する処理を入れたり、どのような入力でもチェックによりエラーを返すなどの処理を入れなくてはいけません。そうしないと 想定外の問題が起こることになります。
この想定外の問題をいかにして少なくすることができるかが、プログラマの腕の見せ所の1つです。
それと、今回はCPUの処理(演算と制御)の部分とメモリ(データ領域の作成)の部分がHello Worldに比べて多く増えています。それらについてはまた次回。
2013年11月4日月曜日
MinGW + MSYS で日本語
C/C++ Hello Worldでは、わざと日本語の使用を避けていました。日本語を扱うには色々と問題があり、設定などが難しかったためです。
gccのコンパイルで警告になることがある
printf("Hello World¥n");でなく、 printf("こんにちは¥n”);とした場合は問題になりませんが、printf("ソフトウェア¥n”);とした場合にはコンパイル時に警告が表示されます。
なぜかというと円マーク(¥)が原因です。円マークは文字コードが0x5Cなのですが(Linuxでは0x5Cの表示がバックスラッシュ(\)の場合もあります)、Windowsが標準で使用している文字コードのCP932(ShiftJISの一種)では、「ソ」は0x83,0x5Cの2バイトとなっていて、コンパイラが2つ目の0x5Cを円マーク(¥)と間違うためです。他にも「表」などが同様に問題があります。
その対策では、--input-chaset=CP932というオプションを付けます。するとgccは文字コードをUTF-8 に変換してからコンパイルします。しかし、これだけではprintf()で文字化けします。--exec-charset=CP932とすることで、実行ファイル内部の文字列をCP932に変換します。
そもそもvimで日本語入力ができない
まずはvimで日本語が化けます。この対策で、.vimrcの最後の行に
と入れると良いようです。ここを参照させていただきました。
しかし、日本語入力はうまく行きません。Alt+半角/全角で日本語入力への切換ができるのですが、vimの画面では変な風にスクロールしていってしまいます。
この対策にはminttyを使うと良いようです。以下でセットアップできます。
"mintty -"(ハイフンが必要)を実行するか、msys.batを起動するときに -minttyオプションを付けるとminttyがが起動します。mintty上で起動したvimであれば日本語入力が出来ます。ここを参照させていただきました。以下はminttyの画面です。
さらに日本語ファイル名を使う
上記ではlsで日本語のファイル名がまた文字化けしますし、入力もできません。以下を行うと日本語ファイル名が使えるようになります。
/etc/profileのファイルを編集して最後に以下を追加します。
また、~/.inputrcを編集して16行目以降を以下のように変更します。
同じくここを参照させていただきました。
gccのコンパイルで警告になることがある
printf("Hello World¥n");でなく、 printf("こんにちは¥n”);とした場合は問題になりませんが、printf("ソフトウェア¥n”);とした場合にはコンパイル時に警告が表示されます。
なぜかというと円マーク(¥)が原因です。円マークは文字コードが0x5Cなのですが(Linuxでは0x5Cの表示がバックスラッシュ(\)の場合もあります)、Windowsが標準で使用している文字コードのCP932(ShiftJISの一種)では、「ソ」は0x83,0x5Cの2バイトとなっていて、コンパイラが2つ目の0x5Cを円マーク(¥)と間違うためです。他にも「表」などが同様に問題があります。
その対策では、--input-chaset=CP932というオプションを付けます。するとgccは文字コードをUTF-8 に変換してからコンパイルします。しかし、これだけではprintf()で文字化けします。--exec-charset=CP932とすることで、実行ファイル内部の文字列をCP932に変換します。
そもそもvimで日本語入力ができない
まずはvimで日本語が化けます。この対策で、.vimrcの最後の行に
set enc=japan
しかし、日本語入力はうまく行きません。Alt+半角/全角で日本語入力への切換ができるのですが、vimの画面では変な風にスクロールしていってしまいます。
この対策にはminttyを使うと良いようです。以下でセットアップできます。
$ mingw-get install msys-mintty
"mintty -"(ハイフンが必要)を実行するか、msys.batを起動するときに -minttyオプションを付けるとminttyがが起動します。mintty上で起動したvimであれば日本語入力が出来ます。ここを参照させていただきました。以下はminttyの画面です。
さらに日本語ファイル名を使う
上記ではlsで日本語のファイル名がまた文字化けしますし、入力もできません。以下を行うと日本語ファイル名が使えるようになります。
/etc/profileのファイルを編集して最後に以下を追加します。
alias ls='ls --show-control-char'
また、~/.inputrcを編集して16行目以降を以下のように変更します。
# disable/enable 8bit input set meta-flag on set input-meta on set output-meta on set convert-meta off
同じくここを参照させていただきました。
MinGW + MSYS で GTK+3
GTK+のバージョン3はなかなかWindows版が出なかったのがやっと出たようです。インストール方法も載っていますが、MSYSで使うのであれば、より簡単かもしれません。
all-in-on bundleをダウンロードして、C:¥MinGW¥msys¥1.0に解凍して上書きします。
その後、MSYS上で以下のコマンドを実行します。
たぶん、これでいいはず・・・
コンパイル方法も全く一緒で、これと同じプログラムが動きます(-mwindowsオプションは付けたほうが良いかもしれませんが)。
all-in-on bundleをダウンロードして、C:¥MinGW¥msys¥1.0に解凍して上書きします。
その後、MSYS上で以下のコマンドを実行します。
$ pango-querymodules > \etc\pango\pango.modules $ gdk-pixbuf-query-loaders > \lib\gdk-pixbuf-2.0\2.10.0\loaders.cache $ gtk-query-immodules-3.0 > \lib\gtk-3.0\3.0.0\immodules.cache
たぶん、これでいいはず・・・
コンパイル方法も全く一緒で、これと同じプログラムが動きます(-mwindowsオプションは付けたほうが良いかもしれませんが)。
C/C++ Hello World ウィンドウ表示(GTK+)
Linuxにおいてもウィンドウ表示のHello Worldを作成してみます。
Linuxでウィンドウアプリケーションを作るにはツールキットと言われるソフトを使用します。GTK+や、Qt(キュート)が有名です。GTK+は少々使いにくいかもしれませんが、C++でなくC言語から使えるのが特徴です。
まずは、GTK+の開発環境をインストールします。
Fedoraの場合
Ubuntuの場合
hello2.cは以下のように作成します。
コンパイルを以下のように行い、実行します。
すると以下のようになウィンドウが表示されます。
少し難しいですが、簡単に各項目の説明をします。
コンパイル時には`pkg-config --cflags --libs gtk+-3.0`を指定します。「`」は日本語キーボードでは「@」のキーをShiftキーを押しながら入力すると表示される文字です。これを指定するとpkg-configのコマンドの表示結果をそのままコマンドに渡すことができます。pkg-configは、GTK+で必要なヘッダーファイルの場所と、ライブラリを指定する引数を表示するコマンドです。
GTK+の詳細についてはここにリファレンスマニュアルがあります。ただし全部英語です。
Linuxでウィンドウアプリケーションを作るにはツールキットと言われるソフトを使用します。GTK+や、Qt(キュート)が有名です。GTK+は少々使いにくいかもしれませんが、C++でなくC言語から使えるのが特徴です。
まずは、GTK+の開発環境をインストールします。
Fedoraの場合
$ sudo yum install gtk3-devel
Ubuntuの場合
$ sudo apt-get install libgtk-3-dev
hello2.cは以下のように作成します。
#include <gtk/gtk.h> int main(int argc, char *argv[]) { GtkWidget *dialog; gtk_init(&argc, &argv); dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_OTHER, GTK_BUTTONS_OK, "Hello!" ); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return 0; }
コンパイルを以下のように行い、実行します。
$ gcc hello2.c `pkg-config --cflags --libs gtk+-3.0` $ ./a.out
すると以下のようになウィンドウが表示されます。
少し難しいですが、簡単に各項目の説明をします。
項目 | 説明 |
---|---|
main | main()関数はargc, argv, envの3つの引数を持つことができます。今までは全て省略していました。envのみ省略もできます。 |
int argc | コマンドライン引数の数が渡されます。 |
char *argv[]; | コマンドライン引数の文字列の配列が渡されます。 |
GtkWidget *dialog; | dialogという名前のウィンドウをこれから作ることを宣言します。 |
gtk_init() | GTK+を使用する前に必須の初期化を行います。 |
gtk_message_dialog_new() | メッセージを表示するだけの簡単なウィンドウdialogを作成します。一つ目の引数は、親ウィンドウを指定します。二つ目の引数は動作を指定します。三つ目の引数はどのようなアイコンを出すかを指定します。4つ目の引数はOKボタンを出す事をしていします。5つ目の引数は、表示する文字列です。 |
gtk_dialog_run() | dialogを表示し、OKが押されるまで待ちます。 |
gtk_widget_destroy() | 作成したdialogを削除します。 |
コンパイル時には`pkg-config --cflags --libs gtk+-3.0`を指定します。「`」は日本語キーボードでは「@」のキーをShiftキーを押しながら入力すると表示される文字です。これを指定するとpkg-configのコマンドの表示結果をそのままコマンドに渡すことができます。pkg-configは、GTK+で必要なヘッダーファイルの場所と、ライブラリを指定する引数を表示するコマンドです。
GTK+の詳細についてはここにリファレンスマニュアルがあります。ただし全部英語です。
1 | 準備(開発環境のインストール) |
2 | 準備(コマンドライン) |
3 | 準備(テキストエディタ) |
4 | Hello Worldと文法 |
5 | Hello World が動く仕組み |
6 | Hello World ウィンドウ表示(Windows) |
7 | Hello World ウィンドウ表示(GTK+) |
8 | 階乗の計算 |
9 | 数値と名前と変数 |
10 | 演算 |
C/C++ Hello World ウィンドウ表示(Windows)
コンソールにHello Worldを表示するプログラムを作りましたが、ウィンドウに表示をした方がプログラムを作った気になるかもしれません。そこでウィンドウにHello Worldを表示するプログラムの作成です。
Windowsの場合、多くのライブラリがMicrosoftから提供されており、Win32 API、.NET Framework API、WinRT API、MFC、ATLなど色々とあります。この中で最も原始的なWin32 APIがC言語と相性がいいので、それを使用します。
プログラムは以下です。
以下のようにコンパイルし、実行します。
すると以下のようなウィンドウが表示されます。
ソースコードはコンソール版とかなり違います。違いを簡単にまとめます。
Win32 APIには、ANSI版とUNICODE版の二つの関数があります。#include<tchar.h>が、その二つの関数の切り替えの仕組みの一つだと考えてください。_T()によって文字列をANSIとUNICODEで切り替えます。_tWinMain()もANSI版のWinMain()とwWinMain()に切り換えられ、MessageBox()もANSI版のMessageBoxA()とMessageBoxW()に切り換えられます。面倒ですが切り替えが出来るように作成した方が将来的に良いかと思われます。
コンパイル時には、-mwindowsというオプションを付けています。これはWindowsの実行形式にはコンソール版とウィンドウ版の二通りがあり、このオプションを付けないとコンソール版となってしまうからです。コンソール版では実行時に必ずコンソールを作成します。エクスプローラからa.exeを実行すると違いがよくわかると思います。
Win32 APIはウィンドウ関係だけでなく、Windowsのほとんどの機能が提供されています。MSDN LibraryのサイトにおいてWindows開発→Windows 2000と辿った所にドキュメントがあるため、Win32 APIの詳細はそれを参照してください。
Windowsの場合、多くのライブラリがMicrosoftから提供されており、Win32 API、.NET Framework API、WinRT API、MFC、ATLなど色々とあります。この中で最も原始的なWin32 APIがC言語と相性がいいので、それを使用します。
プログラムは以下です。
以下のようにコンパイルし、実行します。
$ gcc hello2.c -mwindows $ ./a.exe
ソースコードはコンソール版とかなり違います。違いを簡単にまとめます。
コンソール版 | ウィンドウ版 | 説明 |
---|---|---|
#include<stdio.h> | #include<windows.h> | Win32 APIの使用のために、windows.hを組み込みます。 |
int main() | int _tWinMain() | ウィンドウでは、最初に実行される関数がmain()では無いです。WinMain()またはwWinMain()となります。一つ目と二つ目の引数は過去に使用していましたが、今は使用していません。 三つ目の引数はコマンドライン引数です。四つ目はウィンドウの表示状態です。 |
printf() | MessageBox() | MessageBoxは単純なウィンドウを表示するWin32 APIの関数です。一つ目の引数は親ウィンドウで今回は無いのでNULLです。二つ目の引数はウィンドウ内に表示する文字列、三つめの引数はタイトルの文字列、四つ目の引数はOKボタンを出す事を指定しています。 |
Win32 APIには、ANSI版とUNICODE版の二つの関数があります。#include<tchar.h>が、その二つの関数の切り替えの仕組みの一つだと考えてください。_T()によって文字列をANSIとUNICODEで切り替えます。_tWinMain()もANSI版のWinMain()とwWinMain()に切り換えられ、MessageBox()もANSI版のMessageBoxA()とMessageBoxW()に切り換えられます。面倒ですが切り替えが出来るように作成した方が将来的に良いかと思われます。
コンパイル時には、-mwindowsというオプションを付けています。これはWindowsの実行形式にはコンソール版とウィンドウ版の二通りがあり、このオプションを付けないとコンソール版となってしまうからです。コンソール版では実行時に必ずコンソールを作成します。エクスプローラからa.exeを実行すると違いがよくわかると思います。
Win32 APIはウィンドウ関係だけでなく、Windowsのほとんどの機能が提供されています。MSDN LibraryのサイトにおいてWindows開発→Windows 2000と辿った所にドキュメントがあるため、Win32 APIの詳細はそれを参照してください。
1 | 準備(開発環境のインストール) |
2 | 準備(コマンドライン) |
3 | 準備(テキストエディタ) |
4 | Hello Worldと文法 |
5 | Hello World が動く仕組み |
6 | Hello World ウィンドウ表示(Windows) |
7 | Hello World ウィンドウ表示(GTK+) |
8 | 階乗の計算 |
9 | 数値と名前と変数 |
10 | 演算 |
2013年11月3日日曜日
C/C++ Hello World が動く仕組み
Hello Worldのプログラムを作って動かしたのですが、これがどうやって動いているかを説明します。
まずは、コンピュータの仕組みから説明します。コンピュータは基本的に次の3つから出来ています。
キーボードやマウス、ディスプレイにHDDはどこか?というとI/Oにつながっています。
そして、ご存知のようにコンピュータは電気で動いています。電気が流れる・流れないの2つの状態を大量にメモリに持っています。CPUでその電気の流れる・流れないの状態を取得したり、変更したりします。またCPUはキーボード等からI/Oを通じて電気の流れる・流れないを取得したり、ディスプレイに電気の流れる、流れないを渡して表示を行ったりすることができます。
単純に流れる・流れないは 2値(ディジタル)ですが、2値をたくさん組み合わたデータを操作することで色々なことができるのがコンピュータです。
さて、Hello Worldの仕組みに戻ります。a.exe(a.out)というファイルが作られたのですが、このファイルは実行形式のデータとなっています。画像は単に画像形式に、文書は文書形式になっているだけで、コンピュータは2値を組み合わせたデータを扱うと言う点に違いはないです。
実行形式のデータはOSによって中身が違っており、WindowsではPE(Portable Executable)という形式になっており、LinuxではELF(Excutable and Linkable Format)という形式になっています。gccなどのコンパイラはコンパイルすることでこれらの形式のファイルを作ります。
実行形式のファイルを実行する操作を行うと、OSが実行形式をI/Oを通じてHDDからファイルをメモリ上に展開します。 そして、main()という名前の関数から実行を行うようになっています。
CPUの部分では、以下の手順で実行を行っています。
printf();の実行
↓
return 0; の実行
メモリの部分では、プログラム自身と"Hello World\nGood bye\n"などの文字列を記憶しています。
I/Oの部分はプログラムを書くのが難しい部分です。そこで他の人(OS提供元など)が作ったプログラムを組み込み、それを使います。その組み込むプログラムのことをライブラリと言い、ヘッダーファイル(○○.h)と一緒に提供されます。
#include<stdio.h> では、標準I/O(Standard I/O)と言うものを組み込んであり、コンソールに表示を行う機能など、WindowsでもLinuxでも使えるような機能が提供されています。
ライブラリの組み込みは通常は、コンパイル時のオプションで指定する必要があります。しかしC言語の標準的な機能は、何も指定しないでもデフォルトで組み込まれるようになっています。
以下のように"-v"オプションを付けてコンパイルを行うとコンパイルで何を行っているかの詳細が表示されます。
最後の辺りで -lmsvcrt などの文字が出てくると思います。これらがデフォルトで組み込まれたライブラリです(-lがライブラリを指定するオプションでmsvcrtというライブラリを組み込んだと言うことになります)。
Hello Worldの仕組みについては以上です。Hello Worldでも、大きいプログラムでも、コンピュータの基本が「CPU」、「メモリ」、「I/O」であることから、これらを操作する処理を書くことがプログラムを書くこと、になります。
まずは、コンピュータの仕組みから説明します。コンピュータは基本的に次の3つから出来ています。
メモリ ↔ CPU ↔ I/O(Input/Output)
キーボードやマウス、ディスプレイにHDDはどこか?というとI/Oにつながっています。
そして、ご存知のようにコンピュータは電気で動いています。電気が流れる・流れないの2つの状態を大量にメモリに持っています。CPUでその電気の流れる・流れないの状態を取得したり、変更したりします。またCPUはキーボード等からI/Oを通じて電気の流れる・流れないを取得したり、ディスプレイに電気の流れる、流れないを渡して表示を行ったりすることができます。
単純に流れる・流れないは 2値(ディジタル)ですが、2値をたくさん組み合わたデータを操作することで色々なことができるのがコンピュータです。
さて、Hello Worldの仕組みに戻ります。a.exe(a.out)というファイルが作られたのですが、このファイルは実行形式のデータとなっています。画像は単に画像形式に、文書は文書形式になっているだけで、コンピュータは2値を組み合わせたデータを扱うと言う点に違いはないです。
実行形式のデータはOSによって中身が違っており、WindowsではPE(Portable Executable)という形式になっており、LinuxではELF(Excutable and Linkable Format)という形式になっています。gccなどのコンパイラはコンパイルすることでこれらの形式のファイルを作ります。
実行形式のファイルを実行する操作を行うと、OSが実行形式をI/Oを通じてHDDからファイルをメモリ上に展開します。 そして、main()という名前の関数から実行を行うようになっています。
CPUの部分では、以下の手順で実行を行っています。
printf();の実行
↓
return 0; の実行
メモリの部分では、プログラム自身と"Hello World\nGood bye\n"などの文字列を記憶しています。
I/Oの部分はプログラムを書くのが難しい部分です。そこで他の人(OS提供元など)が作ったプログラムを組み込み、それを使います。その組み込むプログラムのことをライブラリと言い、ヘッダーファイル(○○.h)と一緒に提供されます。
#include<stdio.h> では、標準I/O(Standard I/O)と言うものを組み込んであり、コンソールに表示を行う機能など、WindowsでもLinuxでも使えるような機能が提供されています。
ライブラリの組み込みは通常は、コンパイル時のオプションで指定する必要があります。しかしC言語の標準的な機能は、何も指定しないでもデフォルトで組み込まれるようになっています。
以下のように"-v"オプションを付けてコンパイルを行うとコンパイルで何を行っているかの詳細が表示されます。
$ gcc -v hello.c
最後の辺りで -lmsvcrt などの文字が出てくると思います。これらがデフォルトで組み込まれたライブラリです(-lがライブラリを指定するオプションでmsvcrtというライブラリを組み込んだと言うことになります)。
Hello Worldの仕組みについては以上です。Hello Worldでも、大きいプログラムでも、コンピュータの基本が「CPU」、「メモリ」、「I/O」であることから、これらを操作する処理を書くことがプログラムを書くこと、になります。
1 | 準備(開発環境のインストール) |
2 | 準備(コマンドライン) |
3 | 準備(テキストエディタ) |
4 | Hello Worldと文法 |
5 | Hello World が動く仕組み |
6 | Hello World ウィンドウ表示(Windows) |
7 | Hello World ウィンドウ表示(GTK+) |
8 | 階乗の計算 |
9 | 数値と名前と変数 |
10 | 演算 |
登録:
投稿 (Atom)