C++言語解説:1-4.配列と文字列の基本
2002-05-19 |
[C++ Index Top] [Prev] [Next] |
[概要] C言語習得で「挫折経験」のある人は、ここから教育に参加することをお勧めしま す。C言語での文字列に対する考え方と配列の基本について学習します。 [構成]・1次元配列 * スタイル * 配列データのメモリ配置と容量 * 境界検証は行われない ・文字列 * 文字列の定義 * 文字列定数 * キーボードから文字を読む * ヌル文字を利用する ●1次元配列 ◆スタイル ・1次元配列(array)は関連する変数のリストです。一般形式は以下です。 ┌───────────────────────────────────┐ 型 変数名[サイズ]; (例)int i_sample[10]; └───────────────────────────────────┘ ・型は配列構成要素のデータ型を指します。サイズは要素の数を指定します。 ・配列の要素には添え字(index)を使ってアクセスします。配列の要素は「0」から始 まります。よって int i_sample[10]; と定義した場合、最初の要素は i_sample[0]; 最後の要素は i_sample[9]; となります。 [List1.1次元配列に慣れる(その1)] ┌───────────────────────────────────┐ #include <iostream> using namespace std; int main() { int i_sample[10]; //配列の定義 int i; for (i=0; i<10; i++){ i_sample[i]=i; //各要素へ数値代入 } for (i=0; i<10; i++){ cout << i_sample[i] << ' '; //各要素のデータ表示 } return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [実行結果] 0 1 2 3 4 5 6 7 8 9 └───────────────────────────────────┘ ◆配列データのメモリ配置と容量 ・配列は連続したメモリー位置で構成されます。例えば int i[7]; とした場合 ┌───────────────────────────────────┐ 4byte 4byte 4byte 4byte 4byte 4byte 4byte ←──→←──→←──→←──→←──→←──→←──→ ┌───┬───┬───┬───┬───┬───┬───┐ │ i[0] │ i[1] │ i[2] │ i[3] │ i[4] │ i[5] │ i[6] │ └───┴───┴───┴───┴───┴───┴───┘ └───────────────────────────────────┘ の連続したメモリ領域が確保されます。 ・各要素に割り当てられるメモリ領域の大きさは、プラットフォームにより異なりま すが、32bit OSのint型データには、一般的に32bit(4byte)が割り当てられます。 ・よってこの場合の消費メモリ合計バイト数は 合計バイト数 = 4byte × 7要素 = 28byte となります。 ・また配列の場合、変数のように「=」演算子を用いて代入する事はできません(*1)。 (例) int a[10], b[10]; : a = b; //これはエラーになる [List2.1次元配列に慣れる(その2)] ┌───────────────────────────────────┐ /* =========================================== 各要素に乱数を入れて、最大値/最小値を求める =========================================== */ #include <iostream> #include <cstdlib> //<stdlib.h> #include <ctime> //<time.h> using namespace std; int main() { int i; //インデックス int i_min; //最小値 int i_max; //最大値 int i_list[10]; randomize(); //void randmize():乱数種の初期化<stdlib.h>+<time.h> /* 乱数を代入する */ for (i=0; i<10; i++){ i_list[i] = rand(); //int rand():疑似乱数を返す<stdlib.h> } /* Max/Min値を初期化する */ i_min = RAND_MAX; //RAND_MAX:rand()発生乱数の最大値<stdlib.h> i_max = 0; /* Max/Min値を求める ついでに要素の数値も表示 */ for (i=0; i<10; i++){ if (i_min > i_list[i]) i_min = i_list[i]; if (i_max < i_list[i]) i_max = i_list[i]; cout << "要素" << i << '=' << i_list[i] << endl; } /* 結果を表示する */ cout << "最小値=" << i_min << endl; cout << "最大値=" << i_max; return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [実行結果] 要素0=28773 要素1=9386 要素2=238 要素3=27047 要素4=31373 要素5=3145 要素6=17359 要素7=32578 要素8=30204 要素9=6456 最小値=238 最大値=32578 └───────────────────────────────────┘ ◆境界検証は行われない ・C/C++は配列の境界検証を行いません。具体的には int a[10]; a[10] = 1; //間違い!! a[0]〜a[9]までしか確保されていない 等としても、コンパイラはエラーとして扱いません。 ・ちなみに上記のa[10]は、メモリ位置として、a[9]の次にくる連続領域です。 ┌───────────────────────────────────┐ 4byte 4byte 4byte 4byte ←──→←──→ ←──→←──→ ┌───┬───┬ ┬───┬───┐ │ a[0] │ a[1] │・・・│ a[9] │ a[10]│ └───┴───┴ ┴───┴───┘ │← ここは int a[10]; で確保 →│←??→│ └───────────────────────────────────┘ ・最初の定義で、a[0]〜a[9]についてはメモリ領域を確保するので、自由に読み書き できますが、a[10]の領域は他のデータとして使用されているかもしれないので、こ のようなプログラムを実行すると基本的に落ちます。 ・「何故コンパイラはチェックしないのか?」という根本的な疑問があるでしょう。 それは以下のような「コンパイラの設計思想」という言葉で片付けられています。 ┌───────────────────────────────────┐ C++の設計目標が、プロフェッショナルなプログラマに最高速の、可能な限り 最も効率的なコードの作成能力を提供することにあります。そのため、C++に はプログラムの実行速度を(時には著しく)低下させるエラー検証がほとんど含 まれていないのです。 〜ハーバート・シルト氏著「標準講座C++」より〜 └───────────────────────────────────┘ ・この「厳しさ」はC/C++の根底に流れているものであり「容易に修得できない」原因 ともなっていますが、そのおかげで「低レベル(マシンに近い)プログラミング」や 「多種プラットフォームへの対応」が可能になっているのです。C/C++を使うには、 この思想を了解しておく必要があります。 ●文字列 ◆文字列の定義 ・文字列は、末尾がヌル文字(null character)となる文字配列として定義されます。 ヌル文字とは、値(コード)が0の文字であり「\0」で表します。 ・例えば char str[11]; とした場合、10の文字(character)を保持できる、文字列(string)と扱うことができ ます。1文字少ないのは、ヌル文字の分です。 ◆文字列定数 ・文字列定数は、ダブルクォート「"」で囲まれた文字のリストです。 (例) "Hello" ・ダブルクォートで囲むとコンパイラは自動的にヌル文字を付加します。結果として 文字列 "Hello" は以下のようなメモリ配置となります。 ┌───────────────────────────────────┐ ┌─┬─┬─┬─┬─┬─┐ │ H│ e│ l│ l│ o│\0│ (1枠は 1byte:char型) └─┴─┴─┴─┴─┴─┘ └───────────────────────────────────┘ ・よって空文字列(null string)は、連続したダブルクォート「""」で表すことができ ます。このときの内部文字データはヌル文字のみです。 ◆キーボードから文字を読む ・キーボードから文字データを入力してみます。データを受け取る仕掛けとして、標 準入力cinを使ってみます。 [List3.cinで文字列データを受け取る] ┌───────────────────────────────────┐ #include <iostream> using namespace std; int main() { char s_str[80]; cout << "文字を入力して下さい : "; cin >> s_str; //s_strは配列名 cout << "入力された文字です : "; cout << s_str; //s_strは配列名 return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [実行結果] 文字を入力して下さい : this is a test 入力された文字です : this └───────────────────────────────────┘ ・この結果からわかるように、thisの部分しか取り込まれていません。この理由は、 cinで文字列を読み込む場合、最初のホワイトスペース(white space)で読みとりを 終了してしまうからです。ホワイトスペースに属する文字は以下の3種です。 半角スペース : ' ' タブ文字 : '\t' 改行文字 : '\n' ・これを回避する関数レベルのソリューションとしては、標準ライブラリ関数gets() を使います。gets()のプロトタイプ(*2)は以下です。 gets(配列名); 尚、gets()を使用するには<stdio.h>ヘッダが必要です。 [List4.gets()で文字列データを受け取る] ┌───────────────────────────────────┐ #include <iostream> #include <cstdio> //<stdio.h> using namespace std; int main() { char s_str[80]; cout << "文字を入力して下さい : "; gets(s_str); //s_strは配列名 cout << "入力された文字です : "; cout << s_str; //s_strは配列名 return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [実行結果] 文字を入力して下さい : this is a test 入力された文字です : this is a test └───────────────────────────────────┘ ・さて、先にも出た話ですが、cin / gets()とも、データの境界検証は行いません。 List3/4では入力データを受け取るための、79文字分の配列を用意しましたが、入力 データが79文字を超えても入ってしまいます。結果としてプログラムが落ちるので このことは頭に入れておいて下さい。 ◆ヌル文字を利用する ・文字列はヌル文字で終わります。よって「文字列の最初から最後まで」という処理 を行う場合には文字数を気にせず、「ヌル文字でなければ処理続行」という書き方 が可能になります。 ・これを利用して、入力された文字を大文字へ変換するプログラムを作ってみます。 大文字への変換は、標準ライブラリ関数toupper()を使用します。toupper()関数を 使うには<ctype.h>ヘッダが必要です。 [List5.ヌル文字を利用する] ┌───────────────────────────────────┐ #include <iostream> #include <cstring> // <string.h> #include <cctype> // <ctype.h> using namespace std; int main() { char s_str[80]; int i; cout << "文字列を入力して下さい : "; gets(s_str); for (i=0; s_str[i]; i++) { //ヌル文字でなければ処理続行 s_str[i] = toupper(s_str[i]); //大文字変換 } cout << "大文字へ変換しました : "; cout << s_str; return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [実行結果] 文字列を入力して下さい : this is a test 大文字へ変換しました : THIS IS A TEST └───────────────────────────────────┘ ・さて、大文字変換のforループブロック条件式で s_str[i] と記述しています。forループの条件式は true : 続行 false : 終了 です。そして、C++におけるtrueとfalseの定義は true : 0以外 false : 0 となっています。そしてヌル文字は「コード(数値)0の文字」です。つまりヌル文字 以外はtrue, ヌル文字はfalseとなり、ループ制御の論理判定にこれを利用していま す。このコーディングは多用されるので慣れるようにして下さい。 (*1)理由を理解するには次回説明するポインタの知識が必要です。配列名が意味するもの は何なのか。 (*2)gets()関数のプロトタイプを真面目に書くと char* gets(char* s_str); となります。引数はchar型配列の先頭アドレスです。戻り値には、読み込みが成功し た場合s_strの先頭アドレス、失敗した場合NULLが返ります。これにはポインタの概念 が入っているので、本文中では一般的な記述を避けました。 [Revision Table] |Revision |Date |Comments |----------|-----------|----------------------------------------------------- |1.00 |2002-05-19 |初版 |1.01 |2002-05-26 |[進む]リンク変更 |1.02 |2002-06-30 |語句修正 |1.03 |2002-08-15 |リンク追加 [end] |
Copyright(C) 2002 Altmo
|
[C++ Index Top] [Prev] [Next] |