はじめてのC++ ~代入と計算編~
今回はC++で代入の仕方と計算方法を学んでいきます。
基礎中の基礎なのでしっかり抑えましょう。
基本
#include <iostream> int main() { // 宣言 int x; int y; int z; // 代入 x = 5; y = 3; // 計算 z = x + y; std::cout << z << "\n"; return 0; }
8
初めに
int x; int y; int z;
と書きました。
これは宣言と呼ばれ、「このような変数を使うよ」ってコンパイラに教えてあげます。
これがないとエラーが出ます。
まとめて以下のように書くこともできます。
int x, y, z;
intというのは32ビット符号付整数型のことです。
他にも様々な「型」があります。
よく使うものとしては、double型(浮動小数点型)、char型(文字型)、std::string型(文字列型)、bool型(ブーリアン型:trueかfalseの2値)などがあります。
x = 5; y = 3;
は代入を表しています。
宣言と同時に、以下のように代入することもできます。
int x = 5;
また、他の変数を介して、以下のように代入することもできます。
int a = 5; int x = a; // x = 5
次に、
z = x + y;
は見ての通り足し算です。
以下のように、変数と数値を計算することもできます。
z = x + 3;
引き算は「-」、掛け算は「*」、割り算は「/」でできます。
また、割り算の剰余は「%」で求められます。
// 引き算 z = 5 - 3; // z = 2 // 掛け算 z = 5 * 3; // z = 15 // 割り算 z = 5 / 3; // z = 1 (int型同士の割り算では小数点以下切り捨て) // 剰余 z = 5 % 3; // z = 2
ちなみに「//」はコメントアウトと呼ばれ、「//」以降に書かれた文字は読み飛ばされます。
「//」は1行のコメントを書くことができ、「/* */」の間には複数行のコメントを書けます。
特殊な代入と計算
#include <iostream> int main() { int a, b, c, d, e; a = b = c = d = e = 4; a = a + 2; std::cout << "a=" << a << "\n"; b += 2; std::cout << "b=" << b << "\n"; c -= 2; std::cout << "c=" << c << "\n"; d *= 2; std::cout << "d=" << d << "\n"; e /= 2; std::cout << "e=" << e << "\n"; return 0; }
a=6 b=6 c=2 d=8 e=2
まず、
a = b = c = d = e = 4;
に注目してください。
これは
e = 4
d = e;
c = d;
b = c;
a = b;
と書くのと同じです。
つまり、すべての変数に4が入っています。
次に、
a = a + 2;
を見てみましょう。
これは元々変数aに入っている数値に2を足して、その結果を変数aに格納することを表しています。
したがって、最終的には変数aに6が入ります。
b += 2;
は
b = b + 2;
と同じです。
同様に、
c -= 2;
は
c = c - 2;
と、
d *= 2;
は
d = d * 2;
と、
e /= 2;
は
e = e / 2;
と同じです。
インクリメントとデクリメント
インクリメントは変数の値を1増加させ、デクリメントは変数の値を1減少させることを言います。
C++ではこれらを短く書くことができます。
#include <iostream> int main() { int a, b, c, d; a = b = c = d = 1; // インクリメント a++; ++b; std::cout << "a=" << a << " b=" << b << "\n"; // デクリメント c--; --d; std::cout << "c=" << c << " d=" << d << "\n"; return 0; }
a=2 b=2 c=0 d=0
実はインクリメント演算子(++)やデクリメント演算子(--)が変数の前に来るのと後に来るのでは、微妙に処理が異なります。
#include <iostream> int main() { int a, b; a = b = 1; std::cout << a++ << "\n" << ++b << "\n"; return 0; }
1 2
実行結果を見てわかる通り、演算子が前に来る場合は+1された結果が表示されていますが、後に来る場合は+1されずに表示されています。
これは、演算子が前に来ると文の実行前に+1され、後に来る場合は文が実行された後に+1されるからです。
最後にもう一度aを出力してみると、+1して表示されます。
はじめてのC++ ~Hello World編~
今回はC++でHello Worldをやっていきます。
Hello Worldとはプログラムで「Hello World」という文字を出力することをいいます。
プログラミング初心者の初めの一歩としてよく扱われます。
早速やっていきましょう。
プログラムを書く
適当なフォルダに「helloWorld.cpp」というファイルを作り、その中に下記のコードを書きます。
「cpp」とはC++プログラムの拡張子です。
#include <iostream> int main() { std::cout << "Hello World\n"; return 0; }
解説の前に実行してみましょう。
まずコンパイルをします。
コマンドプロンプトなどでhelloWorld.cppを作ったフォルダに移動し、下記のコマンドを実行してください。
g++ helloWorld.cpp -o helloWorld
その後にコンパイルを行うファイル名を書きます。
「-o」オプションは出力ファイル名を指定するためのオプションです。
ここでは実行可能ファイルに「helloWorld」という名前をつけました。
次に下記のコマンドを実行します。
./helloWorld
「Hello World」と出力されたら成功です。
解説
1行目の
#include <iostream>
は入出力のためのiostreamライブラリを読み込んでいます。
3行目の
int main() {
は現時点では決まり事と思ってもらえれば大丈夫です。
そして3行目の「{」は5行目の「}」に対応しています。
4行目の
std::cout << "Hello World\n";
にはたくさんの要素が入っています。
先頭から順番に見ていきましょう。
「std::cout <<」は標準出力するための道具です。
その後に書いてあるものを出力します。
「"Hello World\n"」は文字列です。
文字列はダブルクォーテーション「”」で囲みます。
「\n」は改行文字です。
これを付けると改行してくれます。
最後にセミコロン「;」を付けます。
基本的にブロック「{ }」以外には「;」を付けると考えてもらえば大丈夫です。
5行目の
return 0;
は戻り値です。
はじめてのJava
Javaの学習をこれから始めたい方向けの記事を書きました。
基礎の部分はほぼ網羅しています。
書籍と比べると飛ばしている部分も多いですが、その分書籍より短い時間で最低限の知識を学べます。
プログラミングの学習をしたいけど時間がない方々はぜひ見てください。
1. Hello World編
2. 代入と計算編
3. 制御文編
4. クラスとメソッド編
5. 継承編
6. 抽象クラスとインタフェース編
7. パッケージとインポート編
8. 例外処理編
9. マルチスレッド編
10. デッドロックと協調編
11. ネットワーク編
はじめてのJava ~ネットワーク編~
今回はJavaでネットワークプログラミングをしていきます。
Webアプリなどを作るためには必須の技術ですのでしっかり覚えましょう。
クライアント/サーバアプリケーション
ここではクライアント/サーバアプリケーションを作っていきます。
それではさっそく見ていきましょう。
import java.io.*; // ネットワーク関係のパッケージ import java.net.*; class Server { public static void main(String args[]) { //ポート番号 final int PORT = 10000; try { String str; // サーバソケットを作成 ServerSocket ss = new ServerSocket(PORT); while(true) { // クライアントから要求を受ける Socket s = ss.accept(); // クライアントからの文字列を読み取る InputStream is = s.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); str = br.readLine(); System.out.println(str); // BufferedReaderオブジェクトをクローズする br.close(); } } catch(Exception e) { e.printStackTrace(); } } }
import java.io.*; import java.net.*; class Client { public static void main(String args[]) { final int PORT = 10000; // IPアドレス(ローカルホスト) final String LOCALHOST = "127.0.0.1"; try { Socket s = new Socket(LOCALHOST, PORT); // 文字列をサーバに書き込む OutputStream os = s.getOutputStream(); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os)); bw.write(args[0]); // BufferedWriterオブジェクトをクローズする bw.close(); } catch(Exception e) { e.printStackTrace(); } } }
説明の前に動かしてみましょう。
まず、コマンドプロンプトなどを開き、サーバプログラム(上のプログラム)を実行してください。
次に、もう一つコマンドプロンプトを開き、そちらでクライアントプログラム(下のプログラム)を実行します。
このとき、コマンドライン引数に好きな文字列を打ち込んでください。
はじめてのJava ~デッドロックと協調編~
今回はマルチスレッドで起こり得るデッドロックとその解決法について説明します。
これを理解しないとプログラムの停止などに繋がるのでしっかり覚えましょう。
デッドロックが起こる場面
次のプログラムはデッドロックを起こします。
class X { Y y; synchronized void x1() { System.out.println("x1"); y.y2(); } synchronized void x2() { System.out.println("x2"); } }
class Y { X x; synchronized void y1() { System.out.println("y1"); x.x2(); } synchronized void y2() { System.out.println("y2"); } }
class ThreadX extends Thread { X x; ThreadX(X x) { this.x = x; } public void run() { for(int i = 0; i < 100000; i++) { x.x1(); } } }
class ThreadY extends Thread { Y y; ThreadY(Y y) { this.y = y; } public void run() { for(int i = 0; i < 100000; i++) { y.y1(); } } }
class Deadlock { public static void main(String args[]) { X x = new X(); Y y = new Y(); x.y = y; y.x = x; ThreadX tx = new ThreadX(x); ThreadY ty = new ThreadY(y); tx.start(); ty.start(); try { tx.join(); ty.join(); } catch(Exception e) { e.printStackTrace(); } System.out.println("end"); } }
// 出力 x1 y1
このプログラムを実行すると、デッドロックが起こり途中で止まります。
なぜなら、ThreadXはx1()メソッドを実行した後y2()メソッドを呼び出そうとしますが、ThreadYはx2()メソッドを呼び出そうとして待っているので、お互いが相手が終わるのを待ってしまうからです。
デッドロックの解決法
デッドロックを解決するにはスレッドの動作を協調させる必要があります。
協調にはwait()メソッドとnotify()メソッド、notifyAll()メソッドを使います。
wait()は現在のスレッドを待機させます。
引数で秒数(ミリ秒)を指定することもできます。
notify()とnotifyAll()はオブジェクトのロック開放を待機しているスレッドに通知を送ります。
notify()は1つのスレッドに、notifyAll()はすべてのスレッドに送ります。
Xクラスを以下のように変えてみましょう。
class X { Y y; synchronized void x1() { try { wait(); } catch(InterruptedException e) { e.printStackTrace(); } System.out.println("x1"); y.y2(); notify(); } synchronized void x2() { System.out.println("x2"); } }
Deadlock.javaを実行すると止まらくなりましたね。
はじめてのJava ~マルチスレッド編~
今回はJavaにおけるマルチスレッドの実装をやっていきます。
マルチスレッドでは複数のスレッドを並行処理できるため効率的にプログラムを実行できますが、設計を誤ると結果が想定と異なってしまうなどの危険性もあります。
しっかり覚えて使いこなしましょう。
マルチスレッドプログラミング
スレッドの作成方法は2種類あります。
スレッドの作成1
Threadクラスを拡張する方法です。
スレッドの処理をrun()メソッドに記述し、start()メソッドでスレッドの実行を開始します。
class Thread1 extends Thread { public void run() { try { while(true) { Thread.sleep(500); System.out.println("Thread1"); } } catch(InterruptedException e) { e.printStackTrace(); } } }
class Thread2 extends Thread { public void run() { try { while(true) { Thread.sleep(500); System.out.println("Thread2"); } } catch(InterruptedException e) { e.printStackTrace(); } } }
class ThreadMainA { public static void main(String args[]) { Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); t1.start(); t2.start(); } }
// 出力 Thread2 Thread1 Thread1 Thread2 Thread2 Thread1 Thread2 Thread1 ・ ・ ・
同じ間隔で出力しているためシングルスレッドであれば「Thread1→Thread2→Thread1→Thread2→・・・」となるはずですが、マルチスレッドにしたので順番になりません。
スレッドの作成2
Runnableインタフェースを実装したクラスを宣言する方法です。
run()メソッドにスレッドの処理を記述するところは先程と同じですが、Threadコンストラクタの引数にRunnableインタフェースを実装したクラス(Runnable1、Runnable2)のオブジェクトを渡すところが異なります。
class Runnable1 implements Runnable { public void run() { try { while(true) { Thread.sleep(500); System.out.println("Runnable1"); } } catch(InterruptedException e) { e.printStackTrace(); } } }
class Runnable2 implements Runnable { public void run() { try { while(true) { Thread.sleep(500); System.out.println("Runnable2"); } } catch(InterruptedException e) { e.printStackTrace(); } } }
class ThreadMainB { public static void main(String args[]) { Runnable1 r1 = new Runnable1(); Runnable2 r2 = new Runnable2(); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); } }
// 出力 Runnable1 Runnable2 Runnable2 Runnable1 Runnable2 Runnable1 Runnable2 Runnable1 ・ ・ ・
同期
マルチスレッドで処理を行うことにより不具合が起こることがあります。
共通の資源に同時にアクセスしてしまうことで、あるスレッドの処理が無視されてしまうのです。
例えば以下のような場合。
class Calc { int num; void increment() { num++; } }
class Call extends Thread { Calc c; Call(Calc c) { this.c = c; } public void run() { try { c.increment(); } catch(Exception e) { e.printStackTrace(); } } }
class SyncMain { private final static int LNUM = 100000; public static void main(String args[]) { Calc calc = new Calc(); Call call[] = new Call[LNUM]; // スレッドの作成と開始 for(int i = 0; i < LNUM; i++) { call[i] = new Call(calc); call[i].start(); } // スレッドがすべて終了するまで待機 for(int i = 0; i < LNUM; i++) { try { call[i].join(); } catch(InterruptedException e) { e.printStackTrace(); } } System.out.println(calc.num); } }
// 出力 99990
これは、初期値0の1つの変数にマルチスレッドで100000回”+1”するプログラムです。
結果は100000になってほしいのですが、99990になってしまっています。
理由を図で説明します。
シングルスレッドであれば以下のように計算されます。
しかし、マルチスレッドにすることで、以下のようなことが起こる可能性があります。
このような計算では、あるスレッドが資源を使っているときは他のスレッドは資源を使えないようにロックをかけなくてはなりません。
それを行う機能が同期です。
「synchronized」を使いますが、修飾子として使う方法とブロックで使う方法があります。
先程のCalcクラスを以下のように変更します。
// 修飾子として使う class Calc { int num; synchronized void increment() { num++; } }
または、
// ブロックで使う class Calc { int num; void increment() { synchronized(this) { num++; } } }
変更後は、いずれも出力は100000になります。
はじめてのJava ~例外処理編~
例外処理とはプログラムの実行中に発生した問題に対する処理のことです。
例外処理を記述することで、問題の発生よる異常終了を避けることができます。
堅牢なシステムを作るためには非常に重要な部分ですので、しっかり使い方を覚えましょう。
例外処理が必要な例
コマンドライン引数を2つ取り、それらを足し合わせた後に「aaa」と出力するプログラムを書きます。
class Calc { public static void main(String args[]) { double ans, a, b; a = Double.parseDouble(args[0]); b = Double.parseDouble(args[1]); ans = a + b; System.out.println("aaa"); } }
コマンドライン引数が1つしかなかった場合、以下のようなエラーメッセージが出ます。
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1 at Calc.main(Calc.java:5)
本来であれば「aaa」と出力されるところが、それ以前に止まってしまっているため出力されません。
このような異常時にも、本来の出力をしたいときなどに例外処理が役立ちます。
例外処理の記述
tryブロックの中に例外が発生し得る処理を記述し、catchブロックの中に例外発生時の処理を記述します。
また、オプションでfinallyブロックに例外が発生しても発生しなくても実行したい処理を記述することができます。
class Calc { public static void main(String args[]) { double ans, a, b; try { a = Double.parseDouble(args[0]); b = Double.parseDouble(args[1]); ans = a + b; } catch(ArrayIndexOutOfBoundsException e) { System.out.println(e); } finally { System.out.println("aaa"); } } }
このように記述すると、コマンドライン引数を1つにしても「aaa」が出力されます。
java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1 aaa
例外を明示的に起こす
「throw」を使うことで例外を明示的に起こすこともできます。
class ExceptionSample1 { public static void main(String args[]) { try { if(Double.parseDouble(args[0]) == 0) { throw new Exception(); } } catch(Exception e) { System.out.println(e); } } }
java.lang.Exception
呼び出し元に例外を投げる
「throws」を使うことで呼び出し元に例外を投げることができます。
class ExceptionSample2 { public static void main(String args[]) { try { a(); } catch(ArithmeticException e) { System.out.println(e); } } public static void a() throws ArithmeticException { double a1 = 1 / 0; } }
// 出力 java.lang.ArithmeticException: / by zero
throwとthrowsは名前は似ていますが使い方は全く異なるので注意しましょう。