- 締切済み
構造体についての問題なんですが
文字列入力関数と(StrInput)いうものをつくっているんですが、作成にあたっては以下の定数・構造体・関数を用います。 #define BUF_LEN (79) struct InputData { char Data; struct InputData *pPrev; struct InputData *pNext; }; struct InputBuffer { struct InputData *pHead; struct Inputdata *pTail; struct Inputdata idList[BUF_LEN]; }; char *StrInput(char *pdefStr, int nlen) { static char cStr[BUF_LEN + 1]; struct InputBuffer ibBuffer; : : : return (cStr); } というふうになるんですが、そこで使う関数を9つ作らなければいけないので作りました。 DataInit関数・・・構造体の初期化 charInp関数・・・1文字入力関数 ListSearch関数・・・リストの空き要素を検索 DispBuffer関数・・・InputBuffer再表示 Delete関数・・・DELキーによる削除 Insert関数・・・INSキーによる挿入 BackSpace関数・・・BSキーによる後退 LeftKey関数・・・カーソルの位置移動 RightKey関数・・・カーソルの位置移動 これを使って作りたいんですが、引数とかが与えられていないので、全く作り方がわかりません。どうか上から4つくらいの関数がどうなるのか、教えて頂けないでしょうか!お願いします。
- みんなの回答 (10)
- 専門家の回答
みんなの回答
- Oh-Orange
- ベストアンサー率63% (854/1345)
★確認。 >ここがほんとに1番悩んでいるところです。 ↑ そうだね。 ・それでこの関数の動作はどんな感じですか? 関数名から推測すると CharInp() は1文字を双方向リスト struct InputData の Data に セットするんですよね。と思っていたのが BUF_LEN 個分の文字を入力するように貼り付けて あるソースになっています。これでいいのですか?動作が? ・それから >if (0 != kbhit()) { > key = getch(); > putchar(key); >} ↑ これはなぜ必要なのですか? また、文字以外のキー(INS、DEL、BS、←、→)が押されたらどうやって Insert、Delete、 BackSpace、LeftKey、RightKey 関数を呼ぶのですか? この辺の動作仕様を詳しく押して下さい。 そうしないと正しいのか、間違っているかが私には分かりません。 ・以上。補足要求します。→CharInp() 関数の動作仕様は?何?
- Oh-Orange
- ベストアンサー率63% (854/1345)
★最初に{ }ブロックがずれていますよ。 ・あとバグが多いです。 >if (pStringData -> pHead == NULL) { > return pStringData -> idList[0]; >} ↑ 『return pStringData -> idList;』 としないと戻り値の型が struct InputData * と合いませんよ。 ・while 文の中は完全に間違っています。 ListSearch() 関数は idList[] 配列から空いている領域を探し出してそのポインタを 戻り値で返す仕様ですよね。ならば、idList[] の先頭から順番に空いている要素を 検索して空きが見つかったらその配列のポインタを返します。また、1つも空きがない 場合は NULL を返すようにします。 ・あと空きかどうかは struct InputData 構造体の Data メンバが 0 なら空きと決めておきます。 このことから DataInit() 関数もちょっと編集すべきです。 次のように修正して下さい。 // defStr を idList[] 配列にセット idList = pStringBuffer->idList; for ( i = 0 ; i < nLen ; i++ ){ idList->Data = defStr[ i ]; idList->pPrev = ここを記述; idList->pNext = ここを記述; idList++; } // (↓)追加部分 for ( ; i < nLen ; i++ ){ idList->Data = 0; ←ここをゼロにすると『空き』状態を意味する idList->pPrev = NULL; ←ここはNULLを idList->pNext = NULL; ←ここも同じ idList++; } // 先頭/末尾をセット (省略) ・下に雛形サンプルを載せます。 雛形サンプル: // リストの空き要素を検索 struct InputData *ListSearch( struct InputBuffer *pStringBuffer ) { int i; for ( i = 0 ; i < BUF_LEN ; i++ ){ if ( ここで空き状態をチェック ){ return 空き状態の idList[] ポインタを返す; } } return NULL; ←空きが全く無かったので NULL を返す。 } その他: ・『ここで空き状態をチェック』と『空き状態の idList[] ポインタを返す』を記述して下さい。 空き状態のチェックは Data メンバが 0 なら『空き状態』と判定すればよい。 ・以上。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アドバイス >これじゃ全然だめでしょうか?const char defstrをどう使うかが全然わかりません。 >どうか、ご指導をお願いします! ↑ そうだね。回答 No.6 の補足で >DataInit() 関数は文字列とカーソル位置を双方向リストの struct InputBuffer 構造体に >セットするための関数なのではないでしょうか。 >その通りです。 ↑ その通りと言っていますので DataInit() 関数で defStr 文字列を struct InputBuffer の 構造体にセットしないと駄目です。貼り付けてくれたソースってのはゼロで初期化していますよ。 ・引数から defStr[] を受け取りこの文字列を1文字ずつ struct InputData 構造体の Data に セットして、pPrev、pNext を双方向リストとしてどんどんと繋げていきます。 そして、繋げた双方向リストの先頭を struct InputBuffer 構造体の pHead、末尾を pTail に セットして初期化関数の DataInit() 関数は処理を終えます。 ・下に雛形サンプルを載せます。双方向リストで繋げる部分を記述して下さい。 雛形サンプル: // 構造体の初期化(セット) void DataInit( struct InputBuffer *pStringBuffer, const char defStr[], int nLen ) { struct InputData *idList; int i; // defStr を idList[] 配列にセット idList = pStringBuffer->idList; for ( i = 0 ; i < nLen ; i++ ){ idList->Data = defStr[ i ]; idList->pPrev = ここを記述; idList->pNext = ここを記述; idList++; } // 先頭/末尾をセット pStringBuffer->pHead = ここも記述; pStringBuffer->pTail = ここも記述; // 先頭の pPrev、末尾の pNext に NULL をセット pStringBuffer->pHead->pPrev = NULL; pStringBuffer->pTail->pNext = NULL; } その他: ・struct InputBuffer 構造体の idList[0] ~ idList[nLen - 1] に1文字ずつ格納しているので pPrev、pNext は idList[] 配列のポインタを1つ手前、1つ後ろという感じでセットすれば良いです。 ただし、先頭の pPrev には NULL、末尾の pNext には NULL をセットします。これは双方向リストの お決まりの構造ですよね。これは for() 文の後にセットすれば楽です。また、このセットは pHead、pTail を正しくセットした後に処理すればさらに楽です。 ・あと pHead、pTail は idList[] 配列の先頭と idList[nLen - 1] が末尾なのでそれをセットすれば いいことになります。 ・以上。それでは頑張って下さい。
補足
ほんとうに丁寧に教えてもらって助かります!ありがとうございます。 で、次のListsearchなんですが、 struct InputData *ListSearch(struct InputBuffer *pStringData) { if (pStringData -> pHead == NULL) { return pStringData -> idList[0]; } while (1) { if (pStringData -> pHead != NULL) { pStringData -> pHead = pStringData -> pHead -> pNext; else { break; } return pStringData -> phead; } } というようにしたんですが、どうでしょうか?ご指導お願いします!
- Oh-Orange
- ベストアンサー率63% (854/1345)
★大分全体像が見えてきました。 >この条件でまた、解答をよろしくお願いします! ↑ まずは DataInit() 関数から順番に見せて下さい。 1関数ずつアドバイスをします。 ・それでは DataInit() 関数のソースを補足に貼り付けて下さい。 ちなみに関数の引数と戻り値は void DataInit( struct InputBuffer *pStringBuffer, const char defStr[], int nlen ); ↑ こんな感じになりますよね。 この関数がうまく作れないと他の関数も難しくなりますのでさらっと作成して貼り付けて下さい。 ・待っています。
補足
すいません。今までこれでいいと思って作っていたのがこれです。 void DataInit(struct InputBuffer *pStringData, int nLen) { int i; StringData.pHead = NULL; StringData.pTail = NULL; for (i = 0; i <= nLen; i++) { StringData.idList[i].Data = 0 } } これじゃ全然だめでしょうか?const char defstrをどう使うかが全然わかりません。どうか、ご指導をお願いします!
- Oh-Orange
- ベストアンサー率63% (854/1345)
★いろいろとアドバイス >key = (key << 8) | getch(); ↑ これはカーソルキー、INSキー、DELキーの場合のキーコードを他の英数字キーと区別する ための1行です。 ・まず最初に getch() 関数では [↑]キーが押されると 0xE0、0x48 [↓]キーが押されると 0xE0、0x50 [←]キーが押されると 0xE0、0x4B [→]キーが押されると 0xE0、0x4D [INS]キーが押されると 0xE0、0x52 [DEL]キーが押されると 0xE0、0x53 というように2つのコードが返されます。 このことから 0xE0 のときに『key = (key << 8) | getch();』を行うと [↑]キーを 0xE048 [↓]キーを 0xE050 [←]キーを 0xE04B [→]キーを 0xE04D [INS]キーを 0xE052 [DEL]キーを 0xE053 というコードで他の英数字の1バイト特別出来るようになるのです。 ・また、 >if ( (key < 0x100) && isprint(key) ){ ↑ これは key が1バイトの文字コードか判定するためです。 普通ならば if ( isprint(key) ) で良いのですが、key は先ほど 0xE048~0xE053 の2バイト コードも含まれることから (key < 0x100) という判定も必要になってくるのです。 必要な理由は isprint() 関数が1バイト分しか判定しないからです。 ・つまり、INS キーの1バイト部分は 0x52 です。 これは 'R' 文字と同じコードです。 isprint(key) だけだとキーボードから 'R' キーの入力と INS キーかを判定できなくなります。 このため (key < 0x100) という条件を付け加えているのです。 >あと、文字の上書きはこれでできるんでしょうか? >できているのであれば、どこの処理の部分でできているんでしょうか? ↑ 文字の上書きは charInp() 関数内で処理することになります。 また、挿入/上書きモードの切り替えは Insert() 関数に任せています。 回答者 No.3 さんの補足に貼り付けたソースには上書きへの対応がありません。 CharInp() 関数で上書入力と挿入入力を分けて記述する必要があります。 上書き入力の機能を追加して下さい。 ・私が提示したサンプル・ソースは StrInput() 関数です。 この関数は文字列の1文字入力、挿入/上書き切り替え、削除、カーソル左移動、カーソル右移動 BS削除などの全体を管理する上位関数です。細かい処理はそれぞれのサブ関数で記述します。 ・最初の質問では関数の引数と戻り値をどうすればよいかでしたよね。 回答 No.5 で StrInput() 関数に合わせた関数の引数と戻り値を提示していますのでこれを 元に Insert()、Delete()、BackSpace()、LeftKey()、RightKey() 関数を作って下さい。 ・簡単に解説すると // DELキーによる削除 >struct InputData *Delete( struct InputData *pCurrentData ); ↑ pCurrentData->pPrev のデータと pCurrentData->pNext のデータを双方向リストで繋げれば [DEL]キーによる削除が実現できます。また、戻り値は pCurrentData->pNext のポインタに なります。これは[DEL]キーで削除詰めされるとカーソル位置の次の文字がカレント位置に なるからです。また、データの最後を削除した場合には pCurrentData->pPrev のポインタを 返すことになります。分かりますか。この仕組み? // INSキーによる挿入/上書きモードの切り替え >struct InputData *Insert( struct InputData *pCurrentData ); ↑ この関数は挿入モードと上書きモードを切り替える関数ではないのですか? 違うのならどこで挿入モードと上書きモードの切り替えを行わせるのか補足して下さい。 もし、切り替えモードならばグローバル変数などで現在のモードを保存するようにしてそれを この関数で切り替えます。また、1文字入力の charInp() 関数で現在のモードを参照して処理を 挿入部、上書部と分けます。こうしないと挿入/上書きの両モードに対応できないと思います。 // カーソル位置の左移動 >struct InputData *LeftKey( struct InputData *pCurrentData ); ↑ この関数はカーソル位置の左移動ですよね。 それならば pCurrentData->pPrev のポインタを戻り値で返すようにすれば良いです。 ただし、pCurrentData のポインタが先頭の場合(pCurrentData->pPrev=NULL)は pCurrentData の ポインタを返せば良いです。 // カーソル位置の右移動 >struct InputData *RightKey( struct InputData *pCurrentData ); ↑ この関数はカーソル位置の右移動ですよね。 それならば pCurrentData->pNext のポインタを戻り値で返すようにすれば良いです。 ただし、pCurrentData のポインタが最後の場合(pCurrentData->pNext=NULL)は pCurrentData の ポインタを返せば良いです。 // BSキーによる後退削除 >struct InputData *BackSpace( struct InputData *pCurrentData ); ↑ この関数はカーソル位置の1つ前の文字を削除しますので考え方は左移動して削除する関数を内部で 呼べば良いです。でもこの関数にその処理を記述しても構いません。この辺は自由です。 その他: ・解答例として StrInput() 関数をサンプルとして載せましたが、これを元にそれぞれの関数群を 作成すると DataInit()、charInp() の動作と引数が少し代わってきます。特に回答者 No.3 での 補足ソースには CharInp() 関数内で1行の文字列入力を行うようになっています。でも、質問は >文字列入力関数と(StrInput)いうものをつくっているんですが、作成にあたっては以下の定数・構造体・関数を用います。 ↑ となっていますよ。この StrInput() 関数で1行の文字列入力やカーソル移動、訂正キーを処理する 仕様ではないのですか?と思いました。 この考えを元に前回の No.5 で StrInput() 関数を解答例としてサンプルを載せています。 よって charInp() 関数は挿入/上書きモードでの1文字だけの入力処理を担当する関数だと思います。 また、DataInit() 関数は文字列とカーソル位置を双方向リストの struct InputBuffer 構造体に セットするための関数なのではないでしょうか。 最初は struct InputBuffer 構造体をすべてゼロにする初期化関数だと思いましたが違うような気がした。 ・こんな感じで各関数の動作をもう一度詳しく補足してくれないと仕様の食い違いで回答が正しく出来なく なる可能性が出てきます。例えば Insert() 関数は『INSキーによる挿入』となっていますが、本当に この関数で挿入する部分を記述するのでしょうか?この関数は[INS]キーで挿入/上書きモードの切り替えを 担当する関数ではないのかな。と思うのですが…。どうなんですかね。 ・以上。いろいろと詳しい補足が欲しいです。
補足
>よって charInp() 関数は挿入/上書きモードでの1文字だけの入力処理を担当する関数だと思います。 また、DataInit() 関数は文字列とカーソル位置を双方向リストの struct InputBuffer 構造体に セットするための関数なのではないでしょうか。 その通りです。また、INSキーは任意の位置に空白の挿入をするという機能を持つということです。文字の上書きは、文字の挿入にはならないので注意!ということです。あと、CharInput関数の最後が、 return DispBuffer となっていましたが、前に書いたとおり return (cStr) とならなければ、いけないそうです。だから、DispBuffer関数は、 DEL,INS,BS関数の中で使うそうです。また、ListSearch関数は、 CharInp、Insert関数の中で使うそうです。この条件でまた、解答をよろしくお願いします!
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アドバイス >以前に載せていた作成の手引きでいうと(4)のところまででいいんですが、CharInp関数、DataInit、ListSearchを >使って作るんですが、どうしたらいいでしょうか?このままで単に合わせればできるのでしょうか? ↑ 出来ません。 ・まずは作成する9つの関数の機能をはっきりと整理して下さい。 (1)DataInit関数(構造体の初期化) (2)charInp関数(1文字入力関数) (3)ListSearch関数(リストの空き要素を検索) (4)DispBuffer関数(InputBuffer再表示) (5)Delete関数(DELキーによる削除) (6)Insert関数(INSキーによる挿入) (7)BackSpace関数(BSキーによる後退) (8)LeftKey関数(カーソルの位置移動) (9)RightKey関数(カーソルの位置移動) ↑ いろいろ読んでみて charInp() 関数が現在のキャレット位置に1文字だけ入力する仕様のようですね。 そして StrInput() 関数で文字列の入力をすべて処理する上位関数ですよね。あっている? そうなると StrInput() 関数内で ListSearch()、charInp()、Delete()、Insert()、BackSpace()、 LeftKey()、RightKey() の下位関数(サブ関数)を呼ぶことになると思います。 よって(5)~(9)のサブ関数も作成しないと作れません。 ・これにより関数の引数、戻り値は次のようにすると良いと思います。 struct InputData *DataInit( struct InputBuffer *pStringBuffer, const char defStr[], int nlen ); struct InputData *ListSearch( struct InputBuffer *pStringBuffer ); struct InputData *charInp( struct InputData *pCurrentData, int key ); struct InputData *Delete( struct InputData *pCurrentData ); struct InputData *Insert( struct InputData *pCurrentData ); struct InputData *LeftKey( struct InputData *pCurrentData ); struct InputData *RightKey( struct InputData *pCurrentData ); struct InputData *BackSpace( struct InputData *pCurrentData ); char *DispBuffer( struct InputBuffer *pStringBuffer, char buff[] ); ↑ この関数群を先に作成しないと StrInput() 関数はできません。 でも、既に作成していると仮定して下のような感じで StrInput() 関数を作ってみました。 上のプロトタイプ宣言は下で紹介する関数に合わせた引数、戻り値になっています。 サンプル: // break付きのキーワード(便利だよ) #define CASE break;case #define DEFAULT break;default // 特殊キーの記号定数 #define CODE_BS (0x08) #define CODE_RET (0x0D) #define CODE_LEFT (0xE04B) #define CODE_RIGHT (0xE04D) #define CODE_INS (0xE052) #define CODE_DEL (0xE053) // 文字列入力関数(解答例) char *StrInput( char pdefStr[], int nlen ) // 初期文字列,初期キャレット位置 { static char cStr[ BUF_LEN + 1 ]; struct InputBuffer ibBuffer; struct InputData *idCurrent; int key; // 構造体の初期化(セット) idCurrent = DataInit( &ibBuffer, pdefStr, nlen ); // 文字列入力のループ while ( (key = getch()) != CODE_RET ){ // リターンキーで抜ける // 特殊キー対策 if ( key == 0xE0 ){ key = (key << 8) | getch(); } // BS,←,→,挿入,削除,1文字入力の分岐 switch ( key ){ CASE CODE_BS: idCurrent = BackSpace( idCurrent ); CASE CODE_LEFT: idCurrent = LeftKey( idCurrent ); CASE CODE_RIGHT: idCurrent = RightKey( idCurrent ); CASE CODE_INS: idCurrent = Insert( idCurrent ); CASE CODE_DEL: idCurrent = Delete( idCurrent ); DEFAULT: if ( (key < 0x100) && isprint(key) ){ idCurrent = charInp( idCurrent, key ); // 1文字入力 } } } return DispBuffer( &ibBuffer, cStr ); } 最後に: ・解答例を作っていて気づきました。 0xE0,0x52,0x53 の 16進数は特殊キーのコードですね。 上のサンプルで記号定数 CODE_XXXX の名前で分かりやすくしています。 直接 16 進数を記述するよりも記号定数などを利用するとソースが読みやすくなります。 これからは #define で記号定数を定義して活用しましょう。 ・以上。
お礼
本当に何度もすいません。 key = (key << 8) | getch();と if ( (key < 0x100) && isprint(key) ){ はどういう処理なんでしょうか?あと、文字の上書きはこれでできるんでしょうか?できているのであれば、どこの処理の部分でできているんでしょうか?どうか、よろしくお願いします!
- Oh-Orange
- ベストアンサー率63% (854/1345)
★回答者 No.1 です。 ・DispBuffer() 関数の if ブロックに break を使っていますが使い方が間違っていますよ。 つまり、 if ( 式1 ){ 処理1 } else if ( 式2 ){ 処理2 } else if ( 式3 ){ 処理3 } buff[ cnt ] = '\0'; return cnt; ↑ と記述します。break ではなくて else if で記述します。 ・break は繰り返しブロックや switch 文でしか利用できません。 ・あとカンマ演算を使っていますが意図的に使っているのですか? >if((pData->Data=0xe0,0x52) && (i>0)){ ↑ 『0xe0,0x52』の部分や >putchar(0xe0,0x52); ↑ 『0xe0,0x52』の部分。 カンマ演算を使うと最後の値が最終値として評価されます。 つまり、 putchar(0xe0,0x52); は putchar(0x52); と同じになります。 この働きをわざと記述しているのですか?あまりお勧めできませんが…。なぜ? ・それから DispBuffer() 関数で i 変数が登場しますがグローバル変数を参照しているのですか? グローバル変数 i でDispBuffer() 関数を制御しているのですか。 こちらもあまりお勧めできません。制御したいのならもっと分かりやすい変数名や DispBuffer() 関数の引数として渡して下さい。 ・その他 0x00、0x08、0x0d、0x20、0x52、0x53 などの 16 進定数がたくさん登場しますが、 もっと分かりやすく記述したほうが良いと思います。つまり、 0x00⇒\0 0x0d⇒\n 0x08⇒\b 0x20⇒SPC 0x52⇒'R' 0x53⇒'S' となります。 ・ただし、SPC は自分で定義した記号定数です。 #define SPC (0x20) ↑ ヘッダ部に記述しておく。 ・以上。今後の参考に。
補足
指摘されたところは全てわざとではなく、自分が全然知らないから単に間違っているだけです。お恥ずかしいです。ところで、明日までに何も機能は付けず、単に文字入力ができるところまで作らないといけないんですが、以前に載せていた作成の手引きでいうと(4)のところまででいいんですが、CharInp関数、DataInit、ListSearchを使って作るんですが、どうしたらいいでしょうか?このままで単に合わせればできるのでしょうか?よかったら、そこまででいいので教えていただけないでしょうか?お願いします!
- fatbowler
- ベストアンサー率48% (26/54)
ANo.2の補足に対する回答です。 最初からつまづくのであれば、答を教えてもらっても写すだけになってしまいます。 ヒントを書きますので、是非ご自分でチャレンジして下さい。 どうしても困ったら、「ここまで作ったけどここが分からない」という形で質問するのが礼儀です。 (1) ANo.2では文字列を管理する構造体を InputBuffer Sample; としましたが、そのまま使えるよう InputBuffer StringData; という名前にしましょう。これがmain関数内に宣言されているとします。 DataInit()は、この構造体を初期化するものです。 main関数から、 DataInit(&StringData); という形でcallされます。 引数で渡された構造体の各メンバを初期化して下さい。 (2) 「メモリ上に用意したstruct InputData配列の領域」とは StringData.idList[BUF_LEN] のことです。 このメモリ領域を利用するのですが、実際のアクセスは StringData.idList[2] のようには行わず、 StringData.pHead->pNext->pNext とします。 あるいは、 struct InputData *pCurrentData; pCurrentData = StringData.pHead->pNext; としておいて、 pCurrentData->pNext としてアクセスします。 関数は、具体的には、仮引数を InputBuffer *pStringBuffer; とすると、 ・初めて文字を入力する時、つまり pStringBuffer->pHead == NULL の場合は、 pStringBuffer->idList[0] を返します。 ・既に何文字か入力されている場合、つまり pStringBuffer->pHead != NULL の場合は、 pStringBuffer->pHead->pNext pStringBuffer->pHead->pNext->pNext pStringBuffer->pHead->pNext->pNext->pNext の順にスキャンしていって、初めてNULLになるものが、次に使用する領域 なので、そのポインタを返します。 (3) 引数は、InputBuffer構造体のポインタです。 どうやらmain関数の中でwhileループを作って文字入力をさせ、その文字を 引数で渡されてcallされるようです。 引数でもらった構造体のメンバ Data に代入します。 (4) (3)のwhileループが終了した時に呼ばれます。 pCurrentDataのポインタと、main関数に別途定義した char StringDataBuffer[BUF_LEN]; のポインタを渡されて、 (引数)->pHead->Data (引数)->pHead->pNext->Data (引数)->pHead->pNext->pNext->Data (引数)->pHead->pNext->pNext->pNext->Data の順にStringDataBufferに代入していきます。 最後にNULLを代入します。 こんなところで頑張ってみて下さい。
お礼
すいません、そのとおりですね。こんなに丁寧に教えてもらっているので、自分で一生懸命がんばってみます。ほんとうにありがとうございます。
補足
struct InputBuffer *ListSearch(struct InputBuffer *pStringBuffer) { struct InputData *pData; int i; if (pStringBuffer->pHead==NULL){ return &pStringbuffer->idList[0]; } if (pStringBuffer->pHead!=NULL){ pData->pPrev=pStringBuffer->pTail; pData->pNext=NULL; pStringBuffer->pTail=&pStringBuffer->idList[i]; return &pStringBuffer->idList[i+1]; } } char CharInp(struct InputBuffer *pStringBuffer, int len) { int key=0; int i=0; struct InputData *pData; char buff[b]; for (i=0;i<=BUF_LEN;i++){ key=getch(); chk=isprint(key); if(0!=chk && i<len){ ListSearch(&ibBuffer); pStringBuffer->idList[i]=(struct InputBuffer *)key; putchar(key); } if(0!=kbhit()){ key=getch(); putchar(key); } if(pData->Data==0x0d){ pStringBuffer->idList[i]->Data=0x00; pData->pPrev=pStringBuffer->pTail; pData->pNext=NULL; pStringBuffer->pTail=pData->pPrev; break; } } b=0; for(pStringBuffer->idList[i]->Data=pStringBuffer->idList[0]->Data;i<=BUF_LEN;i++){ buff[b]=pStringBuffer->idList[i]->Data; if(pStringBuffer->idList[i]->Data==0x00){ buff[b]='\0'; break; } putchar(buff[b]); b++; if(b==BUF_LEN+1){ buff[b]='\0'; break; } return (char)buff; } } int DispBuffer(char *buff, int cnt) { struct InputData *pData; if((pData->Data=0xe0,0x53)&&(i>0)){ putchar(0xe0,0x53); cnt--; break; } if((pData->Data=0xe0,0x52)&&(i>0)){ putchar(0xe0,0x52); putchar(0x20); cnt--; break; } if((pData->Data=0x08)&&(i>0)){ putchar(0x08); putchar(0x20); putchar(0x08); cnt--; break; } buff[cnt]='\0'; return cnt; } というふうに一番自信がない3つの関数をがんばって作りました。恐らく全くできていないと思いますが、解答例など、できればよろしくお願いします!
- fatbowler
- ベストアンサー率48% (26/54)
一瞬、リストが2重構造になっているのかと思いましたが、違いますね。 機能は単純に、1文字ずつ入力させたり、削除したり、カーソルを移動したりして 文字列を1つ入力するだけのものです。 恐らくラインエディタの入力部分で、次の段階で「これを使ってラインエディタを作る」 となるんじゃないでしょうか。 構造体InputDataは、文字列の構成要素である「文字」のデータの双方向リストです。 構造体InputBufferは、対象文字列の構造体で、こちらはリストではありません。 構造体InputDataを管理するためのものです。 InputBufferは静的に、あるいは動的にメモリを確保しますが、InputDataはあくまで InputBufferの要素であり、別にメモリを割り当てられることはありません。 具体例でご説明します。 InputBuffer Sample; と静的にメモリ確保されたバッファに "abcde" という文字列が入っているとすると、 Sample.pHead は 'a' を保持した構造体InputDataのポインタを、 Sample.pTail は 'e' を保持した構造体InputDataのポインタを指します。 もう少し具体的には、 Sample.pHead->Data == 'a' Sample.pHead->pPrev == NULL Sample.pHead->pNext == ( 'b' を保持した構造体のポインタ) Sample.pTail->Data == 'e' Sample.pTail->pPrev == ( 'd' を保持した構造体のポインタ) Sample.pTail->pNext == NULL となります。 また、これが初期化直後に入力されたもので、途中で削除や挿入をしなければ、下記のようになります。 Sample.idList[0] ==( 'a' を保持した構造体の実体) Sample.idList[1] ==( 'b' を保持した構造体の実体) Sample.idList[2] ==( 'c' を保持した構造体の実体) まず、上記の内容を理解して下さい。 次に、各関数で何をするべきなのか、細かく噛み砕いて日本語で文章にしてみて下さい。 そうすると、どうコーディングすればいいか見えてくると思います。 結構高いレベルの問題です。がんばって下さい。
補足
返事が遅れてすみません。丁寧な解説をありがとうございます。しかしずっと考えていたんですけれども最初からつまずいてしまいます。どうか、こういうヒントがおしえられましたので、最初のほうだけでも具体的に関数を作ってくれませんでしょうか?お願いします。 (1)構造体の初期化を行う関数を作成する。 pHead,pTail,InputData構造体の初期化をおこないます。データが入力されていない時のpHead,pTailはNULLを指しておきます。 (2)リストの空き要素を検索する関数えお作成する。 キーボードから入力した文字をidListのどこに格納するのかを決めるため。見つかった空き要素は添え字ではなく、ポインタで返却する。通常は、malloc関数などで領域を確保しますが、今回はメモリ上に用意したstruct InputData配列の領域を利用します。 (3)1文字入力関数を作成する(通常入力処理) 入力された文字をInputBuffer構造体へ登録します。このとき、pHead,pTail,pPrev,pNextを設定すること。 (4)Enterキーによる入力完了の処理を作成する。 InputBuffer構造体から文字列(char配列)へ変換する。この段階で、通常入力できるぐらいに完成しているはずです。 (5)カーソル移動処理関数を作成する。 ←→キーによるカーソル位置移動処理を作成します。 (6)カーソルの指しているデータをポインタで管理する。 DELキー、INSキー、BSキー処理で使用するため。 (7)DELキー、INSキー、BSキー処理を行う関数を作成する。 (8)InputBuffer再表示関数を作成する。 DEL、INS、BS処理のときの、画面上の不具合を直します。 (9)1文字入力関数を使って初期文字列を代入処理を作成する。 というような順番で作っていけば作れるそうなんですが、(4)までの普通の文字入力関数ができるくらいまで、教えていただけないでしょうか?すいませんがおねがいします。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アドバイス ・最初に双方向リストのデータを操作する関数群ですね。 多分、エディタの管理構造のような関数群ですね。あたっているかな? ネットで『双方向リストとは』で検索するとたくさんのサンプルが見つかりますので それを元に引数を自分で決めてみて下さい。 ・ちなみに上から4つは void DataInit( struct InputBuffer *top ); void charInp( struct InputBuffer *list ); struct InputBuffer *ListSearch( struct InputBuffer *list ); void DispBuffer( struct InputBuffer *top ); ↑ こんな感じでどうでしょうか。 『top』というのはリスト構造の最初のポインタを渡す。 『list』というのは任意位置のリスト構造へのポインタを渡す。 を意味しいます。 ・あと残りの関数は1行の文字列を操作する関数群です。多分ね。 Delete関数・・・DELキーによる削除 Insert関数・・・INSキーによる挿入 BackSpace関数・・・BSキーによる後退 LeftKey関数・・・カーソルの位置移動 RightKey関数・・・カーソルの位置移動 ↑ この関数は『struct InputData』の双方向リストを操作するタイプです。 最後に: ・多分、エディタのような行、文字列を管理する構造なのでしょうね。 そうすると 『struct InputBuffer』の双方向リストで行を管理しています。 『struct InputData』の双方向リストは文字列を管理しています。 となります。 ・以上。参考に。
お礼
本当にありがとうございます!かなり参考になります。本当はもう少し聞きたいことはあるのですが、明日一日がんばってみます。それからまた質問をするかもしれませんが、そのときはどうかよろしくお願いします。
補足
ありがとうございます!ほんとに助かります。詳しく説明していただいて。それで、次の関数なんですが、ここがほんとに1番悩んでいるところです。 struct InputData *CharInp(struct InputBuffer *pBuffer, int key) { struct InputData *idList; int i; for (i = 0; i <= BUF_LEN; i++) { key = getch(); if ((0 != isprint(key)) && ListSearch(&pBuffer)) { pBuffer -> idList[i] = (struct InputData *)key; pytchar(key); } if (0 != kbhit()) { key = getch(); putchar(key); } pBuffer -> pHead = idList[0]; pBuffer -> pTail = idList[i]; pBuffer -> pHead -> pPrev = idList[0]; pBuffer -> pTail -> pNext = 0; } return idList; } としました。間違いだらけだとおもいますが、どうぞよろしくお願いします!