void - みる会図書館


検索対象: 月刊 C MAGAZINE 1990年6月号
51件見つかりました。

1. 月刊 C MAGAZINE 1990年6月号

db 型 (main. cc から抜粋 ) #include "d い st. h" / / 抽象データ型の定義 / / データベース ( 双方リストのおおい ) class db { private: d い st a い st, public: node* append ()* e) : node* search(String s) : List4 / / 操作 append node* db : :append ()* e) { a い st. append (e) : / / 操作 search node* db::search(String s) { for (iter it(a い (t) : it() ! = NULL; it. next()) { if (s = ptr->key()) { / / キー情報の比較 return i t ( ) : return NUL し : shop 型 (shop. h, shop. cc から抜粋 ) / / クラス shop の定義部 class shop { / * 基底クラス shop * / private: String nane; Str ing adrs : Str ing phone; public: shop(void) { } shop(String name, String adrs, string phone) : char* key(void) { return name; } / / キー情報を返す virtual void print(void) : / / 仮想関数 List5 / / クラス shop の実装 shop: :shop(String nn, String ar, String pn) :nane (nn) , adrs (ar) , phone (pn) { } VOid shop::print(void) { / / 各メンパの情報を表示する くく name くく” \ n ” cout くく "name ・ くく adrs くく” \ n ” くく” address : くく "phone number : 下のような処理がされます。 くく phone くく "Yn ” : ① 2 以上 , 所定の最大値以下の全整数値を含む整 数の集合 ( 数列など ) を用意する ②整数の集合中の最小の整数は , 素数である ③②で与えた素数の倍数を整数の集合から取り 除く ② ~ ③を繰り返すことにより , 素数を得ることが てきます ( Fig. 2 参照 ) 。この方法は , 大きな素数を得 るのには適していませんが , べンチマークテストな どにしばしば利用されるアルゴリズムてす。 C 十 + プログラミング入門 103

2. 月刊 C MAGAZINE 1990年6月号

・用法 expand( block, size ) ; ■引数 void *block Size t Size ■戻り値 void * block NULL ■工ラーなし 最後に割り当てたメモリへのポインタ 新しいメモリサイズ ( バイト単位 ) : 正常終了 : メモリが不足 ・注意 Tiny モデルを .COM に変換する場合は fa 「 m 訓の使用は不可能 farrealloc ・機能要約 fa 化ープ領域の再割り当てを行う #include く a oc. h 〉 ・用法 farrealloc( oldblock, nbytes ) ・ ■引数 void far * 引 d ock : 以前に割り付けられた領域へのポインタ unsigned long nbytes : 新しいバイト数 ・注意戻り値で示されたメモリは任意の型を記憶できるように調整される。項 目の新しいサイズは msize 関数で調べることができる。メモリの解放後 には , calloc, expand, h 酬 , m 訓 , 「 e 訓 oc などを実行すると , 解放 したメモリは block に指定することができない。解放されたメモリを block に指定して expand を実行しても , そのメモリは解放されたままになる farcalloc ■能要約 far からメモリを割り当てる #include く a Ⅱ oc. h 〉 ・用ラ去 farcalloc( nunits, unitsz ) ; ・引数 unsigned long nunits ・要素数 unsigned long nunitsz ・各要素の / ヾイト長 ■戻り値 void far * ■工ラー NULL 再割り付けたメモリのポインタ ・割り当てに失敗 ・戻り値 void far * ・エラー NULL ・割り付けたエリアへのポインタ : 割り込みに失敗 ・注意 Tiny モデルを .COM に変換するときは使用不可能 farcoreleft ■機能要約 fa 「ヒープの未使用メモリを調べて返す #include く a Ⅱ oc. h 〉 ■用法 unsigned long farcoreleft( void ) ・ ■引数なし ■戻り値割り当て領域とメモリの最後までの間の未使用領域を返す ■工ラーなし ・注意 Tiny モデルを .COM に変換する場合は使用不可能 farfree ・機能要約 far ヒープからメモリを一個解放する #include く a ⅱ oc. h 〉 ー用法 farfree( block ) ; ・引数 voidfar * ock : 解放するメモリへのポインタ ■戻り値 void ■工ラーなし 注意 ock は , far ヒープによって割り当てたメモリを解放する Tiny モデルを .COM に変換する場合は不可能である farmalloc で割り当てられたメモリは f 「 ee では解放されない その反対に maloc で割り当てられたメモリも , fa 市 ee では解放されない ・用法 farmalloc( nbytes ) く訓 oc . h 〉 #include ・機能要約 fa 「ヒープにメモリを割り当てる farmalloc ー引数 unsignedlongsize ・割り付けるメモリのバイト数 ・注意 Tiny モデルを .COM に変換する場合は不可能 —ffree ■機能要約デフォルト以外のデータセグメント内のメモリを解放する #include く m 訓 oc . h 〉 ・用法 ffree( buffer ) ; ・引数 void far * bu 升 e 「 : far ヒープ内の解放するメモリへのポインタ ■戻り値 void ■工ラーなし 注意 buffer は , fm 訓 oc によって割り当てたメモリのポインタである ・戻り値 int : 次の記号定数のうちから , ひとつを返す ■引数 void ー用法 fheapchk() : く m 訓 oc. h 〉 #include ■機能要約 fa 「ヒープ領域内で整合性をチェックする —fheapchk 付けがエラーになることがある fmal で割り付けられないポインタを解放しようとすると以降の割り HEAPOK ヒープは整合性を保っている HEAPBADBEGIN 初期ヘッダの情報がない ヒープが初期化されていない H EAPEMPTY HEAPBADNODE ノード異常か , ヒープの破損がある ■工ラーなし 注意スモールおよびミディアムモデルでは一 nheapchk を使用する ・引数 unsigned int fill : 充境する文字 ・用法 fheapset( fill ) : く malloc. h> #include ■機能要約 fa 「ヒープ領域内で整合性をチェックし , 空のヒープを埋める —fheapset ■戻り値 int : 次の記号定数のうちから , ひとつを返す HEAPOK ヒープは整合性を保っている ・戻り値 void far * ・エラー NULL ・割り付けたメモリの先頭ポインタ ・割り当てに失敗 ■工ラー ・注意 HEAPBADBEGIN 初期ヘッダの情報がないか不正である ヒープが初期化されていない HEAPEMPTY HEAPBADNODE ノード異常か , ヒープの破損ある なし スモールおよびミディアムモデルでは nheapset を使用する 94 CMAGAZINE 19 6

3. 月刊 C MAGAZINE 1990年6月号

dList 型 (dlist. h, dlist. cc) / / 双方リストの定義 #include く stdio. h> #include "shop. h ” typedef shop E : typedef E* pE; / * 節点クラス node の定義部分 * / class node { friend class d い st; / / フレンドの宣言 friend class iter; pr ivate : E* e : / / 要素のポインタ node* previos; / / リンク / / リンク node* next : public: node()* e) : / * operator E* ()o i d) : * / operator pE(void) : List3 / * クラス d い st の定義部分 * / class d い st { friend iter; private: node* head; / / リスト構造の最初へのリンク / / リスト構造の最後へのリンク node* tai l; pub I i c : d い st (node* ptr =NU しい : node* append ()* e) : / * 反復子クラス iter の定義部分 * / class iter { private: node* ptr : pub 1 i c : iter(d い st a い (t) : node* operator() (void) : node* next(void) : node* previos (void) : / / 双方リストの実装 #include ” d い st. h" / * クラス node の実装部分 * / node : :node ()* e) { node : : e = e : next = previos = NU しし: node: :operator pE(void) { if (this = NU しい { return N 乢し : return e; / / 型変換演算子 / * クラス d い st の実装部分 * / d い st: :d い st(node* ptr = N 乢い { C + 十プログラミング入門 101

4. 月刊 C MAGAZINE 1990年6月号

五ロ ニ : ロ はじめて学ぶ 0 プロクラー ニンク インタになっています。実引数は , 順に somed ay. int 型変数 a の値 , b の値 , int 型変数 tashi のア そして以下のように入力する。 ドレス , hiki のアドレスとなります ( & はア grp language < test ドレス取得演算子て、したね ) 。関数 ca て、 このとき , 次のような出力が得られればよ は , x, Y という値の和を第 3 引数が指すアド ポインタとデータの授受その 1 レスに , 差を第 4 引数が指すアドレスに , そ れぞれ代入しています。呼び出し側に戻っ てみると ,tashi や hiki のアドレスには計算結 果が入っていることになります。したがっ て和と差のふたつの値を得ることがてきま この関数 ca の第 1 , 2 引数のような関数間 のデータの受け渡し方法を「値による受け渡 し (call by value) 」 , 第 3, 4 引数のようなポ インタを使った受け渡しを「参照による受け 渡し (call bY reference) 」と呼びます。参照 による受け渡しはこのような複数のデータ を受け取るときに便利て、すが , 関数間の独 立性は低下します。 参照による受け渡しの一種てすが , 引数 として配列を用いる場合があります (List2 の。関数間て、配列を受け渡すときには , void sort(int x ロ ) のように , 引数が配列てあることを明示し ます。配列のサイズはすて、に決まっている のて、 , とくに記述する必要はありません ( 配 列のサイズを記述してもかまいません ) 。あ るいは 9 ー 2 て、お話したようにポインタにして アクセスしてもかまいません。そのときには , VOid sort(int *x) という宣言にします。呼び出す側ては , sort(a) : と引数に配列名を書くだけてよいわけてす。 この配列名が , 配列の先頭アドレスを指す 定数てあることはすてに説明しました。 今月のクイズ 「標準入力から入力し , 第一引数で与え た文字列がある行を , 標準出力するプログ ラム grp を書きなさい ( これは , UNIX の grep のサプレットもどきである ) 」 たとえば , test という名のファイルの中身 が , 以下のようだったとする。 This is the C Magazine. lt seems to be difficult for me. But I will master the C language l'm studying the C language. But I will master the C language someday. List 1 9 い st19 ポインタとデータの授受その 1 2 : 4 : #include く stdio. h> 5 : 6 : VOid calc(int x, int y, int *wa, int *sa) 8 : 9 : 10 : } 12 : void main(void) 13 : { int a, b, tashi, h i k i : = 100 : a b = 300 : calc(a, &tashi, &hiki); printf("%d + Xd = %dYn%d ー 20 : 21 : } h i k i ) : a, b, tashi, ポインタとデータの授受その 2 LiSt 20 2 : い st20 ポインタとデータの授受その 2 4 : #include く stdio. h> 5 : #define NUMBER 10 6 : 7 : VOid swap(int *x. int *y) 9 : int work; work; 16 : void sort(int x ロ ) for (i = 0 : i く NUMBER-I; i 十十 ) for(j i 十 1 : j く NUMBER; j + + ) 22 : 24 : } 26 : void nain(void) int a [NUMBER] : 28 : 29 : int i; 30 : for (i = 0 : i く NUMBER; i + + ) 32 : 33 : 34 : for (i = 0 : i く NUMBER,• i + + ) printf("Xd ” , a[i]); printf("%n") : sort (a) : for (i = 0 : i く NUMBER,• i + + ) 39 : printf("Xd ” , a[i]); printf("%n つ : 40 : 42 : } work はじめて学ぶ C プログラミング 137

5. 月刊 C MAGAZINE 1990年6月号

五ロ 用 応 ます。 なお , printf< は , 画面は自動的にスクロ ールアップしてきました。しかし , これか らは画面の任意の場所に表示てきるのて , すてに表示されているデータとオーバラッ プする位置に表示する場合には , あらかじ めその部分を消去しておく必要があります。 画面消去には , 1 行消去 (clearl ) , 全画 面消去 ( cls) , カーソル位置の右側消去 ( 醂 「 ight), カーソル位置の左側消去 ( c t ) が あります。それぞれを使い分けてください 例として dbmg の main 関数 (List1,Fig.1) を挙げます。 なお , 呼び出し関数を変更する際には , インタフェイスミスが多発します。ェディ タのサーチ機能などを使って , インタフェ イスをよく確認してください また , ガイドメッセージの表示用の msg 関数とエラーメッセージ表示用の e 「「 msg 関 数を新設します (List2) 。関数を作ること て、 , 各所て、 1 行消去関数 , カラー表示関数 ( colo 「 ) を呼び出す手間が減ります。このよ うにあちこちて同じ手続きを踏む場合には , 関数化することによって , ステップ数の節 約とバグの防止になります。 なお , ガイドメッセージは水色て , 工ラ ーメッセージは黄色て表示しています。 List 1 / * データベースドライプ名 * / 51 : drive; Char 52 : title(void); 53 : VO i d di spdata (void) : 54 : VO i d apndrec ()o (d) : 55 : VOid de lrec (void) : 56 : VOid recall-rec (void) : 57 : VOid 58 : pack-rec (void) : VOid recno) ; di sprec ()I LE *datfi le,long 59 : VOid copyrec()I LE *datfile, PILE *tmpfi le, long 60 : int *recn02) : recnol, long 61 : FI し E *datopen (char *mode) : 62 : FI し E *tmpopen (char *mode) : 63 : VO i d mngwrt (VOi d) : 64 : dsp-mng ()o (d) : VO i d 65 : nsg(int y, char *buf, int count) : VO i d 66 : err-msg (int y, char *buf, int count) : VOid 67 : 68 : / * 69 : 70 : int main() 71 72 : int dbmfile; int workno, stat, i : str[50] : 74 : Char 75 : cls(); nsg ( 20 げ管理ファイル db. nng のドライプを指定してください第 48 ) : 77 : ” g ( 21 ドライプ名 = 第 13 ) : 78 : gets (str) : fname [0]=drive=str[0] : 80 : dbnfi 1 e=open(fname, 0 ー RDON し幻 O-BINARY) : if(dbmfile==-l) 83 : buzzer ( ) : 84 : err-msg ( 22 , ”管理ファイルをオープンできません。 - , 34 ) : 86 : stat=read(dbmfi le, (char * ) &mng, sizeof (nng) ) : 88 : if (stat== 90 : buzzer ( ) ; err-ns g ( 22 管理ファイルをリードできません。第 32 ) : 92 : close (dbnf ile) : 94 : 95 : if (stat== 96 : return : 97 : menu . 98 : key-asgn ( の : do { 99 : cls(): 100 : title(); 101 : 田 sg ( 20 , " 処理番号を指定してください。 - , 28 ) : 102 : color(0RG,SKY) : 103 : put-str(27,3,"1 : 1 レコード表示 " , 18 ) : 104 : 105 : put-str(27,5,"2 : 1 レコード追加第 18 ) : 106 : put-str(27,7,"3 : レコード削除 " , 16 ) : put-str(27,9,"4 : 削除レコードの復活 22 ) : 107 : 108 : put-str(27,11,"5 : 削除レコードの圧縮第 22 ) : 109 : put-str(27,13,"6 : DB 管理テープルの表示第 26 ) : 110 : put-str(27, 15 げ 9 9 : 終了 1 の : ” g ( 21 , " 処理 N 0 . 112 : gets (str) : workno=atoi (str) : 113 : 114 : switch (workno) 115 : 116 : case 1 : d i spdata ( ) : 118 : break; 119 : case 2 : 120 : apndrec 0 : 121 : nngwrt ( ) : 122 : break; 123 : case 3 : 124 : de lrec ( ) : 125 : break; 126 : case 4 : 127 : recall-rec ( ) : 128 : break; e ー se / * キーコード定義 * / ・入力関係 main の先頭と最後て、 keyin. c の key asgn 関数を呼びます ( List1 ) 。 key ー asgn 関数を実行しないとキーポード 入力がうまくてきません。もちろん MS-DOS へ復帰する前に , 割り付けはもとどおりに 戻しておきます。 レコード追加 ( apnd 「 ec 関数 ) のキーポード 入力を gets 関数から get key st 「関数へ変史 します。 これにより画面上の任意の場所てキーポ ード人力がてきます。 gets 関数は , ほかの場所ても使用してい ます。 apnd 「 ec 関数の使用例を参考に修正し てみてください (List3, Fig. 2)。 " , 13 ) : 1 レコード表示 * / 1 レコード追加 * / / * 管理ファイル更新 * / / * レコード削除 * / / * 削除レコードの復活 * / 応用 C 言語 111

6. 月刊 C MAGAZINE 1990年6月号

さてこの main 関数の宣言部分の a 「 gv て、す void main(int argc, char * *a 「 gv) とか , void main(argc, argv) int argc; char * * argv; としてもかまいません。これはコラムに出 てきた「ポインタのポインタ」という関係 てす。ちょっとしたプログラム例を挙げて おきます。とくに説明はしませんが , 慎重 に見ていけば理解てきるてしよう (List16, 17 ) 。 main 関数の引数に関するまとめとして , List18 にちょっと大きなプログラムを載せま しよう。別に何に使えるというわけてもあ りませんが , オプション解析 ( というほどて、 もないてすが ) をしています。私たちがいろ いろとお世話になるフリーソフトウェアに はオプションをつけて機能を拡張するもの がたくさんあります。オプションをどのよ うに判定しているかの一例て、す。体裁を整 えていたらちょっと大きめのプログラムに なってしまいました。 14 : } 22 : } 34 : { C 言語はいくつかの関数からなり , 各関数 間て、は引数 , 戻り値といったものを使って データのやりとりをしています。しかしこ の機能だけて、は , 次のようなことは実現て きません。 ふたつの数 x , y を渡してその和と差を得 る関数を作る なぜならば関数て、は , 戻り値はひとつに 限られています。この場合 , 和と差のふた for (i ー printf("Xc", *argv[i]); * sa * wa い st18 m a i n 関数の引数その 5 1 8 main 関数の引数その 5 void calc(int x, int y, int *wa, int * sa) List 2 : 5 : 6 : 9 : 20 : 23 : 24 : 26 : 28 : 29 : 30 : 32 : 33 : 35 : 36 : 38 : 39 : 40 : 42 : 43 : 44 : 48 : 50 : 52 : 53 : 54 : 55 : 56 : 59 : 63 : 64 : 65 : 66 : 69 : } そして呼び出しは , strings [strings あるのに対して , 第 3 , 第 4 引数は int へのポ っています。第 1 , 第 2 引数が int 型データて て、す。第 3 , 第 4 引数が今まてにない形にな calc(), b, &tashi, &hiki) : 4 : #include く stdio. h> #include く ctype. h> #include く process. h> 7 : #define TRUE 1 8 : #define FALSE 0 10 : void usage(void) pri ntf ("%ausage exit(l); 16 : VOid missopt(void) printf("%amissing option printf( ” printf(" exit(l); int length(char *str) i nt i : for ( i = 0 : *str ! = return(i) : command -OPt ion i 十十 , lower case") : upper case") : str 十十 ) nochange VOid main(int argc, char *argv[]) int lower ま FA し SE, upper ニ FA し SE, int i, J, count; if (*ptr 十十 = ptr = argv[l]; usage ( ) : if (argc く 2 ) char *ptr; switch (*ptr) { nochange ニ PA し SE,• case ' 1 ' case ・し・ break : case 'u ・ break; case ' \ 0 ' break; default: lower upper = TRUE; = TRUE; nochange = TRUE; missopt ( ) : つの値を返したいのて、すから , 方法ては難しいわけてす。 これまての 2 : i く argc; i 十十 ) { ptr = argv[i]; = length(ptr) : count = 0 : j く count; j 十十 . argv[i] 十十 ) { for (j 外部変数を使ったデータの授受をすれば 実現てきますが , たくさん外部変数を使う プログラムはあまりお勧めてきる代物て、は ありません。このようなときにポインタが 使えます。 136 CMAGAZINE 19 6 んて、いる式に秘密があります。関数は , List19 がその例て、す。関数 ca にとそれを呼 if (lower) printf("Xc" i f (upper) printf("Xc" i f (nochange) printf("}n") : (char)tolower((int)*argv[i])); (char)toupper((int)*argv[i])) :

7. 月刊 C MAGAZINE 1990年6月号

最初に指摘したいのは , いうまてもない ことだが , int と void ては型が違うという問 題てある。本来ならば , startup が期待して いる型と , 実際の main( ) が返そうとしてい る型が違うということて , リンク時にエラ ーになってもおかしくない。このような事 態が容認されること自体が , すてに C という プログラミング言語の欠陥を露呈している とも考えられる。 似たような例をあげよう。よくあるまち がいとして知られているケースのひとつに 算術関数の型宣言を忘れるという問題があ る。たとえば sq 「 t ( ) という平方根を計算する 関数を使うとしよう。正しい使い方は , math. h をインクルードすることてある。 #include <math. h> void foo(void) double s ; S sqrt(2) ; ところが , 上の例て math. h のインクルー ドを忘れるとひどいめにあう。これは ANSI CC も , 古い C ても同じてある。 C の言語仕 様ては , 型宣言がなされていない関数は int を返すものと仮定されるため , sq 「 t ( ) が返す のは int だと解釈された結果 , s へと代入する 際に , 無用な doub 厄への型変換が行われる のだ。すなわち , sq 「 t( ) が返してきた ( 実は dou e の ) 値のビットパターンを誤って int て、 あると解釈し , その値を再び dou e へと変 換するという余計な作業が行われてしまう のだ。 なお , 上のコードは ANSI C を想定したも のてある。余談だが , 古い C てあれば , 仮に math. h をインクルードしていたとしても sqrt(2) という呼び出しを行うとまずい。そ れは , sqrt( ) が引数として dou e 型の値を 期待しているにもかかわらず , 2 という int 型 の値を渡そうとしているからてある。 古い C て、は仮引数の型に関する情報がなか ったため , 明示的に dou e の値として渡す 必要があった。具体的には sq 「 t ( 2.0 ) とか , sq 「 t ( ( double ) 2 ) などと記述しなければうまく いかなかった。 sq 「 t ( ) に int を渡す場合には型 変換をしなければならない。 ANSI C の場 合 , プロトタイプによって仮引数の型情報 が与えられる ( math. h の中てプロトタイプ宣 言が行われているはず ) ため , この変換が自 動的に行われる。 main ( ) の場合も状況は似たようなものて、 ある。すなわち , 呼び出し側ては int の値が 返ってくると期待しているのに , それを裏 切って void と宣言してしまうことが可能て , その場合にも , それなりにリンクは行われ る ( ことが普通てある ) 。 しかし , もし startup が int を期待している 環境てあれば , void main( ) を使うことは明 白な誤りだ。ところが , これは先の sq 山 ) の ケースほど表面に現れる問題を引き起こさ ことがほとんどなのて , 多くの場合 , 「実際はまちがいてある」が「まちがいに気づ かれずにすんて、いる」のだといえよう。 値を返す関数と , 返さない関数を混同し てしまっても , もしその関数の返す値がま ったく使われないか , あるいは非常に目だ ちにくいケースてしか使われないとしたら , その場合に誰が気づくてあろうか ? こて , 心配性の人は気にするかもしれ ない。「値を返す」 main ( ) を期待している star tup に , 実際には「値を返さない」 main ( ) を与 えているのだから , startup は何もないとこ ろから返り値を取り出さなければならない はずだが , いったいどこから借金 ( ・ ) す るのだろうかと。 この問題は結論からいうと「ゴミを拾う」 ということて片づく。 ほとんどすべての C コンパイラにおいて , 関数から i ⅲが返される場合 , 機械語レベル ne Oint Edition て、は , ( コンパイラによってあらかじめ定め られた ) レジスタに入れられて値が返され というのは , C の場合 , 値を返しても必ず しもそれが使われるとは限らない ( 6 ) のて、 , 使われても使われなくても実害がないよう な ( もし値が使われなかった場合に , わざわ ざ捨てる手間をかけずにすむような ) 方法て、 値を返すのが賢明だと考えられるからてあ る。レジスタに値を入れて戻せば , 使われ なくても何の問題もないわけだ。 このため , 逆の場合にもあまり被害はな い。つまり実は値を返さない関数を , 値を 返す関数だと思って呼び出し , 返された ( と 思っている ) 値を利用した場合には , レジス タの値を参照することになる。レジスタに は , なんらかの値がつねに入っているのて , いっても参照することが可能てある。しか し , それはおそらくゴミだろう。もっとも , 処理系によっては , たまたまある意味をも った値が入っていることもあるようだ 返り値のな、 m ⅲ ( ) 関数 いちおうこれて , int が正しいが , void と 書いてもさほど問題はないらしいというこ とがおわかりだろう。もちろん終了コード を何かの目的に使うつもりてあれば , void main( ) と宣言してはまずい ( 7 ) ては , もうひとつの疑問だった , void と 宣言していない main( ) なのに , 値を返して いない (return く式〉 : がない ) 場合にはどう なるのだろうか。この場合 , 型宣言が行わ れていない関数は int を返すと仮定するとい う C のルールによって main ( ) は int を返すこ とになっているはずてある。それなのに値 を返していない場合の問題てある。 これまての説明をよく読めば , すてに答 えはわかるのてあるが , この場合も「ゴミ」 が返される。つまり , return く式〉 : て具体 的に値を返さない場合には , void main()< ANSI C ー more 123

8. 月刊 C MAGAZINE 1990年6月号

きます。考えてみれば当然のことてすが , ポインタ p が変数てあるからこそてきる芸当 て、あって , 配列てはこの処理がとてもめん どうなものになります。 ②文字列の入れ換え 文字列の入れ換えをしてみましよう ( List10 ) 。蛇足ながら p も s 們も st 「 2 もポイン タてすから変数てす。 ③文字列のシフト これまての例てはいずれのポインタも文 printf("strl . XsYn", strl) : 14 : } す。まず str に文字列 " HeIIo 字列の先頭を示していますが , 先頭以外を 示させてみましよう ( List11 ) 。このプログラ ムにはちょっと説明が必要てすね。 fo 「文がこのプログラムの核になっていま ! " の先 List 2 : 3 : 5 : 7 : 10 : 17 : 20 : 22 : 23 : 24 : List 2 : 3 : 5 : 7 : 12 : 15 : 20 : 22 : 23 : 24 : ポインタと文字列その 5 1 0 6 : #include く stdio. h> 8 : void main(void) Char Char い st 10 ポインタと文字列 文字列の入れ換え *strl, *str2; 頭アドレスを代入します (str = " HeIIo ! " の式 ) 。 * st 「は st 「の指すアドレスの 値を表していますが , この値は char 型のデ ータてす。真ん中の式ては現在 st 「の指して いるアドレスの値が ' \ 0 ' てあるかどうかを調 べています。文字列の終わりは , 必ず ' \ 0 ' に なっている約束てすから , この式は fo 「文の 継続条件になります ( * str ! = ' 判 ' ) 。 f0 「文 の継続するかぎり st 「を先頭とする文字列を 出力し ( p 「 intf 関数 ) , st 「の値をひとつ進めま ④文字列のコビー す (str 十十 ) 。 文字列のコピーをしてみましよう (List12)0 この例ては st 「 2 を st 「 1 にコヒ。ーし ています。 wh ⅱ e 文が重要てす。複合形にな っているのて一見難しそうてすが , 簡単に すると次のようになります ( * wl ! = ' \ 0 ' は ⑤文字列の連結 w2 十十 ; wl 十十 ; * wl * w2 ; while (*wl ! = ' \ 0 ' ) str2; w2 strl; wl * wl ! = 0 と同じことてす ) 。 いています。まえもって strl の終わりにポイ (List13)0 ここては st 「 1 の後ろに st 「 2 をつな さらにふたつの文字列をつないて、みます str 十十 ) List 2 : 3 : 5 : 7 : 12 : List 2 : 3 : 5 : 7 : 12 : 13 : 14 : 20 : 22 : 23 : 24 : ポインタと文字列その 6 1 1 い s t 11 ポインタと文字列 文字列のシフト strl str2 = ” Hello, everyone. : printf("str2 ・ printf("strl str2 = p; strl = str2; P = strl; printf("str2 : XsYn" XsYn ” . XsYn ” str2) : strl) : str2) : 6 : #include く stdio. h> 8 : void main(void) Char for (str printf("XsYn" ” Hello c-world ! ! ! ” str) : ポインタと文字列その 7 1 2 6 : #include く stdio. h> 8 : void main(void) *strl, *str2; Char * wl. *w2; Char strl = "This is strl. str2 = ” THIS 給 STR2. printf("strl . XsYn ” printf("str2 : XsYn" い st12 ポインタと文字列 文字列のコピー strl) : str2) : ポインタと文字列その 8 1 3 6 : #include く stdio. h> 8 : void main(void) *strl, *str2; Char い st 13 ポインタと文字列 文字列の連結 *wl, *w2; Char ” This is strl. strl str2 = ” This is str2. printf("strl : XsYn" printf("str2 : XsYn" for()l = strl; *wl ! = 2 : str2: ⅶⅱ e ( * wl + 十 = * w2 + + ) strl) : str2) : str2 : strl : w2 printf("str2 : XsYn" printf("strl . XsYn" while(*wl + + = * w2 + + ) strl) : str2) : printf("strl 十 str2 : %sYn" strl) : 134 CMAGAZINE 19 6

9. 月刊 C MAGAZINE 1990年6月号

関数の引数にも void が置かれている。 void 修飾子は当然戻り値のないもの , または指 定する引数がないものを修飾する。したが って , このプログラムは , 非常に簡単にす るために , よけいな引数や戻り値を設定し ていない。 buffe 「は , cha 「型のポインタ変数 て、ある。この変数は訓 oca ( ) 関数によって確 保するスタック領域の先頭アドレスが格納 される。 最初に stackavail() 関数を使って利用可 能なスタックサイズを求めることがて、きる。 いちばん最初の p 「 intf ( ) 関数て、は , stack- avail( ) 関数の実行と同時に , その値をスク リーンに表示を行っている。このように使 用するとよけいな変数を使う必要がなくな る。また , 実際のプログラムサイズも小さ くなることが知られている。 今度は , スタックを指定した値だけ確保 する。このプログラムて、は訓。 ca ( ) 関数を使 用して 100 バイト割り当てている。 もし , この関数が実行時において失敗し た場合には NULL を返す。そのため , 次の if 文て、は訓。 ca ( ) 関数によって返ってきた値 を調べ , NULL かどうかを判断している。 工ラーの場合には , 工ラーメッセージを スクリーンに表示をして , プログラムを終 了させる。スタックの割り当てに成功する と , メッセージをスクリーンに表示させて , 再度スタックの利用可能サイズを調べ , そ の結果をスクリーンに表示している。した がって , スクリーンには , 最初のスタック 値と領域が確保されたときのスタック値の ふたつの値が表示されているはずて、ある。 また , その値の差は確保された値よりも大 ラタ乙 イラ 門 きいはずて、ある。なぜ , 指定した値よりも 大きく確保されるのかは , よくわからない 確保されたスタック領域の解放は , プロ グラムを終了することて行われる。 また , 訓 oca ( ) 関数の使用を , C プログラ ムて、いうところの別関数にして使用すると , その関数の終了とともにスタック領域の割 り当てが解放されるのて , こうした方法も よいかもしれない 本プログラムて、は , すべてスモールメモ リモデルて、コンパイルと実行の確認を行っ 0 ランタイムライプラリー覧表 3 alloca ■機能要約スタック領域からメモリを割り付ける #include く m 訓 oc. h 〉 ・用法 alloca( size ) ; ・引数 size t size : スタックから割り付けるバイト数 ・戻り値 void * : 割り付けられた領域への void ポインタ : 割り付け失敗時の戻り値 ■工ラー NULL ・注意割り当てられたメモリ空間は , 訓 oca を呼び出した関数の終了時に自動 的に解放される。訓 oca で返されたポインタ値は , f 「 ee の引数としてはい けない。また , 訓 oca は単純な代入文でのみ使用すべきである brk ・機能要約データセグメントのプレーク値を再設定する calloc ■工ラー ・戻り値 0 ー引数 void *addr ・用法 brk( addr ) #include ー 1 , errno に ENOMEM ( メモリが不足した ) をセット : 割り当てスペースの変更に成功 増減するバイト数 く訓 oc. h 〉 ■用法 calloc( n, size ) ・ ■工ラー N ULL ・戻り値 void* Size t Size ■引数 sizetn : 要素数 : 各要素のバイト長 : 割り付けたエリアへのポインタ : 割り付けに失敗した場合 ・注意割り付けたメモリはどんな型にも適合することが保証されている size に 0 を指定しても , ヘッダのみの割り当ては行われない coreleft ・機能要約未使用メモリを調べて返す #include く a Ⅱ oc . h 〉 ■用ラ去 Tiny, small, Midium の場合 unsigned CO 「 e 厄代 ( void ) Compact, Large, Huge の場合 unsigned long coreleft( void ) : ・引数なし ■戻り値 Large Small なし : ヒープとスタック間の未使用量を返す : スタックとデータセグメント間の未使用量から 256 バイ ト引いた値を返す ・機能要約 n 個の要素をもつ配列を割り付ける #include く m 訓 oc. h 〉 く訓 oc. h 〉 く stdlib. h> ・エラー —expand CTurboC] ■機能要約ヒープ領域の先頭を固定したまま , #include く m 訓 oc. h 〉 メモリサイズを変更する C プログラマのためのランタイムライプラリ入門 93

10. 月刊 C MAGAZINE 1990年6月号

五ロ はじめて学ぶ 0 プログラー ニンク いた場合 , 最後に文字列の終わりを示すデ char * Str; ータ ' \ 0 ' を入力しなくてはならない点てす ( 入 と宣言したのち , 9-3-2 文字列処理の実際 力しなかったらどうなるか , List7< 確認し This is てください ) 。 st 「 3 は scanf 関数を用いた手入 を代入しています。「 BASIC と同じてはない 文字列の設定がてきたのて , 実際の文字 力の場合てす。これは以前にも説明したこ か」と思われるかもしれませんが , 実は大き 列の処理例を見ていきましよう。 とがありますね。 く異なります。 BASIC は文字列変数がその scanf 関数を使った str3 は別にするとし 文字列そのものを記憶しているのに対して , 9-3-2 -1 簡単な文字列処理 て , s な 0 , strl, st 「 2 のいずれの場合も , 入 C のポインタによるこの方法は文字列の先頭 ①文字列のつなぎ変え 力に手間がかかったり設定する文字列の変 アドレスのみを記憶しているだけにすぎま まず List9 を見てください。 3 つの printf 関 更がしにくいなど , 少々使いにくくなりま せん ( Fig. 4 ) 。システムてはまず , " This is 数はいずれもポインタ p のさすアドレスを先 す。そこて本題のポインタを使った例をご " の文字列をどこかの空領域に確保し , 頭とする文字列を出力しています。しかし 紹介しましよう。 その先頭アドレスをポインタ str に代入して 出力される内容は 3 っとも異なります。秘密 ②ポインタを使う いるにすぎません。今回はポインタの講座 ( というほどてもないてすが・・・・・・ ) は直前の 文字列の設定にポインタを使ってみた例 てすから , おもにこちらを使うことにしま 代入文にあります。このようにひとつのポ が List8C す。このプログラムては , インタ p をつなぎ変えて別の文字列を出力て ポインタと文字列その 2 Fig. 4 ポインタによる文字列の設定 メモリ str= List 7 2 : 3 : 5 : 6 : #include く stdio. h> 7 : 8 : void main(void) str [ 80 ] ; Char str [ 0 ] str[l] 13 : str [ 2 ] str [ 3 ] str [ 4 ] str [ 5 ] str [ 6 ] str [ 7 ] 20 : str [ 8 ] str [ 9 ] 22 : str[10] = 23 : str[ll] = 24 : / * printf("XsYn ” 26 : 28 : } str い st7 ポインタと文字列 配列を使う場合 ( \ 0 を入れないとどうなるか ) str) : ポインタと文字列その 4 List 9 2 : 3 : 5 : 6 : #include く stdio. h> 7 : 8 : void main(void) Char Char *strl, *str2, *str3; strl ” Hello, 14 : str2 = everyone. str3 = ” HOW are you?" : 17 : 20 : 22 : 23 : } い st9 ポインタと文字列 文字列のつなぎ変え ポインタと文字列その 3 List 8 2 : 3 : 5 : 6 : #include く stdio. h> 7 : 8 : void main(void) Char str し ist8 ポインタと文字列 ポインタを使う場合 P = strl; printf("XsYn" = str2; P printf("XsYn" P = str3; printf("XsYn" "Th is is str by pointer. printf("XsYn" str) : はじめて学ぶ C プログラミング 133