C++入門(1)

入出力,引数,データの読み込み,vector,関数,乱数

画面操作キー(全画面,ワイド表示,ソースコードハイライト)

コンパイルと実行

  • プログラミングは,次のサイクルを繰り返します.
    • ソースコードの作成・修正
    • コンパイル
    • 実行
  • C++のソースコードのファイル名には,拡張子".cpp"をつけてください
  • 本講義では,コンパイラg++をオプション-std=c++11付きで使用.
  • コンパイル例(ソースコードファイルをprog1.cpp,実行ファイルをprog1とします): 
  • g++ -std=c++11 prog1.cpp -o prog1 
  • 実行例:
  • ./prog1

coutを使った例

以下のコードをprog1.cppという名前で保存し,コンパイル,実行してください.endlの右端の文字lはエルです.

#include <iostream>
using namespace std;
int main(){
  cout << "Hello" << endl;
}
  • cout(標準出力)に"<<"を使って文字列を代入(出力)することができます.例では,下記のようにHelloと改行(endl)が端末へ出力されます.
  • ./prog1
    Hello
  • 1行目の#include <iostream>は,「標準入力や標準出力とのやりとりに必要なライブラリを使う」という意味.このプログラムでは必須

変数(string, int, double)

以下のコードをprog1.cppという名前で保存し,コンパイル,実行してください

#include <iostream>
#include <string>
using namespace std;
int main(){
  string s1  = "選挙権は";
  string s2  = "円周率は";
  int    age = 18;
  double  pi = 3.14;
  cout << s1 << age << ", " << s2 << pi << endl;
}
  • 実行結果は下記.stringは文字列変数s1,s2を,intは整数型変数ageを,doubleは浮動小数点型変数piのための「型」.#include <string>は,文字列を扱うため
  • ./prog1
    選挙権は18, 円周率は3.14

引数

コマンドの引数を変数に代入することができます.そのためにmain関数の書き方を変えます(引数がない場合も使えます).以下のコードをprog1.cppという名前で保存し,コンパイル,実行してください.引数があるので注意

#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[]){
  string name = argv[1];
  int     age = stoi(argv[2]);
  cout << "名前は" << name << ", 年齢は" << age << endl;
}
  • 実行結果は下記.第1,2引数は,argv[1]argv[2]によりプログラム内で文字列として参照可.age(整数型)に代入する前に,整数型へ変換する関数stoiを適用
  • ./prog1 Hana 20
    名前はHana, 年齢は20

cinを使った例

以下のコードをprog1.cppという名前で保存し,コンパイル,実行してください

#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[]){
  cout << "あなたの名前の入力:";
  string name;
  cin >> name;
  cout << "名前は" << name << endl;
}
  • cin(標準入力)から">>"を使って文字列を代入することができます.例では,あなたの名前の入力:の表示に続けて文字列を入力しEnterキーを押すとnameに代入されます
  • ./prog1
    あなたの名前の入力:Hana
    名前はHana

外部ファイルからの入力(1)

以下をdata1.datprog1.cppいう名前で保存し,ソースコードはコンパイルしてください.

1 2
3
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char* argv[]){
  string fileName = argv[1];
  ifstream ifile( fileName );
  int d1, d2, d3;
  ifile >> d1 >> d2 >> d3;
  cout << d1 << " " << d2 << " " << d3 << endl;
}
  • ifstream ifile( fileName )の,ifstreamは,入力ファイルストリームifileをファイル名fileNameで開くための「型」.#include <fstream>は,ファイルストリームを扱うため.data1.datを第1引数とすれば,そこからデータを順次d1,d2,d3に代入(下記実行例)
    ./prog1 data1.dat
    1 2 3

外部ファイルからの入力(2)

#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char* argv[]){
  string fileName = argv[1];
  ifstream ifile( fileName );
  int d;
  while( ifile >> d ){
    cout << d << endl;
  }
}
  • prog1.cppを変更.今度は,while( ifile >> d )により,入力ファイルストリームifileから次のデータを変数dへ代入.データがあればこの代入はになり,次の文によりデータdを出力して改行(下記実行例)
  • ./prog1 data1.dat
    1
    2
    3
  • データ数に関わらず,すべてを読み込んで処理できる

外部ファイルからの入力(3)

#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char* argv[]){
  string fileName = argv[1];
  ifstream ifile( fileName );
  string buffer;
  while( getline(ifile, buffer) ){
    cout << buffer << endl;
  }
}
  • prog1.cppを変更.今度は,while( getline(ifile, buffer) )により,入力ファイルストリームifileから次の1行を文字列としてbufferへ代入.データがあればこの代入はになり,次の文によりデータbufferを出力して改行(下記実行例)
  • ./prog1 data1.dat
    1 2
    3
  • 行ごとの処理を行う場合に使う.複数のデータを含む文字列からデータを取り出す方法を次で紹介

外部ファイルからの入力(4)

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
using namespace std;
int main(int argc, char* argv[]){
  string fileName = argv[1];
  ifstream ifile( fileName );
  string buffer;
  while( getline(ifile, buffer) ){
    istringstream iss(buffer);
    int d;
    while( iss >> d ) cout << d << endl;
  }
}
  • prog1.cppを変更.今度は,読み込んだbufferを元にistringstreamにより,文字列ストリームissを宣言.ストリームissは入力ファイルストリームと同様に扱え,">>"を使って変数への代入が可能.#include <sstream>は,文字列ストリームを扱うため(下記実行例)
    ./prog1 data1.dat
    1
    2
    3

vectorによる配列(ベクトル)操作(1)

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
int main(int argc, char* argv[]){
  vector<int> vec1(3,0);
  vector<int> vec2 = {8,9,10};
  cout << vec1[0] << " " << vec1[1] << " " << vec1[2] << endl;
  cout << vec2[0] << " " << vec2[1] << " " << vec2[2] << endl;
}
  • prog1.cppを変更.vector<int>は,整数型intの配列(=ベクトル)vectorを宣言する型で,vector<int> vec1(3,0)により,次元数が3の配列を要素値0で初期化してvec1を宣言,vector<int> vec2 = {8,9,10}は,値指定で初期化しながらvec2を宣言.同様にして浮動小数点型doubleの配列なども宣言可
  • 配列の要素は[添字]により参照(添字は0始まり).#include <vector>は,配列(ベクトル)を扱うため
  • ./prog1
    0 0 0
    8 9 10
    

vectorによる配列(ベクトル)操作(2)

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
int main(int argc, char* argv[]){
  vector<int> vec1(3,0);
  vector<int> vec2 = {8,9,10};
  vector<vector<int>> vecs;
  vecs.emplace_back(vec1);
  vecs.emplace_back(vec2);
  cout << vecs[0][0] << " " << vecs[0][1] << " " << vecs[0][2] << endl;
  cout << vecs[1][0] << " " << vecs[1][1] << " " << vecs[1][2] << endl;
}
  • prog1.cppを変更.vector<vector<int>>は,整数型の配列vector<int>の配列をvector<vector<int>> vecsにより宣言. vecs.emplace_back(vec1)とすることで,vecsの最後尾に配列vec1と同じ配列を追加
    配列の配列vecsの要素(=配列)は[添字]により参照.参照した配列の要素も[添字]で参照するので,値の参照は[添字][添字]という形になる
    ./prog1
    0 0 0
    8 9 10
    

vectorによる配列(ベクトル)操作(3)

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
int main(int argc, char* argv[]){
  string fileName = argv[1];
  ifstream ifile( fileName );
  vector<int> vec;
  int d;
  while( ifile >> d ){
    vec.emplace_back(d);
  }
  cout << vec[0] << " " << vec[1] << " " << vec[2] << endl;
}
  • prog1.cppを変更.vec.emplace_back(d)とすることで,vecの最後尾に整数dの値を持つ要素を追加.外部ファイルのデータを配列に読み込むことも可.行ごとに配列として読み込み,これを束ねて配列の配列にすることも可(例:教科書p.20のread2D.cpp
  • ./prog1 data1.dat 
    1 2 3
    

関数(1)

関数は,何度も使う一連の処理をまとめ,呼び出して使えるようにしたもの.下記は,形式(左)と例(右)

戻り値の型 関数名( 引数の型と名前,..){

  関数本体

  return 戻り値
}
double sum( vector& vec ){
  double val = 0;
  for( int i = 0 ; i < vec.size() ; i++ ) 
    val += vec[i];
  return val;
}
  • 引数は,呼び出される側を仮引数,呼ぶ出す側を実引数とよび,名前を変えることも可
  • 仮引数の型の後ろに"&"をつけると引数の「参照渡し」となり,仮引数は呼び出し元と同じように扱え,変更は呼び出し元(実引数)にも反映されます.こちらを使うことが多い
  • 仮引数の型の後ろに"&"をつけなければ「値渡し」となり,仮引数は呼び出し元の実引数と別物になります.その変更は呼び出し元(実引数)には反映されません
  • 上の例において,vec.size().size()は,配列vecのサイズ(要素数)を返すメソッド(≒関数)
  • 次ページに使用例を示します.この関数は,配列の要素の和を求めます.下記は実行例
  • ./prog1 data1.dat 
    6
    

関数(2)

  • 関数の使用例.一般には,関数の定義と関数の呼び出し部分(プロトタイプ宣言)を別ファイルに保存し,プロトタイプ宣言が書かれたファイル(ヘッダファイル)を#includeを使い,読み込んで利用する(教科書p.50-54)
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <sstream>
    #include <vector>
    using namespace std;
    double sum( vector<int>& vec ){
      double val = 0;
      for( int i = 0 ; i < vec.size() ; i++ ) 
        val += vec[i];
      return val;
    }
    int main(int argc, char* argv[]){
      string fileName = argv[1];
      ifstream ifile( fileName );
      vector<int> vec;
      int d;
      while( ifile >> d ){
        vec.emplace_back(d);
      }
      cout << sum( vec ) << endl;
    }

乱数

  • 乱数の使用例.mt19937 gen(seed)は,乱数生成器genを乱数初期化の種seedを使っての宣言.続く2行は,それぞれ一様分布の整数乱数(0以上9以下)と同じく一様分布の浮動小数点乱数(0以上1未満)の宣言.乱数の生成は,乱数生成器genを引数にして行う.#include <random>は,乱数を扱うため.一様分布以外も用意されている
  • #include <iostream>
    #include <random>
    using namespace std;
    int main(int argc, char* argv[]){
      int seed = stoi( argv[1] );
      mt19937 gen(seed);
      uniform_int_distribution<int>     distInt(0, 9); // [0,9]の整数乱数
      uniform_real_distribution<double> distDbl(0, 1); // [0,1)の浮動小数点乱数
      cout << distInt(gen) << " " << distInt(gen) << " " << distInt(gen) << endl;
      cout << distDbl(gen) << " " << distDbl(gen) << " " << distDbl(gen) << endl;
    }
  • 実行例を示す.引数で乱数の種を指定している.これを変えると結果が変わる
  • ./prog1 0
    7 5 5
    0.423655 0.544883 0.602763

<Thank You!>

C++入門(1) 内山俊郎