第 3 回 プログラミング言語

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

3-1. C 言語

ローカル変数、グローバル変数

C 言語では関数の内部で宣言された変数は、その関数の外部(他の関数など)か らはアクセスできません。 これをローカル変数と言います。 一方、関数の外側でも変数を宣言できます。 これをグローバル変数と言います。 グローバル変数へはどんな関数でもアクセスできます。 但し、別のファイルの中にあるグローバル変数にアクセスするには関数のプロ トタイプのように宣言をする必要があります。 グローバル変数を宣言するのがextern 宣言 です。構文は次の通りです。


extern 変数名

これは、グローバル変数であることを宣言しているだけで、変数そのものの宣 言になってません。そのため、一つのソースファイルでグローバル変数の宣言 をする必要があります。 一般に、このような extern 宣言はヘッダファイルに格納します。

但し、グローバル変数の利用には注意が必要です。 グローバル変数は全ての関数からアクセスが可能なので、プログラムミ スなどによりグローバル変数の値がおかしくなった時など、原因の追求が大変 です。プログラム分割をシンプルにしたい立場からも、グローバル変数という 全関数が知らなければならないような情報はない方が良いです。 但し、 C++ などのオブジェクト指向言語ではないので、関数の外部に情報を 蓄えるにはグローバル変数を使う必要があるでしょう。 逆に C++ 言語、Java 言語などのオブジェクト指向言語では、特定の変数とそ れにアクセスする関数をまとめてクラスとして扱えるので、グローバル変数を 使わないようにすべきです。 変数など、不必要な情報へのアクセスを制限することをオブジェクト指向では カプセル化と言います。

なお、グローバル変数と同じ名前の変数をローカル変数として宣言可能です。 おなじ名前になった時は、グローバル変数にはアクセスできず、ローカル変数 にだけアクセスできます。

また、関数の宣言の時に宣言する引数は、値を読み出すことはできますが、そ れは値が与えられたローカル変数なので、書き換えた値は呼び出した側には影 響しません。

演習3-1

次のプログラムがどのように動くか予想し、また、実際に動かして動作を確か めなさい。


#include <stdio.h>
int i;
int a(int i){
   i++;
   return i;
}
int b(int j){
   i++;
   return i;
}
int c(int k){
  int i;
  i=0;
  return i;
}
int main(void){
  i=1;
  printf("%d\n",i);
  printf("%d ",a(i));
  printf("%d\n",i);
  printf("%d ",b(i));
  printf("%d\n",i);
  printf("%d ",c(i));
  printf("%d\n",i);
  return 0;
}  

キャスト

ある型を別の型として扱いたい時に、一時的に別の型を指定する方法があります。 例えば、整数(int)の値 1 と 2 を考えた場合、個数や合計は整数(int)になり ますが、平均値は実数(double)になります。 C 言語では(整数)/(整数)の結果は整数になってしまうため、割算をする時、 double だと指定する必要があります。そこで、「(型) 式」と書くと小数点以 下を切り捨てられず、結果が double 型になります。


#include <stdio.h>
int main(void){
  int a[]={1,2,-1}
  int i;
  int goukei, kosuu;
  double heikin;

  kosuu=0;
  goukei=0;
  for(i=0;a[i]!=-1;i++){
    kosuu++;
    goukei+=a[i];
  }
  heikin=(double)goukei / kosuu;
  printf("個数=%d, 合計=%d, 平均=%f\n",
          kosuu, goukei, heikin);
  return 0;
}

ポインタ

C 言語では、変数の値を格納しているメモリの番地を取り扱うことができます。 変数 a に対して、その変数の値のメモリの番地は &a で表します。 また、この番地を変数に代入して処理することもできます。 int 型の変数の番地を扱う変数の型は int * で表します。 int *x; と宣言すると x は int の変数の番地を取り扱うこと ができます。 そして int y; と整数型変数 y を宣言しておくと、 x = &yで y の番地を代入できます。 また、 *x とするとこれは x に格納されているメモリの番地に入っている値 を表します。 もし y の値が 1 ならば、*x も 1 になります。 このメモリの番地を扱う変数を ポインタ と言います。 このメモリの番地やポインタには様々な利用法があります。

なお、「どの変数やメモリも指していない無効な番地」を意味する定数として NULL が定義されています。 関数においてポインタの値の計算を失敗した時や、番兵などに使用できます。

関数とのデータのやりとり

関数を呼び出す時、引数の処理のしかたには次の 3 種類があります。

値呼び出し call by value
関数を呼び出す時、引数の値をコピーして関数に渡します。 関数内で引数の変数の変更をしても、呼び出し側の変数は変更されません。
変数呼び出し call by variable (参照呼び出し call by reference)
関数を呼び出す時、引数として指定された変数は呼び出し側の変数と同一のも のとして扱われます。 したがって、関数内で引数を変更すると、呼び出し側でその値を参照できます。
名前呼び出し call by name
関数を呼び出す時に、引数に式や関数が指定されている場合、引数を評価 する必要がない可能性を考慮して、関数に引数を式のまま持っていきます。 値を計算する必要が生じる時だけ評価します。 なお、評価が高々一回だけ行われる場合、必要呼び出し call by need と呼ば れます。

C 言語では値呼び出しだけがサポートされています。 また、C 言語では関数の中で宣言された変数はローカル変数です。 したがって、ある変数の値を表示する関数は何の工夫もなしに作ることができ ますが、その変数の値を変更するようなプログラムは何の工夫もなくは作れま せん。 例えば次のような関数に意味はありません。


int f(int x){
   x=1;
   return 0;
}

この関数を f(y) と呼び出しても、 y の値は変わりません。

さて、関数で変数の値を変えるにはどうすればよいでしょうか? もし、一つの変数の値だけなら、 a=f(a) のように関数の戻り 値を代入することで f から a の値を変更できます。 しかし、二つ以上の変数に対しては行えません。 例えば、変数 a と変数 b の値を交換することを考えます。 関数さえ呼び出さなければ次のようにすればできます。


int a=4,b=1;
int c;

c=a;
a=b;
b=c;

この処理はしばしば出てくるので、これを関数としたいところです。 ところが次のようにしても思うように動きません。


#include <stdio.h>
void swap(int x, int y){
  int z;
  z=x;
  x=y;
  y=z;
  return;
}
int main(void){
  int a,b;
  a=2; b=3;
  swap(a,b);
  printf("a=%d, b=%d\n",a,b);
  return 0;
}

関数の引数の宣言での x, y はローカル変数です。 従って、呼び出す側の変数と値は同じですが、既に別の変数ですので、代入し ても呼出側の変数の値は変わりません。

そこで、ポインタを使います。 呼び出し側の変数の番地をサブルーチンに渡し、関数では、その番地に値を書 き込むのです。 次のようにすると思った通りの動きになります。


#include <stdio.h>
void swap(int *x, int *y){
  int z;
  z=*x;
  *x=*y;
  *y=z;
  return;
}
int main(void){
  int a,b;
  a=2; b=3;
  swap(&a,&b);
  printf("a=%d, b=%d\n",a,b);
  return 0;
}

配列の要素を指す

C 言語での配列名は実はポインタです。 配列 a の 0 番目の要素が格納されているメモリ上の番地が a になっていま す。つまり、 &a[0] と a は等しいです。ですから、a[0] と *a は等し いです。


#include <stdio.h>
int main(void){
  double a[]={1.0,2.0,3.0,-1.0};
  printf("&a[0]=%d, a=%d\n",(int)&a[0],(int)a);
  printf("a[0]=%f, *a=%f\n",a[0],*a);
  return 0;
}

では a[1] と a の関係はどうでしょうか? double のように一文字では表せないようなデータは、複数の番地に渡って格 納されているため、 a の隣の番地はまだ a[0] の内容の続きが入っています。 sizeof 演算子を使うと、その型が何バイトの記憶領域を使うかが 分かります。 筆者の環境では sizeof (double) の値は 8 になっています。 つまり、一つの double 型の変数を格納するのに 8 バイトの記憶領域が必要 になってきます。 ですから、 a[1] の番地は a[0] の番地から 8 バイト先の位置に記憶されて います。 結局、 C 言語の配列というのは次のようにして計算された値になります。

a[i] = *( a+i *sizeof(a[0] ))

ところが、ポインタの計算では、何の型のポインタかが明らかなため、 +1 を すると、次の要素を指すように値が増えます。つまり、その型のサイズ分だけ 増えます。 結局 a[1]=*(a+1) となります。


#include <stdio.h>
int main(void){
  double a[]={1.0,2.0,3.0,-1.0};
  printf("&a[1]=%d, a=%d, sizeof double = %d\n",
	 (int)&a[1],(int)a,sizeof (double));
  printf("a + 1 * sizeof a[0] = %d, a + 1 =%d \n",
	 (int)a + 1*sizeof a[0], (int)(a+1)); 
  printf("a[1]=%f, *(a+1)=%f\n",a[1],*(a+1));
  return 0;
}

これを使うと、配列の値を次々指し示す場合に ++ などのインクリメントが使えま す。 またループ変数を無くすこともできます。


#include <stdio.h>
int main(void){
  double a[]={1.0,2.0,3.0,-1.0};
  double *x;
  for(x=a; *x != -1; x++){
    printf("%f\n",*x);
  }
  return 0;
}

構造体

座標のように、複数の値を対に用いたい時に使うのが構造体です。 座標の構造体の型は次のように宣言されます(最後のセミコロン(;)は必須です)。


struct point {
  double x;
  double y;
};

そして、この後、座標の変数を宣言するには次のようにします。


struct point p;
struct point *q;
struct point r={1.0,2.0};

プログラムの中では、まず、構造体から構造体へは代入が可能です。 p=rq=&p*q=r な どできます。 また、構造体の内部の値はピリオド(.)の後にフィールド名を書きます。 p.x=3(*q).y=4 などが可能です。 また、特に、(*q).yq->yと書くこ とができます。

文字配列

C 言語では文字配列の関数が提供されています。 利用するには string.h ヘッダファイルを読み込む必要があります。 その時、次を行うプログラムが以下に示すように簡単に書けます。 なお、文字配列の最後には必ず '\0' を入れておく必要があります。

  1. 文字列の長さを求める
  2. 文字列中で最初に特定の文字が出現する位置
  3. 文字列中に含まれる特定の文字の数
  4. 文字列の結合

関数を使わない場合

  1. 
    #include <stdio.h>
    int main(void){
      char x[]="This is a pen.";
      int i,k;
      k=0;
      for(i=0;x[i]!='\0';i++){
        k++;
      }
      printf("%d\n",k);
      return 0;
    }
    
  2. 
    #include <stdio.h>
    int main(void){
      char x[]="This is a pen.";
      char y='p';
      int i,k;
      k=0;
      for(i=0;(x[i]!='\0')&&(x[i]!=y);i++){
        k++;
      }
      if(x[k]!='\0'){
        printf("%d\n",k);
      }else{
        printf("Not found.\n");
      }
      return 0;
    }
    
  3. 
    #include <stdio.h>
    int main(void){
      char x[]="This is a pen.";
      char y='p';
      int i,k;
      k=0;
      for(i=0;x[i]!='\0';i++){
        if(x[i]==y){
          k++;
        }
      }
      printf("%d\n",k);
      return 0;
    }
    
  4. 
    #include <stdio.h>
    int main(void){
      char x[]="This is a pen.";
      char y[]="That is a book.";
      char z[50];
      char *p,*q;
      for(p=x,q=z;*p!='\0';*(q++)=*(p++));
      for(p=y;*p!='\0';*(q++)=*(p++));
      *q='\0';
      printf("%s\n",z);
      return 0;
    }
    

関数を使った場合

  1. 
    #include <stdio.h>
    #include <string.h>
    int main(void){
      char x[]="This is a pen.";
      printf("%d\n",strlen(x));
      return 0;
    }
    
  2. 
    #include <stdio.h>
    #include <string.h>
    int main(void){
      char x[]="This is a pen.";
      char y='p';
      char *z;
      z=strchr(x,y);
      if(z!=NULL){
        printf("%d\n",(int)(z-x));
      }else{
        printf("Not found.\n");
      }
      return 0;
    }
    
  3. 
    #include <stdio.h>
    #include <string.h>
    int main(void){
      char x[]="This is a pen.";
      char y='p';
      char *z;
      int k;
      k=0;
      for(z=strchr(x,y);z!=NULL;z=strchr(z+1,y)){
        k++;
      }
      printf("%d\n",k);
      return 0;
    }
    
  4. 
    #include <stdio.h>
    #include <string.h>
    int main(void){
      char x[]="This is a pen.";
      char y[]="That is a book.";
      char z[50];
      strcpy(z,x);
      strcat(z,y);
      printf("%s\n",z);
      return 0;
    }
    

3-2. C++言語

C++ 言語は C 言語に Simula67 で開発されたオブジェクト指向の考え方を導入 したものです。 これはプログラミングの方法論を変えてしまう仕組みです。 C++ 言語を使う以上、C++ の流儀に基づいてプログラミングをすべきです。

コメント

C 言語ではコメントとして/* と */ を使いましたが、 C++ 言語ではさらに // から行末までもコメントとして取り扱います。 /* */ は入れ子に出来ませんので、 // でコメントを付けた部分は、後で /* と */ で囲めるので便利です。 例えば、プログラムの注釈のコメントは // の後に書くように統一すると、プ ログラムの特定部分を無効にするのに /* と */ で括っても、 // のコメント は含むことができるので、便利です。


i=2; //初期値
/*
j=3  //これも初期値
*/

オーバーロード

C++ 言語では引数の型だけが違う同じ名前の関数が許されています。 これをオーバーロードと言います。 これは、C 言語では禁止されています。


double square(double x){
  return x*x;
}
int square(int x){
  return x*x;
}

名前空間

大きなプロジェクトにおいて関数、変数、クラスなどの名前は衝突する可能性 があります。 そのため、これらの名前に名字を付けるような感覚で、名前空間という手法を 使うことができます。


namespace sakamoto {
  int i;
  double f(double x);
  class C {
    C* create(); 
  };
}

また namespace でプロトタイプを定義した場合、 namespace 外でスコープ演算子 :: を用いて定義できます。


double sakamoto::f(doube x){
  return 1.2;
}
sakamoto::C* sakamoto::C::create(void){
  return new sakamoto::C();
}

このように定義すると他で定義された変数 i や関数 f(x) やクラス C と分け ることができます。 実際に使う際には次のようにします。

スコープ演算子を使用

int main(void){
  sakamoto::C x;
  x.show();
  return 0;
}
using を使って、使用する名前を登録

using sakamoto::C;
int main(void){
  C x;
  x.show();
  return 0;
}
using を使って、名前空間を登録

using namespace sakamoto;
int main(void){
  C x;
  x.show();
  return 0;
}

このうち最後の名前空間を登録する方法は簡便ですが、名前の衝突を避けると いう本来の名前空間の目的に反しているので推奨できません。

なお、C++ はもともとは名前空間を持たず、途中から導入されました。 名前空間を持たなかった頃のヘッダファイルには C 言語同様に .h の拡張子 が付いていました。 ですから、iostream.h は名前空間が定義されていません。 一方 iostream と .h の付かないヘッダファイルには std という名前空間が 定義されています。 従って、例えば iostream を使って標準出力に値を出力するには次のようにし ます。

  1. 
    #include <iostream>
    int main(void){
      std::cout << "Hello World!" << std::endl;
      return 0;
    }
    
  2. 
    #include <iostream>
    using std::cout;
    using std::endl;
    int main(void){
      cout << "Hello World!" << endl;
      return 0;
    }
    
  3. 
    #include <iostream>
    using namespace std;
    int main(void){
      cout << "Hello World!" << endl;
      return 0;
    }
    

C 言語で使用したヘッダファイルに関しては、ファイル名から.h を除き、語 頭に c を付けたものが用意されています。 この中で使用する関数は名前空間 std に含まれています。

例3-1


#include <cstdio>
int main(void){
  std::printf("Hello World\n");
}

変数宣言

C++ では C 言語と違い、宣言を自由な位置に置くことができます。 従って、条件に適合する時だけ巨大な配列を作り、必要ないときは領域を作ら ないように処理することもできます。 また、変数宣言はブロック内のみ有効で、ブロックの外で廃棄されます。


if(a==0){
  int b=1;
}    // この部分で b は廃棄される
a=b; // b は既に廃棄されているのでエラーになる

クラス

C++ では C 言語とのしがらみがない限りは、構造体を使いません。 その代わり、構造体の拡張のような形で クラス が導入されています。 クラスの内部では変数だけではなく、関数のプロトタイプを書くことができま す。


class Sample {
  double x;
  double y;
  void show(void);
};

class はオブジェクト指向を実現するために導入されました。 オブジェクト指向とは、メッセージのやりとりを行うオブジェクトを用いてプ ログラムを作成する手法です。 オブジェクトとは変数とメソッドをもつものです。 オブジェクトを生成し、それに「メソッド+引数」というメッセージ を送ることで計算を行います。 このオブジェクトの設計図と言えるものがクラスです。 クラスでは生成したオブジェクトが持つ変数宣言と、メソッドの定義を行いま す。メソッドの定義はクラスの中では関数のように定義します。


class Sample {
  double x;
  double y;
  void show(void){ std::cout << x << y; }
};

クラスの中にはプロトタイプのみを書き、外部で改めてメソッドを定義する時 はクラス名とメソッド名の間にスコープ演算子 :: を使います。 慣例ではクラス宣言をヘッダファイル、メソッドの実装は cpp ファイルに入 れます。


//----- sample.h ファイル ------
class Sample {
  double x;
  double y;
  void show(void);
};
//----- sample.cpp ファイル ------
void Sample::show(void){
  std::cout << x << y;
}

なお、オブジェクト指向プログラミングでは外部から変数への直接アクセスを 禁止し、必ずメソッドのみでオブジェクトを操作することが好まれます。 このようにオブジェクト指向で変数などを隠蔽することをカプセル化 と言います。 また、メソッドも外部に公開するものと内部だけで使うものに分かれますが、 特に外部に公開するメソッドをインターフェイスと言います。 このようにすると、オブジェクトの操作方法は限定されたものになるため、操 作に必要な情報が少なくて済みます。

C++ ではこのカプセル化のコントロールのため、外部からのアクセスを禁止す るため private: というキーワードが用意されています。 その反対に外部に公開するメソッド(インターフェイス)のために public: というキーワードも用意されています。


//----- sample.h ファイル ------
class Sample {
 private:
  double x;
  double y;
 public:
  void show(void);
};

クラスを作ると、新しい型としてオブジェクトの宣言が可能です。 生成したオブジェクトに対して、 public 宣言された変数にアクセスできるこ とは構造体と同じです。

クラス名 オブジェクト名;

また、構造体と同様に、変数名やメソッド(関数)はピリオドの後に書きます。 メソッド(関数)はメソッド名の後に丸カッコ()を付けます。

オブジェクト名.メソッド(引数)

なお、 C++ 言語ではオブジェクトのポインタも用意されています。 宣言は通常と同じ * 付きの変数として宣言します。 オブジェクトは変数宣言の他、 new 演算子によっても生成できます。 クラス名と同じ名前のメソッドをコンストラクタと言います。 これはオブジェクトを生成する時に初期化のために呼び出されるメソッドです。 new 演算子の後にコンストラクタを呼び出すと、オブジェクトを生成してポイ ンタを返します。 オブジェクトを作る際にコンストラクタが呼ばれますので、クラスに一つ必ず コンストラクタが必要です。 また、ポインタからメソッドを呼び出すには * と . の組合せでもできますが、 ポインタ専用に -> を使うことができます。

なお、 new で生成したオブジェクトは自動的に廃棄されませんので、 delete ポインタ で消す必要があるときがあります。 オブジェクトのポインタの配列を廃棄する場合、delete [] ポインタ と指定方法が違うので注意する必要があります。 一方、変数が廃棄される際、特別な処理が必要な場合、デストラクタ を定義します。 デストラクタは C++ では ~ にクラス名を付けたメソッドです。


#include <iostream>
using std::cout;
using std::endl;
class Sample {
 private:
  int x;
 public:
  Sample(void){x=0;} // コンストラクタ
  ~Sample(void){cout << "object is deleted." << endl; }
                 // デストラクタ
  void set(int n){x=n;}
  int get(void){return x;}
};
int main(void){
  Sample b; //コンストラクタが自動的に呼ばれる
  cout << b.get() << endl;
  b.set(3);
  cout << b.get() << endl;
  Sample *c; //ポインタが作られるだけでオブジェクトは作られない
  c = new Sample(); // オブジェクトの生成
  cout << c->get() << endl;
  c->set(4);
  cout << c->get() << endl;
  delete c; // c が指すオブジェクトの明示的な廃棄
  return 0;
}

オブジェクトは生成する度いくつでも作ることができます。 一方で、複数のオブジェクトで共有させておきたいデータや手続きに関しては キーワード staticを付けて定義できます。 これをクラス変数クラスメソッドと呼びます。 静的〜と呼ぶこともあります。 これらはクラス名.変数名、あるいはクラス名.メソッド 名で参照します。 以下はこのクラス変数やクラスメソッドと使用した例で、 Singleton デザインパターンという定石です。 コンストラクタの代わりに Sample.getInstance クラスメソッドを呼び出すこ とで、外部から何度 getInstance を呼び出しても、作成されるオブジェクトは単一 のままになります。


class Sample {
  private:
    static Sample * instance = null;
  public:
    Sample(void){ }
    ~Sample(void){ instance = null; }
    static Sample * getInstance(void){
      if(instance==null){
        instance = new Sample();
      }
      return instance;
    }
};

テンプレート

テンプレートは特定の関数やオブジェクトクラスを任意の型に対して生成した い時に使います。 例えば、数の二乗をする関数を次のようにテンプレートで作成します。


template <typename T>
T square(T a){
  return a*a;
}

これをプログラムに組み込むと、C++ コンパイラは自動的 にその型に応じた関数を生成します。


#include <iostream>
using std::cout;
using std::endl;
template <typename T>
T square(T a){
  return a*a;
}
int main(void){
  cout << square(2) << endl
       << square(3.1) << endl;
  return 0;
}

STL

STL(Standard Template Library) は良く使われるデータ構造と関 数が集められたテンプレートです。 なお、STL の関数はアルゴリズムと呼ばれています。 STL に含まれるデータ構造や関数は、この講義でもしばしば取り上げます。

さようなら配列

STL では要素を自由に追加、削除できる配列のテンプレートである vector が 用意されています。 C 言語では領域外にアクセスした場合、動作は不定で暴走することもありまし た。 しかし、Vector で定義した配列は x.at(n) のようにアクセスするとエラーを 発生し、暴走を防ぐことができます。 また、アルゴリズムを使うと高速に検索などのアクセスができます。 以下は配列の合計と平均を求めるプログラムです。


#include <iostream>
#include <numeric> //accumulate に必要
#include <vector>
using std::cout;
using std::endl;
int main(void){
  int a[5]={ 3, 6, 2, 1, 4 };
  std::vector<int> v(a, a+5);
  int kosuu=v.size();
  int goukei=accumulate(v.begin(),v.end(),0);
  cout << "個数: " << kosuu << endl
       << "合計: " << goukei << endl
       << "平均: " << static_cast<double>(goukei)/kosuu << endl;
  return 0;
}

なお、ここで平均を取る際、キャストしています。 C++ にはキャスト演算子と言うものがあり、最低限の型チェックをします。 ここでは簡単に紹介するだけにとどめておきます。

static_cast
int と double など関連する型への変換
reinterpret_cast
int * から int など、関連性のない型への変換
dynamic_cast
実行時に型チェックをする変換
const_cast
const 限定子を取り除く変換

さようなら文字配列

C++ の STL では string テンプレートが用意されています。 これを用いて C 言語で示した文字列の処理を書くと次のようになります。

  1. 文字列の長さを求める
    
    #include <iostream>
    #include <string>
    int main(void){
      std::string x="This is a pen.";
      std::cout << x.size() << std::endl;
      return 0;
    }
    
  2. 文字列中で最初に特定の文字が出現する位置 (但しこの場合、文字ではなく文字列でも可能)
    
    #include <iostream>
    #include <string>
    using std::cout;
    using std::endl;
    using std::string;
    int main(void){
      string x="This is a pen.";
      string y="p";
      int z=x.find(y);
      if(z<=x.size()){
        cout << z << endl;
      }else{
        cout << "Not found." << endl;
      }
      return 0;
    }
    
  3. 文字列中に含まれる特定の文字の数
    
    #include <iostream>
    #include <string>
    #include <algorithm>
    using std::cout;
    using std::endl;
    using std::string;
    bool match(const char &c){
      return (c == 'p');
    }
    int main(void){
      string x="This is a pen.";
      string::iterator p;
      int count=0;
      for(p=find_if(x.begin(), x.end(), match);
          p!=x.end();
          p=find_if(p+1, x.end(), match)){
        count++;
      }
      cout << count << endl;
      return 0;
    }
    
  4. 文字列の結合
    
    #include <iostream>
    #include <string>
    using std::cout;
    using std::endl;
    using std::string;
    int main(void){
      string x="This is a pen.";
      string y="That is a book.";
      string z=x+y;
      cout << z << endl;
      return 0;
    }
    

3-3. Java 言語特有の話

Java は version 5 から Generics(総称) という C++ の template と同様の機能が追加されました。 Java 1.4 ではクラスライブラリとして与えられてた物が自然な拡張で使える ようになっています。 複数の情報を扱うためのツールは java.util カテゴリのクラスライブラリで 提供されています。 C++ の vector テンプレートに対応するのが java.util.ArrayList クラスに なります。 なぜ、 Java ではクラスによる提供が可能なのでしょうか? それは Java にはルートクラス java.lang.Object があり、すべてのオブジェ クトクラスはこの java.lang.Object のサブクラスになっているからです。 従って、すべてのオブジェクトは Object 型の変数に代入できます。 つまり、 java.util.ArrayList の中には java.lang.Object 型の変数が宣言 されており、値を入れる時はどんなオブジェクトでもそのまま入れることがで きます。 しかし、取り出す時は java.lang.Object 型で取り出すことになりますから、 入れたオブジェクトを取り出す時キャストする必要があります。

ところで int などの基本型はオブジェクトではないので、この java.util.ArrayList には入れることができません。 そのため、ラッパークラスと呼ばれる基本型をオブジェクトに変 換するクラスが用意されています。 例えば、 int に対して java.lang.Integer クラスが対応していて、 new Integer(3) などでオブジェクトに変換します。 得られたオブジェクトは intValue() メソッドで int 型に変換できます。

またさらに Java 5 ではデータの集まり全部をアクセスする foreach 構文が あります。

一方、Java には文字列型として java.lang.String(変更不可), java.lang.StringBuffer(変更可) があります。

Java 1.4 以前


import java.util.*;
class Main{
    public static void main(String[] arg){
	int[] a = {3, 5, 0, 2, 4 };
	List l = new ArrayList();
	for(int i=0; i< a.length;i++){
	    l.add(new Integer(a[i]));
	}
	int kosuu = l.size();
	int goukei=0;
	for(Iterator i = l.iterator(); i.hasNext();){
	    goukei += ((Integer) i.next()).intValue();
	}
	System.out.println("個数: "+kosuu);
	System.out.println("合計: "+goukei);
	System.out.println("平均: "+(double)goukei/kosuu);
    }
}

Java 5


import java.util.*;
class Main{
    public static void main(String[] arg){
	int[] a = {3, 5, 0, 2, 4 };
	List<Integer> l = new ArrayList<Integer>();
	for(int i=0; i< a.length;i++){
	    l.add(a[i]);
	}
	int kosuu = l.size();
	int goukei=0;
	for(Integer i : l){ // foreach 構文
	    goukei +=  i ;
	}
	System.out.println("個数: "+kosuu);
	System.out.println("合計: "+goukei);
	System.out.println("平均: "+(double)goukei/kosuu);
    }
}

文字列の処理

  1. 文字列の長さを求める
    
    class Test1 {
        public static void main(String[] args){
    	String x="This is a pen.";
    	System.out.println(x.length());
        }
    }
    
  2. 文字列中で最初に特定の文字が出現する位置 (但しこの場合、文字ではなく文字列でも可能)
    
    class Test2 {
        public static void main(String[] args){
    	String x="This is a pen.";
    	String y="p";
    	int k=x.indexOf(y);
    	if(k>=0){
    	    System.out.println(k);
    	}else{
    	    System.out.println("Not found.");
    	}
        }
    }
    
  3. 文字列中に含まれる特定の文字の数
    
    class Test3 {
        public static void main(String[] args){
    	String x="This is a pen.";
    	String y="i";
    	int k=0;
    	for(int i=x.indexOf(y); i>=0; i=x.indexOf(y,i+1)){
    	    k++;
    	}
    	System.out.println(k);
        }
    }
    
  4. 文字列の結合
    
    class Test4 {
        public static void main(String[] args){
    	String x="This is a pen.";
    	String y="That is a book.";
    	StringBuffer z;
    	String z;
    	z=x+y;
    	System.out.println(z);
        }
    }
    

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科