/*THK Analytics用解析タグ*/

【プログラミング言語C】第1章 「やさしい入門」 1.9
 文字配列

2022年8月17日

C言語における配列とは同じ型の連続して定義されているデータを指します。
詳細については5章でご説明しますが実はこれまでの学習でも皆様は配列を利用していました。
それが文字配列です。
例えば最初に学習した"Hello, world\n"も配列です。
これはchar型のデータが以下のように並んでいる配列です。

hello world

注目していただきたいのは、最後の「\0」。
文字列は必ず最後にこの値を入れる必要があります。これを見てコンピュータは文字の終端を理解します。
「\0」をNULL文字と言います。

本項では文字配列(一般的には文字列と呼びます)をアクセスするプログラムを作成しながら文字配列への理解を深めます。

プログラムの目的

今回のプログラムは少々複雑です。
複数の行で構成されているデータを読み込み一番長い行を出力します。
複数の行で構成されているデータとは、エディタで作成した文章などをイメージするとわかりやすいでしょう。
読者の皆様はまだファイルからのデータ入力を学んでいませんのでサンプルプログラムではキーボードからの入力を処理します。

ソースコードに入る前に、このプログラム基本骨格を紹介します。

while(行が存在する間はループする) {
if (今回の行が一番長いか?) {
この行を格納する
この行の長さを記憶する
}
}
一番長い行を出力

ソースコード

//=============================================================================
//=============================================================================
/*! @file function.cpp
@brief プログラミング言語C 1.9 文字配列
@author chiegraのプログラミング教室
@date 2020/12/10
@par 履歴
Copyright (C) 2020 chiegra. All rights reserved.
*/
//=============================================================================
//=============================================================================
#include <stdio.h>
//=============================================================================
/*!
@brief 行をコピーする関数
@return void
@note dst[]はsrc[]のデータが入る十分なサイズを保有するとする
@author yoimonologue
@date 2020/12/20
@par 履歴
*/
//=============================================================================
static void line_copy (char dst[] //!< [out]行のコピー先
,char src[] //!< [in]行のコピー元
) {
int i = 0;
while ((dst[i] = src[i]) != '\0') {
i++;
}
}
//=============================================================================
/*!
@brief キーボードからのデータを1行取得する関数
@return int 読み取った行の長さ
@note
@author yoimonologue
@date 2020/12/20
@par 履歴
*/
//=============================================================================
static int get_line (char s[] //!< [out]読み取った結果を格納する文字配列
,int s_len //!< [in]文字配列(s[])の配列長
)
{
//----------------------------------
//文字配列数のループ
//----------------------------------
int i;
for (i = 0; i < s_len-1; i++) { //iは文字配列へのデータ格納先を示す
int c = getchar(); //キーボードからのデータを受け付ける
if (c == EOF || c == '\n') { //行の終端かを判定している
//行の終端は'\n'、入力終了はEOF
//行の終端になった
break; //ループから脱出
}
}
return i;
}
//=============================================================================
/*!
@brief 複数行の中から最大長の行を見つける
@return int main関数の実行結果
@note なし
@author yoimonologue
@date 2020/12/10
@par 履歴
*/
//=============================================================================
int main()
{
const static int MAX_LINE = 1000; //getlineで取り込む1行バッファの長さ
char current_line[MAX_LINE]; //読み取った行データ
char longest_line[MAX_LINE]; //最大行のデータ
int len = 0; //getline関数で取得出来た文字
int max = 0; //最大文字数を記録する
//--------------------------------------------
//行データを取り込み最大文字数の行を調査するループ
// 行の先頭でエンターキーを押されたらループから抜ける
//--------------------------------------------
while (1) {
len = getline (line, MAX_LINE); //キーボードからのデータを1行取得
if (len <= 0) {
//すぐに改行されたので終了する
break;
}
if (len > max) { //記録している過去の最大文字数と今回取得した文字数を比較
//今回の方が文字数が多いのでデータを入れ替える
max = len;
line_copy (longest_line //!< [out]行のコピー先
,current_line //!< [in]行のコピー元
);
}
}
//--------------------------------------------
//最大文字数の行を表示する
//--------------------------------------------
if (max > 0) {
pirntf ("%s", longest_line);
}
return 0;
}

解説

いつもと同じように呼ばれる関数から記述します。
今回は以下のように並んでいます

・行コピー関数
・キーボードから1行を読み取る関数
・メイン関数

メイン関数

メイン関数から説明します。
const static int MAX_LINE = 1000;はK&Rでは#defineで表現していますね。
ループの先頭でgetline関数をコールして1行の入力を取得します。
K&Rではこのように1行で記述していますが、今回のように分けた方が見やすいと思います
while((len = getline (line, MAX_LINE)) > 0)

getline関数ではキーボードの入力を受けて、文字数と入力された文字を返します。
メイン関数では文字数が0ならループから脱出します。
文字があれば文字数をチェック最大文字数なら過去の文字列と入れ替えます。(line_copy関数)

ループを脱出したら最後に記録されている最大文字配列を表示して終了です。

キーボードからのデータを1行取得する関数

標準関数getchar関数を使ってキー入力を受け取ります。
改行入力で1行の終端と判定します。
最後に’\0’を追加しています。
最初の図で示したように文字列の終端を示します。
この終端が無いと次のline_copy関数やprintfでの表示が出来なくなります

line_copy

1行をコピーする単純な関数です。
'\0’までコピーしましょう。
K&R本ではwhile文に1行でコピーと判定を行っていますが、理解しやすいようにあえてバラバラに記述しています。
僕は行数を減らすよりこのように見やすく記述する方が重要だと考えています。

動作の確認

ブレークポイントの設定

ブレイクポイントをif (len > max) {の位置にセットして下さい。
下記の矢印ポイントで右クリックをします。

 

デバッグ実行

メニューよりデバッグ→デバッグの開始を選択して下さい。(F5でもOK)
デバッグが開始されると、キー入力を受け付けるコマンドプロンプトが出現します。
「aaa」と入力しEnterキーを押して下さい。

プログラムが正常に動作していれば、ブレークポイントでプログラムが一旦停止します。

 

変数のチェック

get_line関数は入力された文字数をカウントするプログラムです。
変数をチェックして期待した処理を行っているか確認しましょう。

ウォッチウインドウ

変数はウォッチウインドウで確認すると便利です。
ウォッチウインドウはデフォルトで表示されていると思いますが、出ていない場合は
メニューよりウインドウ→ウォッチ→ウォッチ1と選んで下さい。

 

ちなみに、デバッグ中では無いと、このメニューは出ませんのでご注意下さい。

ウオッチウインドウ。

ウォッチウィンドウが出たら、確認したい変数を登録します。
「項目をウォッチに追加する」のところをクリックしてlenと入力して下さい。
次の行にはmax。
以下のように表示されるはずです。
これでそれぞれの変数が確認出来ます
aaaは3文字ですので「3」と表示されれば、get_line関数の文字カウントは成功です。
maxは何も代入していないので初期値0のままです。

 

次に入力したキーのデータが正しく保存されていることを確認します。
同様にウォッチウィンドウにcurrent_lineを追加します。

 

入力した文字列「aaa」と表示されれば正解です。

ステップ送り

ステップ送りとはプログラムを1行づつすすめて、動作を確認するデバッグ手法です。
F10キーを押して下さい。(メニュー→デバッグ→ステップオーバーでも同じです)
1行先に進みます、さらにF10キーを押します。
line_copy行で止まった状態になります。

 

 

ここでウォッチウインドウを見て下さい。
先ほどmx=0,len=3だったのでif文は真となり
max = len;の行を通過しました。
現在はmaxに3が入っているはずです。

 

 

再度ステップ送りをしてline_copyを確認します。
ウォッチウインドウにlongest_lineを追加します。

 

 

line_copyが成功すればcurrent_lineがlongest_lineにコピーされます。

ここまで確認できたらF5(続行)します。

最終確認

またコマンドプロンプトの入力受付状態になるので「123456」と入力、Enterです。
再度ブレークポイントで止まるはずです。
今回の文字数は6ですので先ほどと同様、if文が真となり新たに123456がlonget_lineされますされます。
先ほど同様に確認したら今度はブレークポイントをif (max>0)にセットします。]

 

ブレークポイントのセットが終了したらF5で続行です。
コマンドプロンプトではすぐにEnterキーを押して下さい。

すると今回は先ほどのブレークポイントには止まらず、新しくセットしたブレークポイントに止まるはずです。
これは文字入力をしていないのでget_line関数の戻り値が0になりif (len <=0)が真でループを脱出したからです。
ここでステップ送りをしてreturn行まで送りコマンドプロンプトを表示すると以下のようになっています。

以上です、今回はここまで。

C言語教室総合目次に戻る

C言語教室

Posted by taka