トップ->C言語入門

あなたは

人目のC(C++)言語入門受講生です。

C言語入門内検索

目次
C言語入門〜トップ
C++言語入門〜トップ
0. はじめに

1. 基本的な決まり
   1. 書式
   2. 予約語
   3. 構成
   4. データの形と宣言
   5. 定数と変数
   6. 簡単な演算

2. 入出力
   1. printf
   2. scanf
   3. 練習問題1
   4. 1文字入出力
   5. エスケープシーケンス

3. 制御文
   1. 順次構造
   2. 単一分岐
   3. 多重分岐
   4. ケース構造
   5. 反復構造(while)
   6. 反復構造(do)
   7. 反復構造(for)
   8. 補助制御文
   9. 練習問題
   10.練習問題

4. 配列
   1. 配列とは
   2. 配列の宣言と初期化
   3. 配列の使用法
   4. 練習問題
   5. 文字列
   6. 2次配列と多次元配列
   7. 練習問題

5. 演算子
   1. 演算子の種類
   2. インクリメント演算子とデクリメント演算子
   3. 複合代入演算子
   4. ビット演算子
   5. シフト演算子
   6. キャスト演算子
   7. 順次演算子
   8. 条件演算子(三項演算子)
   9. sizeof演算子
   10.演算子の優先順位
   11.式と値
   12.条件式と値
   13.練習問題

6. ポインタ
   1. ポインタとは
   2. 配列とポインタ
   3. 文字列とポインタ
   4. ポインタのメリット

7. 関数
   1. 関数の作り方
   2. プロトタイプ宣言
   3. 配列とポインタ
   4. 値渡しとアドレス渡し
   5. main関数の引数
   6. 練習問題
   7. 標準関数
   8. 再帰関数

8. プリプロセッサ
   1. プリプロセッサとは
   2. #define, #undef
   3. #include
   4. #if
   5. #error、#warning
   6. マクロ
   7. 定義済みマクロ

9. 変数の有効範囲
   1. 変数の種類
   2. グローバル変数の有効範囲
   3. オート変数の有効範囲
   4. スタティック変数の有効範囲

10. 構造体
   1. 構造体とは
   2. 構造体の宣言
   3. 構造体の使用法
   4. 構造体のポインタ
   5. 構造体の構造体
   6. 構造体と関数
   7. 練習問題

11. 共用体
   1. 共用体とは
   2. 共用体の宣言
   3. 共用体の使用法

12. ファイル
   1. ファイル
   2. ファイル構造体
   3. ファイル作成・オープン
   4. ファイル読み込み
   5. ファイル書き込み
   6. ファイルクローズ
   7. ファイルエラー
   8. ランダムアクセス
   9. 標準入出力
   10. 練習問題
   11. ファイルの検索
   12. ファイルの削除
   13. ファイル名変更
   14. ディレクトリ操作

13. 低水準入出力関数
   1. 高水準入出力関数との違い
   2. ファイル作成
   3. ファイルオープン
   4. ファイル読み込み
   5. ファイル書き込み
   6. ファイルクローズ
   7. 標準入出力
   8. ランダムアクセス
   9. ファイルポインタとファイルディスクリプタ
   10. 練習問題

14. データ構造
   1. データ構造とは
   2. データ構造の種類
   3. 線形リスト
     4. 単方向リスト
     5. 双方向リスト
     6. 環状リスト
   7. ベクター
   8. 木
     9. 二分検索木
   10. スタック
   11. キュー

15 標準関数
   1. 文字分類・文字変換
   2. 文字列操作
   3. データ変換
   4. メモリー操作
   5. 数値演算
   6. ファイル操作(高水準入出力関数)
   7. ファイル操作(低水準入出力関数)
   8. プロセス関係

16 関数ポインタ
   1. 関数ポインタとは
   2. 関数ポインタ
   3. 関数ポインタと引数・戻り値
   4. 関数ポインタの配列

17. そしてその後は (PR)
   1. ヒューマンアカデミー C言語講座
   2. el school C言語講座
   3. C++入門


・ このページの先頭に戻る
・ トップページに戻る


・ このページの先頭に戻る
・ トップページに戻る

トップ-> C言語入門:データ構造-> 14-4. 単方向リスト

←前ページへ :  トップへ :  次ページへ→


例題

  この単方向リスト構造を用いて、char型の文字と、次のデータのポインタを格納する 構造体を定義する。そして、main関数で、キーボードから入力されたアルファベット をリスト構造として格納する。

  そのほかに、このリストを操作する関数群を作れ。関数群は以下の通り。

void addhead(struct c_list newlist); リストの最初にデータを追加する
void addtail(struct c_list newlistt); リストの最後にデータを追加する
void removeall(); リストの全てを削除する
struct c_list removetail(); リストの最後のデータを削除する
struct c_list removehead(); リストの最初のデータを削除する
struct c_list getat(int nt); n番目のデータを取得する
void setat(int n, struct c_list newdatat); n番目のデータをnewdataで置き換える
void removeat(int n); n番目のデータを削除する
int getcount(); リストのよう素数を返す
int empty(); リストが空かどうかを調べる
void insertafter(int n, struct c_list newdata); n番目の後にnewdataを追加する
void insertbefore(int n, struct c_list newdata); n番目の前にnewdataを追加する
int finddata(struct c_list data); リスト内にdataがあれば、何番目かを返す
struct c_list getnext(); 順次アクセス:次の要素を返す
void getheadposition(); 順次アクセス:getnextを呼ぶ前に呼ぶ。


解答


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <conio.h>

//
// 構造体の定義
struct c_list{
    char   ch;               // 格納文字
    struct c_list *pnext;    // 次のデータのポインタ
};

// プロトタイプ宣言
void   addhead(struct c_list newlist);
void   addtail(struct c_list newlist);
void   removeall();
struct c_list removetail();
struct c_list removehead();
struct c_list getat(int n);
void   setat(int n, struct c_list newdata);
void   removeat(int n);
int    getcount();
int    empty();
void   insertafter(int n, struct c_list newdata);
void   insertbefore(int n, struct c_list newdata);
int    finddata(struct c_list data);
struct c_list getnext();
void   getheadposition();

//
// リストの最初と最後のデータのポインタ
struct  c_list *pstart, *pend;
struct  c_list *ptr;


// // メイン関数 void main(){ struct c_list data; char ch; // リスト作成 ch = getche(); while( isalpha(ch) ){ data.ch = ch; addtail(data); ch = getche(); } // 改行 printf("\n"); // リスト表示 getheadposition(); do{ data = getnext(); if ( data.ch ) putch(data.ch); }while(data.ch); printf("\n"); // 確保した領域を開放する removeall(); }
// // リストの最後に追加 void addtail(struct c_list newlist){ struct c_list *ptr; // メモリー確保 ptr = (struct c_list*)malloc(sizeof(struct c_list)); // 現在の最後のデータにアドレス格納 if ( pend ) pend->pnext = ptr; else pstart = ptr; pend = ptr; // データをコピー *pend = newlist; // 追加したデータの次のポインタをNULLにする pend->pnext = NULL; }
// // リストの最初に追加 void addhead(struct c_list newlist){ struct c_list *ptr; // メモリー確保 ptr = (struct c_list*)malloc(sizeof(struct c_list)); // データをコピー *ptr = newlist; // 追加するデータの次のポインタを、現在の最初のデータにする ptr->pnext = pstart; // 最初のデータを、今追加したデータに置き換える pstart = ptr; }
// // すべてのデータを解放する void removeall(){ struct c_list *p, *pnext; // 1つずつ削除していく for ( p = pstart ; p ; p = pnext ){ pnext = p->pnext; free( p ); } // リストの開始、終了位置をNULLにする pend = pstart = NULL; }
// // 最初のデータを削除する // 戻り値として、最初のデータを返す struct c_list removehead(){ struct c_list ret; // 構造体の中身をNULLで初期化する ret.ch = '\0'; // リストの要素が空でなければ if ( pstart ){ // 最初のデータ取得 ret = *pstart; // 最初のデータを解放する free( pstart ); // 最初のデータを次のデータに置き換える pstart = ret.pnext; // 空になったらpendも修正 if ( !pstart ) pend = NULL; } ret.pnext = NULL; return ret; }
// // 最後のデータを削除する // 戻り値として、最後のデータを返す struct c_list removetail(){ struct c_list ret; // 戻り値用構造体をNULLで初期化する ret.ch = '\0'; // リストの要素が空でなければ if ( pend ){ struct c_list *ptr; // 最後のデータ取得 ret = *pend; // 最後から2番目のpnextを変更する for ( ptr = pstart ; ptr ; ptr = ptr->pnext ){ // 最後から2番目のデータなら if ( ptr->pnext == pend ) // pnextをNULLに変更 ptr->pnext = NULL; } // 最後のデータを解放する free ( pend ); // 最後のデータを、その前のデータに置き換える pend = ptr; // 空になったらpstartも書き換える if ( !pend ) pstart = NULL; } ret.pnext = NULL; return ret; }
// // n番目のデータを返す struct c_list getat(int n){ struct c_list *ptr; struct c_list ret; // 戻り値用データをNULLで初期化する ret.ch = '\0'; ret.pnext = NULL; // nがマイナスはありえない if ( n < 0 ) return ret; // n番目の要素を返す for ( ptr = pstart ; ptr ; ptr = ptr->pnext ){ if ( n-- == 0 ) return *ptr; } // n番目の要素がない場合 return ret; }
// // n番目の要素を置き換える void setat(int n, struct c_list newdata){ struct c_list *ptr; struct c_list *pnext; // nがマイナスはありえない if ( n < 0 ) return; for ( ptr = pstart ; ptr ; ptr = ptr->pnext ){ if ( n-- == 0 ){ pnext = ptr->pnext; *ptr = newdata; ptr->pnext = pnext; return; } } }
// // n番目の要素を削除する void removeat(int n){ struct c_list *ptr, *ppre; // nがマイナスはありえない if ( n < 0 ) return; // 0番目だったら、removeheadを呼び出して帰る if ( n == 0 ){ removehead(); return; } // リストが空だったら帰る if ( !pstart ) return; // 準備 ppre = pstart; ptr = pstart->pnext; n--; while( ptr ){ // 削除するぞ! if ( n-- == 0 ){ // リストのつなぎ換え ppre->pnext = ptr->pnext; // n番目のリストを解放する free ( ptr ); return; } ppre = ptr; ptr = ptr->pnext; } // n番目がない場合はここに来る }
// // リストの要素数を返す int getcount(){ struct c_list *ptr; int n = 0; // 空ではない場合 if ( pstart ){ for ( ptr = pstart ; ptr ; ptr = ptr->pnext ) n++; } return n; }
// // リストが空かどうかを調べる // 戻り値:0 ------> 空ではない // 0以外 --> 空である int empty(){ return pstart ? 0 : 1; }
// // n番目の次に要素を追加する void insertafter(int n, struct c_list newdata){ struct c_list *ptr, *pnew; // nがマイナスはありえない if ( n < 0 ) return; for ( ptr = pstart ; ptr ; ptr = ptr->pnext ){ // 指定要素なら if ( n-- == 0 ){ // メモリー確保 pnew = (struct c_list*)malloc(sizeof(struct c_list)); // 新しく確保したメモリーにデータをコピーする *pnew = newdata; // リストのつなぎ換え pnew->pnext = ptr->pnext; ptr->pnext = pnew; return; } } // n番目のデータがなければ、ここに来る }
// // n番目の前に要素を追加する void insertbefore(int n, struct c_list newdata){ struct c_list *ptr, *pnew; // nがマイナスはありえない if ( n < 0 ) return; // 最初に追加するなら、addheadを呼んで帰る if ( n == 0 ){ addhead( newdata ); return; } for ( ptr = pstart ; ptr ; ptr = ptr->pnext ){ // 指定要素なら if ( --n == 0 ){ // メモリー確保 pnew = (struct c_list*)malloc(sizeof(struct c_list)); // 新しく確保したメモリーにデータをコピーする *pnew = newdata; // リストのつなぎ換え pnew->pnext = ptr->pnext; ptr->pnext = pnew; return; } } // n番目のデータがなければ、ここに来る }
// // 要素を探し出す // 見つからなければ-1を返す int finddata(struct c_list data){ struct c_list *ptr; int i = 0; for ( ptr = pstart ; ptr ; ptr = ptr->pnext ){ // 見つかった! if ( data.ch == ptr->ch ) return i; i++; } // 見つからなかった return -1; }
// // 順次アクセス // 現在のデータをptrで保存しておき、この関数が呼ばれる度に次のデータを返すようにする struct c_list getnext(){ struct c_list ret; // 初期化 ret.ch = '\0'; if ( ptr ){ ret = *ptr; ptr = ptr->pnext; } ret.pnext = NULL; return ret; }
// // 順次アクセス // 現在のデータをpstartにし、次回のgetnextで最初のデータを返すように初期化する void getheadposition(){ ptr = pstart; }

  このように、リストに関する一連の関数群をあらかじめ作成しておけば、 実際にリストを使う関数(この場合はmain関数)は、リスト構造がどうなっているのかを 知っている必要はない。

  また、このような関数群を作る場合は、可能な限り構造体の中身が変わっても 変更する必要がないように作るべきである。上の例では構造体の中身が、char型1つの データから、メンバーを増やしたりしても、リスト操作用の関数群には変更する必要は ほとんどない(唯一finddata関数だけ変更する必要がある)。


←前ページへ :  トップへ :  次ページへ→