サイトマップ / C言語講座出入り口総目次目次:ポインタ>ポインタと配列

青い直線

ポインタと配列

青い直線

[ポインタの新しい宣言法]←このソース→[文字列をコピー]

/* ポインタはアドレスを指す変数です。

ポインタと配列

int array[10];と、arrayという変数を宣言すると、10個のint型の連続した配列(メモリブロック)が確保されます。Cでは、配列を表す変数の名前は配列の先頭要素のアドレスになります

3番目の要素の値は、

    *(array + 2);        // ポインタで参照
    array[2];            // 配列の添字で参照

で、知ることができます。

Cでは、ポインタと配列は密接に関連します。しかし、ポインタと配列はイコールではありません。添字を使って配列の要素にアクセスするよりも、ポインタを使うほうがよりコンパクトなコードになり、実行速度も向上します。

ポインタも、普通の変数と同様に演算ができます。array + 2;では、arrayのサイズ(int型のサイズ)が2に乗算されてからarrayに加えられます。

arrayはarray[ ]の最初の要素を指していますが、ポインタではないので、その値を書き換えることはできません

array[10]というint型の配列があったとすると、int *pa;と宣言し、pa = array;とすることにより、paは配列の先頭のアドレスを指すようになります。pa++とインクリメントすると、paは配列の次の要素を指すようになります。

ソースプログラムの説明

今日は、ポインタを使って、ある文字列(char s1)の後ろに別の文字列 (char s2)を結合する関数StringCat( )を作ります。この関数は2つの char型のポインタを引数として取ります。2つ目の引数はconstを付けて宣言されているので、書き換えようとするとコンパイラがエラーを出します

これらのポインタは最初はそれぞれの文字列の先頭を指しています。

    void  StringCat(char *s1, const char *s2)
    {
        while (*s1 != '\0')      // ポインタの指す中身がヌル文字でなければ
            s1++;                // ポインタの指す位置をインクリメント

        while (*s2 != '\0') {    // ポインタの指す中身がヌル文字でなければ
            *s1 = *s2;           // 一文字連結
            s1++;                // ポインタの指す位置をインクリメント
            s2++;                //ポインタの指す位置をインクリメント
        }
        *s1 = '\0';              // 末尾にヌル文字を追加
    }

Cでは文字列はヌル文字('\0')で終わります。3行目で、ポインタが文字列の末尾を指しているかどうか調べます。末尾でなければ、ポインタの指している位置を1進めます。このwhile ( )ループを抜けた時、ポインタはs1の末尾の'\0'を指しています。

次のwhile ( )ループでは、s2を指すポインタが文字列の末尾を指しているかどうかを調べます。そうでなければ、7行目でs2の中身をs1の末尾に加えます。次いで、それぞれのポインタをインクリメントします。while ( )ループを抜けた時点では、文字列の末尾に'\0'は付いていないので、最後に'\0'を付け加えます。

次に、もう一つのStringCat2( )を紹介します。上記のコードはCのソースプログラムとしてはやや冗長です。簡潔な表現に改めたものが下記のコードです。ほとんど同じ動作をします。

最初のwhile ( )ループから抜けた時、s1は末尾の'\0'のひとつ先を指しているので、5行目でデクリメントします。この点が違う所です。

    void  StringCat2(char *s1, const char *s2)
    {    // s1 の指している中身がヌル文字でない間s1 の指している位置を進める
        while ( ( *s1++ ) != '\0' )
            ;
        s1--;                                    // 一つ先読みしたので戻す
        while (((*s1++) = (*s2++)) != '\0')      // s1の末尾にs2を連結
            ;
    }

6行目のwhile ( )ループのカッコ内を見てみましょう。

    ((*s1++) = (*s2++)) != '\0'

代入演算子(=)と関係演算子(!=)があります。演算の優先順位は'!='が'='よりも高いということに注意して下さい。カッコを省いて、

    (*s1++) = (*s2++) != '\0'

とすると、比較が先に行われてしまいます。コンパイラはこのようなチェックはしてくれません。文法的に正しければコンパイラのチェックを通ります。

重要:演算の優先順位は'!='が'='よりも高い。

StringCat( )と同等の標準ライブラリ関数に、strcat( )があります。

    #include  <string.h>
    char  *strcat(char *s1, const char *s2);

    例:strcat(s1, s2);

strcat( )は文字列s1の末尾に文字列s2を連結し、末尾に'\0'を書き込みます。今回自作した関数はあくまで学習のためなので、特別な事情がない限り標準ライブラリ関数を使いましょう。 */

/* ここからソースプログラム */

#include <stdio.h>

void  StringCat2(char *s1, const char *s2);
void main(void);

  /* 文字列 s1 に s2 を連結する */
void  StringCat2(char *s1, const char *s2)
{    /* s1 の指している中身がヌル文字でない間s1 の指している位置を進める */
    while ( ( *s1++ ) != '\0' )
        ;

    s1--;                                  /* 一つ先読みしたので戻す */

    while (((*s1++) = (*s2++)) != '\0')    /* s1 の末尾に s2 を連結 */
        ;
}

void main(void)
{
    char s1[80]  = "私たちは ";
    char s2[80] = "C 言語を学んでいます。";

    printf("連結前: s1 = %s\n", s1);
    printf("連結前: s2 = %s\n\n", s2);
    StringCat2(s1, s2);
    printf("連結後: s1 = %s\n", s1);
}

/* ここまでソースプログラム */

/* s1[ ]には充分なサイズを確保しておかなければなりません。末尾にs2[ ] をつないだ後、配列の要素を上回る文字列になってしまうと、他の変数の使っているメモリを破壊してシステムをクラッシュさせてしまう恐れがあります。Cではコンパイラのポインタに対するチェックが甘いので、充分注意して下さい。 */

[ポインタの新しい宣言法]←このソース→[文字列をコピー]

青い直線

/* (C) 2000- YFプロ. All Rights Reserved. */    提供:C言語講座−それ自体コンパイルできる教材を使った講座です−

青い直線

サイトマップ / C言語講座出入り口総目次目次:ポインタ>ポインタと配列