29章 アドレスの計算

http://www.geocities.jp/ky_webid/c/029.html

アドレスを計算するとな。なにやら不穏な予感がします。

難解そうな文章が目に付いたのでここもじっくりゆっくり読み進めていきます。

ポインタの値を直接、加算することができます。例えば、+1する操作(インクリメント)は、

int *p = #
p++;

となります。間接参照演算子*を付けてインクリメントすると、変数numの値が+1されます。付けなければ、このようにポインタ変数に記憶されたアドレス自体を加算します。ところが、もし変数numのアドレスが「1000」であったとすると、ポインタをインクリメントした結果は「1004」になります(32ビット環境下)。

ぬぐぅ。移動したからどうなるっていうんだろう。

上記の状態だとpはいったい何処を指すことになるのか。わけがわからない。

ポインタと配列

ここの話は難解でしたが、読めば読むほど良く分かってきました。

今までの配列の扱いもかなりしっくりきます。

配列の名前だけを書くと、それは先頭要素のアドレスになる

これは特に重要なポイントですね。

また配列のポインタを一つ進めると、次の要素のポインタになるという部分もかなり重要です。

演算子の優先順位の問題

ここも迷いそうなのでしっかり覚えないといけないですね。

「*」よりも「++」の方が結合度が高いので

 *p++;

これアドレスを進めてから間接参照してることになります。

もし間接参照した値をインクリメントしたければ優先順位を明示的にすることです。

 (*p)++;

これでいけます。

問題1

次の2つが同じ結果になることを確認するプログラムを作って下さい。

printf( "%d\n", array[3] );
printf( "%d\n", *(p+3) );

さっそく作ってみる。

int main () {
    int array[] = {10,20,30,40};
    int *p;
    
    p = array;
    
    printf( "%d\n", array[3] );
    printf( "%d\n", *(p+3) );
    return 0;
}
40
40

ばっちりです。

問題2

80文字以内の文字列を入力し、それを配列に格納します。この文字列をポインタを使って、先頭から末尾まで1文字ずつ改行して出力するプログラムを作って下さい。

文字の入力ですね。fgetsを使います。

int main () {
    char str[81];
    char *p;
    
    fgets(str,81,stdin);
    
    p = str;
    
    while (*p != '\0') {
        printf("%c\n",*p);
        ++p;
    }
    return 0;
}

できました。インクリメントするだけでいいので添え字が必要な配列で扱うより楽ですね。

また、ここからもう少し短くできないか色々変更してみました。

    while (*p != '\0') {
        printf("%c\n",*p++);
    }

といってもインクリメントの位置を少し変えてみただけなのであまり変わってませんね。

答え合わせ

なるほどー。ここでもfor文ですか。確かに初期値と条件式と再評価式があるのでforにうってつけですね。

あと\0の評価の仕方についての情報がありました。

実際には'\0'は0と同じなので、for文の条件式は、

 *p!=0

ともでき、さらに0は「偽」を表すので、

 *p

とだけ書けばいいことになります。

な、なるほど。これは凄い便利ですね。

さっきのコードもこれに置き換えてみます。

int main () {
    char str[81];
    char *p;
    
    fgets(str,81,stdin);
    
    p = str;
    while (*p) {
        printf("%c\n",*p++);
    }
    return 0;
}
$ main
asdf
a
s
d
f

おー。ちゃんと動いてます。素晴らしいですね。

問題3

入力された80文字以内の文字列の文字数を、計算して表示するプログラムを作って下さい。文字数を取得するためにstrlen関数を使ってはいけません。ポインタ同士の減算をうまく使って下さい。

ポインタの減算を利用しろとのこと。

やってみます。

int main () {
    char str[81];
    char *p,*start;
    
    fgets(str,81,stdin);
    
    p = start = str;
    while(*p++) {}
    --p;
    
    printf("文字の長さは %d です",p - start);
    return 0;
}
$ main
aaaassss
文字の長さは 9 です

できました。8文字なのに9となるのは改行コードの分がカウントされてるからですね。

また、whileのところを

while(*p){
    ++p;
}

こう書くこともできます。

どちらの実装にしようか迷ったのですがなんとなく「*p++」と書きたかったので前者の方でいきましたw

答え合わせ

解答例の方では問題2と同様、for文を使って実装していますね。

また、何も処理をしないfor文を書く場合

for(p=str; *p; ++p)
    ;

という書き方がよく使われるようです。覚えておきましょう。