ポインタ - みる会図書館


検索対象: 月刊 C MAGAZINE 1991年11月号
56件見つかりました。

1. 月刊 C MAGAZINE 1991年11月号

明解 五ロ = = ロ 一言座 と宣旨・初期化されています。初期化子の 数から配列の大きさは 3 となります。先の二 元配列と異なり , く char へのポインタの大 きさ 3 の ( 1 元 ) 配列 > となります。配列の要 素てある p [ 0 ] , p [ 1 ] , p [ 2 ] は , それぞ れ char へのポインタ型を持ちます。初期値 として Turbo, MS, LSI が代入され , そ れぞれの文字列を指すように初期化されま す。この様子が Fig. 7 の状態て、す。 ポインタの配列の受け渡し 6 ~ 10 行の print 関数は , List 6 と同様に 指定された n に応じて文字列を表示します。 関数の引数としての文字の配列の受け渡 しを行う場合は , void func(char x [ ] ) ; void func(char * x) ・ こては『文字』 のように行いましたね。 の配列て、はなく「文字へのポインタ』の配 列の受け渡しを行うのて、すから , void func(char *s [ ] ) ; void func(char * (s) ・ となります。これて、はわかりにくいのて、 , Fig. 8 にまとめました。これならばわかるて、 しよう。一般にく型 type > の配列の受け渡 しは , その配列の先頭要素へのポインタと ( 共通点 ) ありません。また多元配列の場合と違って して行います。いま , この type 自体がたま 配列とポインタは , [ ] 演算子を用いて要 たま「ポインタ」となっているだけて、す。や く余分 > な空間はないものの , メモリ上に 素を x [ i ] のように表せます。多元配列の場 りとりはポインタへのポインタの形て、行い は文字列とは別に三つのポインタがとられ 合も同様て、す。たとえば s [ 1 ] も p [ 1 ] も ます。 ます。 2 番目の文字列 MS へのポインタとなり , s [ 1 ] 【問題 3 】曜日に対応する文字列への [ 0 ] も p [ 1 ] [ 0 ] も文字 M を表します。 ポインタを返す関数 ( 相違点 ) ポインタの配列のやりとりは , その配 char *weekstr(int n) 配列はメモリ上の連続した空間に配置さ 列の先頭要素へのポインタ , すなわち を書け。たとえば weekstr(0) ; は文字 れますから , 二元配列 s は Fig. 5 のようにメ くボインタのポインタ > という形で行 列「日曜日」へのポインタを返すように モリ上に格納されます。 3 x 6 の大きさが確 つ する。多元配列版とポインタの配列版の 保されますから , MS や LSI の後ろの部分は ふたつのバージョンを記述すること。 く余分 > てあり , もったいないてすね。 ふたつの実現の違い 一方 , ポインタの配列は , Turbo, MS, 文字列の配列を , 二元配列 s とポインタの LSI へのポインタて初期化されますが , Fig. マンドライン引数 配列 p のふたつの方法て実現しました。これ 7 からもわかるように , これらの文字列リテ らの共通点・相違点を考えましよう。 ラルが連続して配置される保証はまったく 文字列の配列 ( ポインタの配列 ) List 1 : #include く stdio. h> / * 文字列の個数 * / 3 : #define MAX 3 / * 文字列の長さ ( 不要 ) * / 4 : #define LEN 6 5 : 6 : void print(char *SC], int n) if (n > = 0 & & n く MAX) 8 : printf( ” %s*n ” 9 : 10 : } 11 : 12 : int main(void) 13 : { 14 : int 1 ; char *pC] { ” Turbo ” 15 : 16 : for (i ニ 0 ; i く MAX; i + + ) 17 : PriiYLkP, i); 18 : return( の ; 19 : Fig. 8 配列の引数の宣言 ☆配列の受け渡し時の引数の宣言☆ char *x : または charx [ ] char * * z : または char * z ロ ☆通常の宣言☆ ( a ) 文字 Char X : ( b ) 文字へのホインタ Char *Z : 重要 明解 ANSI C 言語入門講座 129

2. 月刊 C MAGAZINE 1991年11月号

ます ) , void print(char (*p) [ 6 ] , int n) の意味を持ちます。 p の指す『大きさ 6 の配 列』の型て、あることを示すために , どちら の記述方法においても 6 を省略することはて、 きません。 p は s [ 0 ] を指し , *p は s [ 0 ] のェイリアスとなります。関数と引数の関 係は , Fig. 6 のようになります。 * p は [ ] 演算子を適用すると p [ 0 ] とも記述て、きま す。 よって p [ 0 ] は s [ 0 ] の別名となり , れて、 p は s と同様に使えます。混乱しないよ うに , 重要な点をもう一度整理しましよう。 ①多元配列は , 配列の配列て、ある。すな わちその要素がたまたま配列て、ある配 列が , 多元配列て、ある。 ②関数の引数として配列の受け渡しを行 うには , 配列の先頭要素へのポインタ という形式て、行う。 ③よって引数として多元配列の受け渡し を行うには , 配列て、ある先頭要素への ポインタという形式て、行う。 このことから print 関数の呼び出し print(), i) ・ の s は , &s [ 0 ] のことて、あって , &s [ 0 ] [ 0 ] のことて、はないとわかります。 Fig. 7 ホインタの配列による文字列の配列 Fig. 5 2 元配列による文字列の配列 p[O] pC2] ” Tu 「 bo ” ” MS ” ” LSI" sCO] sC2] * [ 2 い、 \ 0 Fig. 6 関数呼び出しと引数の授受 多元配列に対して特別なことはない。 たまたま配列の要素が配列になってい ることにのみ留意すればよい ポインタの配列による実現 たとえば char *p ” ABC ” のようにポインタを使って文字列を表せま す。ポインタの三つの配列を定義すれば , 三つの文字列を表せるはずて、す。ポインタ の配列を使って文字列の配列を実現したの が List 7 てす。ポインタの配列は , char * p [ ] pCO] s[O) main 関数 print(), i) LEN] print 関数 char p int n sC2] ・ pC2] 128 C MAGAZINE 1991 11

3. 月刊 C MAGAZINE 1991年11月号

00 ライフラリ開発技法 TabIe 12 List 25 のライプラリの機能概路 ライプラリ名 fptr(s,o) peek(s,o) peekb(). 0) poke(). 0) pokeb(s,o) のになります。 そのほかの処理系 (a) CS の調査 CS を調査するのはちょっとめんどうて、す。 アセンプラて、あれば , スモールコードメモ リモデルのとき一発て、わかるし , ラージコ ードメモリモデルて、も関数を組んてスタッ ク上にブッシュされているリターンアドレ スを調査すれば一発なのて、すが・・ は , 9 行目の宣言により s という名の SR は , こて、は C 言語のみを使って解決する方法 EGS 構造体を持ちます。 これは内部リンケ code= (segread ( & ージを持ちますから , ほかのソースファイ となりますにこて、カンマ演算子を使う理由 を考えましよう。 8086 用のほとんどの処理 ルの識別子 ( 名前 ) と衝突することはありま は , 「明解 ANSI C 言語入門講座」 ' 91 年 9 月 系には ,segread という関数が用意されてい せん。また 15 行目て、定義されたマクロによ 号を参照してください ) 。まず segread 関数を ます。 segread 関数は , <dos. h> て、定義され s. 呼び出して , り , getCseg は (segread ( & s) , s にセグメントをセットし ている SREGS 構造体に現在のセグメント値 0 と置き換、ら 0 ます ! たと、ば , てもらい , コードセグメントに相当するメ を格納する関数て、す。 getCseg ( ) ンバ cs を返します。これによって無事に現在 List 26 をインクルードしたプログラム COde Fig. 23 ポインタのセグメント値 / オフセット値を得る 機 能 セグメント s, オフセット 0 の値を持つ fa 「ホインタを作成する セグメント s , オフセット 0 のアドレスにあるワード値を得る セグメント s , オフセット 0 のアドレスにあるバイト値を得る セグメント s , オフセット 0 のアドレスにワード値を格納する セグメント s , オフセット 0 のアドレスにバイト値を格納する fa 「ポインタ (a) ポインタ s のオフセット値を得る nea 「ポインタ セグメント オフセット (unsigned) にキャスト (b) ポインタ s のセグメント値を得る セグメント オフセット (void far * ) にキャスト DS 上位 16 ピットを 取り出す 特集ライプラリ開発技法 71

4. 月刊 C MAGAZINE 1991年11月号

もに同じように取り出すことがて、きま す。 Fig. 22 nea 「ポインタ概念図 最大 64K (b) セグメントの取得 先頭 す。 far;tk インタは , もともと far なのて、 まず (void far * ) にキャストしま ※セグメントは固定 ポインタは先頭からのオフセット List 24 ポインタのセグメント部とオフセット部を得る #define SEGof(s) ((unsigned)(((unsigned long)((void far * ) s ) ) 〉〉 16 ) ) 5 : #else ー 0FFof(s)) > 〉 4 ) ) #define SEGof (s) ((unsigned) ( (((uns igned long) ( (void huge *)s)) \ 2 : #if defined(LATTICE) ((unsigned) (s)) 1 : #define OFFof(s) 6 : 4 : 3 : 7 : #endif List 25 —peek, ー poke ライプラリ 変化しません。 near ポインタて、あれ ば , 自動的にセグメント部に現在のデ ータセグメントがセットされます。 のように far* インタにキャストするこ とによって , たとえ near•+ インタて、も そのセグメント値を得ることがて、きま す。 16 ビットを取り出すだけて、す。 こまて、来れば後は簡単て、す。上位 うになります。機能概略は TabIe 13 に示し う。作成したヘッダファイルは List 26 のよ よび CS の値を返すマクロを作成しましょ 必要があることが頻繁に起こります。 DS お コードセグメント ( 以下℃ S ク ) の値を知る ータセグメント ( 以下、、 DS 〃と呼ぶ ) および 8086 プログラミングにおいて , 現在のデ 現在のセグメント値の調査 le 12 に示します。 k, poke ライプラリて、す。機能概略を Tab 一般的な形式にしたのが List 25 に示す一 pee 間中の任意のアドレスにアクセスて、きます。 far;tk インタを使用すれば , IM バイトの空 任意の番地のアクセス ます。 こには重要なテクニックが隠され 3 : 6 : 8 : TabIe 1 1 12 : #define _peekb(), 0 ) 11 : #define _peek( s, 0 ) 10 : #define _pokeb(), 0 , c) 9 : #define —poke( s, 0 , c) 7 : #endif 1 : #if defined(LATTICE) 2: ・ #define fptr(seg, ofs) ((vo id far * ) \ (((uns igned long) (seg) くく 4 : #else fptr(seg, ofs) ((void * ) \ 5 : #define (((unsigned long) (seg) くく 16 ) 4 ) (*((char ( * ( (char far * ) far * ) (unsigned)(ofs))) (unsigned)(ofs))) 能 List 24 のライプラリの機能概路 70 C MAGAZINE 1991 11 SEGof(s) OFFof(s) ライプラリ名 機 ポインタ s のオフセット値を得る ポインタ s のセグメント値を得る OFFof, SEGof は nea 「ホインタ , fa 「ポインタの両方に対して使用できる ていますから , 少し詳しく見ていきましょ Tu 「 bo ( 1 ~ 3 行目 ) Turbo て、は疑似レジスタ変数がサポートさ れていますのて、 , もっとも簡単に調査する ことがて、きます。生成されるコードは非常 にコンパクトなものになります。 LS Ⅱ 4 ~ 7 行目 ) LSI て、は , アセンプリインライン関数がサ ポートされていますのて , その機能を利用 することより , 簡単に記述することがて、き ます。生成されるコードはコンパクトなも

5. 月刊 C MAGAZINE 1991年11月号

明解 AWC 言ロロ 入門講座 行われることはありません。 文字列の長さを求める List ポインタをくボインタらしく > うまく 用いることによって効率のよいプログ ラムを記述することができる 文字列の長さを求める 前回作成した文字列の長さを求める関数 も , ポインタを < ポインタらしく > 使用し て書き換えましよう。プログラムは List 5 の ようになります。詳しい解説は省略します。 プログラムを解読してください 【問題 2 】文字列の連結を行う関数ー char * strconcat (char * S, const char *t) をポインタをポインタらしく使って作 成せよ。 1 : #include く stddef. h> 2 : 3 : s ize—t strlength(const char *S) s ize t len = 0 ; 5 : 6 : while ( * s + + ) 7 : 8 : len 十十 ; return(len) ; 10 : } 文字列の配列 ( 2 元配列 ) List 1 : #include く stdio. h> / * 文字列の個数 * / 3 : #define MAX 3 4 : #define LEN / * 文字列の長さ * / 6 5 : 6 : void print(char PC]CLEN], int n) if (n 〉ニ 0 & & n く MAX) 8 : printf("%s*n", p[n]) ; 9 : ミ 10 : } 11 : 12 : int main(void) ミ 13 : { 14 : int i; char sC]CLEN] = { ” Turbo" 15 : 16 : for (i = 0 ; i く MAX; i + + ) 17 : print(), i); 18 : return( の ; 19 : 列の配列 文字列は , それ自体が一種の「配列』て、 [ ] [LEN] Char S す。その文字列の配列を使う必要があるこ て、は , 二元配列の宣言・初期化を行ってい ともあるて、しよう。文字列は配列とポイン void func(char (x) ・ ます。配列の大きさは , 初期化子の個数か タのふたつの方法て、表現て、きますから , 文 という意味を持ちました。多元配列の場合 ら自動的に決定されることは説明しました。 字列の配列は , 「配列の配列」と「ポインタの はどうて、しよう。たとえば二元配列 こて、は初期化子が三つありますから , 配列」のふたつの方法て、表現て、きます。 char s [ 3 ] [ 6 ] ; char s [ 3 ] [LEN] ニ元配列による実現 を考えましよう。二元配列は「配列」を要素 と解釈されます。 とする配列て、したね。てすからこの配列は , こて、 Turbo, MS, LSI の三つの文字列 多元配列の受け渡し , s [ 0 ] [ 幻 Fig. 5 からもわかるように の配列を実現する方法を考えます。 といった大きさ 3 の配列て、あると考えられま 6 ~ 10 行の print 関数は , 指定された n に応 第 6 回 ( 9 月号 ) て、説明したように , 配列の す。その配列の「先頭要素』とは s [ 0 ] のこ じて文字列を表示します。 0 ならば Turb0' 配列は二元配列てす。もっとも長い文字列 とて、あり , それは「大きさ 6 の配列』て、す。 1 ならば MS, 2 て、あれば LSI と表示しま Turbo は \ 0 を含めると 6 文字て、す。したがっ 「配列の先頭要素へのポインタ」は『大き す。 て , 3 x 6 の大きさを持つ配列を用意しなけ さ 6 の配列ぞある s [ 0 ] へのポインタ」とい 関数の引数としての配列の受け渡しは , ればなりません。 List 6 を見てください。ま うことになり , List 6 の その配列の先頭要素へのポインタという形 ず 3 , 4 行目て、文字列の個数を表す MAX と void print(char P [ ] [ 6 ] , int n) て、行いました。たとえば文字の配列を受け 文字列の長さを表す LEN をマクロて、定義し は ( リスト中て、は 6 て、はなく LEN と書いてい 取る関数 ています。 15 行目の void func(char x [ ] ) ; は 明解 AN C 言語入門講座 127

6. 月刊 C MAGAZINE 1991年11月号

Fig. 2 文字列と整数 文字列へのポインタ 整数テータ 文字列へのポインタ 整数テータ 文字列 st 「 uct sdata { char *string; int 一 data; ) stack—bufCSlZEJ; int sp = O;/*StackPointer*/ 文字列へのポインタ 整数テータ 文字列へのポインタ けません。 よって構造化することにより , プログラム デタ構造の決定 構造体としてひとつのデータ構造を定義 の拡張性・可読性・保守性などを高めてい し , 構造体の配列の形て、スタックの領域を きます。 , こて、 , 管理されるデータが整数だけて、 確保するように設計します。また , スタッ ュールの設計 ある場合は , 整数型の配列を用いればスタ クポインタは , データを代入する位置を示 ックを実現することがて、きます。また , 文 すために配列の添字を値とするようにしま 字列だけて、あれば , char 型の配列を用いれ す。 具体的には , Fig. 3 に示すように push, p ばよいということになります。しかし , 今 叩のふたつの関数をスタック操作するモジ グラム構造の決定 回の問題はく文字列と整数 > がペアになっ ュールの入口とするモジュール構成て、設計 たデータを扱うことを考慮しなければなり しています。そして , その push, pop の関数 ません。したがって , く文字列と整数 > を 次に , プログラムのアルゴリズムを決定 を利用して , 与えられた問題を解決するた まとめて管理するためには , 整数型と char します。ここて、はふたつのモジュールから めのモジュールを設計します。 型のふたつの配列によってスタックを実現 なるプログラム構造を考えてみました。ひ しかし , このような設計を行ったとして する必要があります。 とつは , 間題を解決するためのアルゴリズ も , 実際にプログラムを作成する際には , さらに , 配列て、は管理てきるデータ数が ム。つまりスタックを利用してシステムの 問題解決モジュールからスタックのデータ 限れられてしまうため , その領域を動的に 目的を実現するモジュールて、す。これを問 を直接操作してしまう可能性があります。 確保する方法なども検討しなければなりま 題解決モジュールとします。そしてもうひ なぜなら , スタックを実現しているのは構 せん。扱うデータの構造を決定するととも とつは , 実際にデータ構造の操作を行うス 造体の配列なのて、 , push, p 叩関数を用いな に , そのスタック自体を , どのようにして タックを実現し問題を解決するモジュール くても先頭アドレスさえわかればメンバに 実現するかといったことまても決定しなけ を支援するアルゴリズム , いわば部品とな 対して直接アクセスてきるからて、す。 るようなモジュールて、す。ここて、は , これ ればならないのて、す。 もし , そのようなプログラムを作成して をスタック実現モジュールと称します。 べアになったデータを扱うには , 配列よ しまった場合のデバッグ時などて、は , いつ りも構造体を用いるほうが適切てす。 Fig. 2 システムの設計段階て、は , このふたつの スタックが操作されているかが不明確とな モジュールを同時に設計することになりま に示すような構造体のメンバとスタックと り , 非常に可読性の悪いプログラムとなっ しての領域確保の方法を決定しなければい す。このように , プログラムをその目的に てしまいます。また , スタックの領域を配 134 C MAGAZINE 1991 11

7. 月刊 C MAGAZINE 1991年11月号

ング添削 プログラ の必要はないて、しよう。 「正しく動作するじゃないて、すか ! 」とい って終わることがて、きればよいのて、すが , そうは問屋が卸しません ( 誉 ; ) 。 ■同じ綴りの文字列リテラル List 3 を見てください。 ma ⅲ関数中て、 , ポインタ ptr を宣言し , 文字列リテラル ' ' AS DF " て、初期化しています。この宣言により p tr はメモリ上の " ASDF " を指すことになりま す。ここて、 f00 関数の " ASDF " と , main 関数 の ' ' ASDF " が同じ綴りて、あることに注意して まったく同じ綴り ください。このように を持つ文字列リテラルが複数ある場合 , 別々 のメモリ上に確保するのはもったいなく , ひとつにまとめれば省メモリになります。 もし同じ綴りの文字列リテラルをまとめて しまえば , ptr の指すのは f00 関数の中の文字 列リテラルとなります。 List 3 の 13 行目て、 , きます。 ANSI て、は『文字列リテラルを変更 値などと訳されることも多いようてすが , * ptr するような書き込み操作の結果がどうなる 左値のほうがニュアンスとしてはより正確 との代入を行っていますから , "ASDF" は かはく基本的には > 定義されない』ことに て、す。厳密な定義は難しいのて、 , 簡単にい "XSDF" に変更されます。したがって , f00 うと代入式の左辺に書けるものとて、も理解 関数の返す値は "XSDF" へのポインタとな なっています。 しましよう ( だったらく左辺値 > て、もいいじ ります (Fig. 3 (a) ) 。もし同じ綴りを持っ文 本来 K & R て、は , 文字列の書き換えを推奨 ゃないといわれそうて、すが , 厳密な定義を するような風潮て、した。書き換え不可とい 字列をひとまとめにしなければ , f00 関数は 始めると , 後のべージを全部喰ってしまい う処理系はあまりないようて、す。実際 ANS "ASDF" を返します (Fig. 3(b))0 I て、も「同じ綴りの文字列を区別する処理系 ANSI< は , 「同じ綴りの文字列を同じも そうなのて、・・・・・・ ) 。 List 4 を見てください。 x 関数の中に xx と のとみなすかどうかは処理系によって違う』 であれば , 文字列リテラルの書き換えはで いう静的記憶寿命を持っオプジェクトがあ きるかもしれない」といった定義が行われ と定義しています。 ります。 xx は関数の呼び出しに関わらず , ています。このように , 文字列リテラルの 値を保持します。 x 関数は xx へのポインタを 書き換えは可能て、あるかもしれないという 返します。すなわち関数呼び出し式 , 同じ綴りの文字列を同じものとみなす こととも関連して , 最近は文字列定数とい かどうかは処理系によって異なる う言葉は使われなくなっています。 は int へのポインタの型を持ちます。ポイン ちなみにパソコン用の処理系ては , コン タに * 演算子を適用すると , そのポインタ 文字列リテラルは定数とは限らない パイルオプションやメニューなどによって , の指す実体を表すことになります。したが 選択てきるものが多いようてす。 (a) のよう って , 12 行目の printf の呼び出して、は , xx の な結果も (b) のような結果も出せます。 値て、ある 1 と表示されます。 ーポインタと左値 そして 14 行目てはなんと関数呼び出しを こて、「文字列リテラルは , そもそも定 左辺に書いています。「こんなのあり ? 』と 数のようなものて、ある。このような書き込 思う人もいるてしようが , 関数呼び出して 左値とは lvalue のことてす。ちなみに左辺 みを行ってよいのか』という疑問がわいて プログラミング添削 143 ホインタと左値 List 同じ綴りの文字列リテラル List 1 : #include く stdio. h 〉 2 : #include く string. h> 3 : 4 : char *foo(void) return( ” ASDF") ; 6 : 8 : 9 : int main(void) char *Ptr 12 : 13 : *ptr 14 : printf( ” %s}n", f00 ( ) ) : return( の ; 16 : 、ノ、 0 ・ 0 Ⅳん 0 Ⅳん、ノ ・ 1 & 、ノ 1 より朝っ 0 -4- 戸 0 ワー 8 0 》 0 1 より 00 4- 戸 0 れ 0 ー -8 く stdio. h> ” ASDF ”・ Fig. 4 x と * x ( ) Fig. 3 List 3 の実行結果 ASDF ( b ) 処理系 B XSDF ( a ) 処理系 A

8. 月刊 C MAGAZINE 1991年11月号

明解 IC 言語 入門講座 格納されます。実行例からもわかるように トダウン ( 「秒読み』の時みたいに 3 , 2 , 1 , となっていますのて、 , 表示後 argv はインク argvC0] の指す先頭の引数にはプログラム 0 とする ) することによってループを組んて、 リメントされます。インクリメント後は ar 名が格納されます。最後のポインタて、ある います。 Fig. 10 のように書けばわかるて、し gv は②を指します。 * argv は②の工イリア argv [ 3 ] には空ポインタ (null pointer) が スとなります。あとは同様にして , 引数を 格納されます。空ポインタとは , 通常のオ 最初 argv は①を指すポインタて、あり , * 次々に表示していきます。 プジェクトへのポインタなどとはっきり区 演算子を適用した * argv は①の工イリアス List 9 て、は printf を用いて , 文字列をいっ 別のて、きる値を持つポインタのことて、 , 通 となります。①は LIST8. EXE を指すポイン べんに表示しました。今度は 1 文字ずつくな 常は 0 の値を持っポインタのことて、すにの タて、す。 printf の引数として * argv を渡し , ぞって > いきながら putchar て、表示してみま ことを逆に考えると 0 番地には変数 , すなわ LIST8. EXE と表示されます。 8 行目は , しよう。 List 10 のようなプログラムとなり ちオプジェクトは格納されないということ printf("%d . %s*n", i 十十 , ます。 がわかります ) 。 *argv 十十 ) ; 12 行目の argv 十十は List 9 の printf の中て、 空ポインタを表すマクロは , <stdio. h> な どのヘッダて、定義されています。処理系に コマンドライン引数の表示 ( 第 2 版 ) よって異なりますが , たとえば , #define NULL (void * ) 0 のように定義されています。空ポインタに 関する具体的なことは , 別の機会に解説し ます。 argv はポインタへのポインタて、すから , 8 行目のように argv [i] と記述て、きることは わかりますね (List 7 の print 関数とまったく 同じて、す ) 。 List 8 のプログラムをポインタらしく記述 したのが List 9 てす。ここては argc をカウン コマンドライン引数と実行環境 MS-DOS の Ver. 3 より前のバージョンで は , プログラムが自分自身の名前を知る 方法を提供していません。したがって , A > LlST8 XYZ VSOPO 0 1 : XYZ 2 : VSOP のような実行結果となります。 ANSI で は , このようにプログラム名を得ること ができない場合は , argv [ 0 ] は判を指 すことになっています ( 判は文字列の 終わりの印でしたね ) 。すなわち argv [ 0 ] [ 0 ] は \ 0 となります。 LiSt 1 : #include く stdio. h> 3 : int main(int argc, char *argv[]) int i while (argc--) printf("%d : %s}n" 8 : return( の ; 10 : } i 十十 , *argv 十十 ) ; Fig. 10 コマンドライン引数を指すポインタの更新 argc 3 2 1 0 ①②③④ ①②③④ ①②③④ ar a rgv ①の工イリアス = ”凵 ST8. EXE ”を指す ポインタ ①を指すポインタ ②を指すポインタ → ③を指すポインタ * a rgv ②の工イリアス = ” XYZ ”を指す ポインタ → ③の工イリアス = ” VSOP ”を指す ポインタ 明解 ANSI C 言語入門講座 131

9. 月刊 C MAGAZINE 1991年11月号

文字列の連結 LiSt 三つの文字列 ( 配列 / ポインタ / リテラル ) LiSt 1 : #include く stdio. h 〉 3 : int main(void) char strl ロ ” ABC ”・ 5 : ” ABC ”・ 6 : char *str2 7 : puts ( ” ABC ” ) ; 8 : puts(strl) : 9 : puts(str2) ; 10 : return( の ; 11 : 12 : } 1 : #include く stdio. h> 2 : #include く string. h> 3 : 4 : int main(void) char str1C10] 6 : 7 : char *str2 8 : puts(strcat(strl, 9 : 10 : puts (strcat (str2, return(0) ; 12 : } / * 配列 * / / * ポインタ * / / * 文字列リテラル * / ” ABC ”・ / * 配列 * / ” ABC ”・ / * ポインタ * / 1 は先頭の文字 str [ 0 ] すなわち A へのポイ れます。 このように図示すると , これらふたつは ンタて、す。 str2 はもともとポインタて、あり , 自動記憶寿命を持つ配列の初期化 根本的に違うことがはっきりします。時々 どこかに格納されている ABC の最初の文字 聞かれる『配列もポインタもほとんど同じよ A へのポインタて、す。ポインタに [ ] 演算 前回までに説明したように AN 引準拠で なけま自動記憶寿命を持つ配列 ( 関数の うなもの』などにはだまされないようにしま 子を適用して , 配列のようにアクセスが可 中で static を指定せずに定義するもの ) の 能となりました。配列 strl とポインタ str2 の 初期化を行うことはできません。 さて前回解説したように 両方に対して , 2 番目の文字 B は strl [ 1 ] , 掲載リストのプログラムを正しくコン str2 [ 1 ] のようにまったく同じような感じ ” DEF ” strl パイルできない処理系を使用しているの のような代入は , 文法的に正しくありませ て、アクセスて、きます。 であれば配列の宣言は , ん ( C 言語て、は配列に対して要素の一括代入 static char strl [ ] このように strl, str2 は , 配列とポインタ のように static を指定する要があります。 といった操作を行うことはて、きません ) 。し という根本的な違いがあるものの , ほとん 一方 , 配列でない通常の自動記憶寿命 かし , ポインタ str2 に対して ど同じように文字列を表せるという共通点 を持つオプジェクトの初期化は AN 以前 があります。 str2 ” DEF ” からも可能でした。したがって , ポイン の代入は可能て、す。この代入により str2 はメ タの宣言・初期化である ポインタ使用上の注意 モリ上のどこかに格納されている DEF を指 char * str2 は , AN 引準拠でない処理系でも static を指 すように変更されます。文字列の複写を行 ポインタを使って文字列を表すときに注 定せずに正しくコンパイルできるはずで 意しなければならないことがあります。 っているわけて、はないことに注意してくだ す。 前回説明したように , 文字列の連結は標 準ライプラリて、ある strcat を呼び出せば簡単 strl は固定的な領域を持つ配列の名前て、あ ほかのオプジェクトや , 関数 , さらにはプ に実現て、きます 0List 2 が文字列の連結を行 り , str2 は単なるポインタてす。ポインタの ログラムの大事な部分が格納されている可 代入により , いろいろな文字を指すように うプログラムて、す。 能性があります。そのような領域に書き込 変更てきます。 strl は配列て、す。 str2 はポインタて、 , メモ みを行ってよいはずはありません。最悪の リ上のどこかに格納されている ABC を指す 場合 , 暴走も考えられます。 ように初期化されます。 Fig. 2 を見てください。配列 strl に対する 連結はわかるて、しよう。前回説明したよう に , 連結後のことを考えて配列の大きさは 10 としています。ポインタ str2 に対する連結 はどうて、しようか。 ABC の最後の文字て、あ る \ 0 の位置から連結を行います。しかし , こて、よく考えてください。この領域は , 配列でもポインタでも , 扱う文字列を 十分に格納できる大きさを持つように あらかじめ定義しなければならない ポインタを使って文字列を表すときは , とくにこのようなミスを犯しがちなのて、注 char strl [ ]="ABC ”と char *str2= "ABC" ; はまったく違う もう何度も説明したように , 配列の名前 が単独て、現れると , その配列の先頭要素へ のポインタと解釈されます。この場合は str 124 C MAGAZINE 1991 11

10. 月刊 C MAGAZINE 1991年11月号

明解 IC 言語 入門講座 のインクリメントと同じて、すから何とかわ かるて、しよう。難しいのは 10 , 11 行の whil e 文の部分て、す。 argv に対して * や十十がた くさん適用されています。分解すると putchar(c) ・ 十十 * argv ; となります。 *argv は LIST8. EXE ( の先 頭文字 ) へのポインタて、す。ポインタに対し て * 演算子を適用すると , そのポインタの 指すオプジェクトを表します。よって , * *argv は L を意味します。 * argv を P と置き換えてみましよう。 P は 文字へのポインタの型を持ちます。そうす ると この図は通常の文字列と , そ に表せます。 字列を表示したら 13 行目て、 while ( (c れを指すポインタの図て、す。たとえば十十 putchar('*n' ) ; putchar(c) ・ P のようにインクリメントすると , P は次の と改行を行います。 12 行目は 十十 P ; 文字て、ある I を指すようになります。したが argv 十十 , とインクリメントを行いますのて、 , P は Fig. って , このループは \ 0 が出現するまて、 , L, となります。最初の状態は Fig. 11 ( a ) のよう 11 (b) のように②の別名となり , XYZ を指 ・ , E と 1 文字ずっ表示します。文 こまて、説明すればも すようになります。 Fig. 1 1 コマンドライン引数とそれを指すポインタ う後はわかるて、しよう。 コマンドライン引数の表示 ( 第 3 版 ) List 1 : #include く stdio. h> 2 : 3 : int main(int argc, char *argvCJ ) 5 : 6 : 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : } をいプいを」いいいにい一を」いンいンー int i char c; while (argc--) { printf("%d ・ i 十十 ) ; while (c putchar(c) ; argv 十十 ; putchar (' }n' ) ; return( の ; プをい 1 い霧を ポインタへのポインタなどの複雑なプ ログラムの解読には , 適当な置き換え を行おう ただし , 演算子の優先中や結合規則な どに注意せよ ①②③④ ①②③④ —l ー U) 8 . X 0 X >- 2 回にわたって文字列について解説しまし た。今月号の「 CMAGA セミナールーム」 て、もうチョット難しい話題を取り上げてい ます。ぜひそちらもあわせて読んて、くださ い ( 10 ページ後ろて、すよ ) 。 さて次回はお待ちかね , ポインタと並ぶ 難関と呼ばれるく構造体 > て、す。 \ 0 \ 0 (a) 132 C MAGAZINE 1991 11