C言語関係掲示板

過去ログ

No681 while(*d++=*s++) ; で文字列がコピーできる理由

[戻る] [ホームページ]
No.7768

ポインタでの文字列コピー
投稿者---huji(2003/06/25 15:12:55)


ポインタの勉強中に分からないソースが出てきたので書き込みました。
まずソースを以下に載せます。

#include<stdio.h>
char *cp(char *d, char *s);
void main()
{
        char temp[100];
        char s1[100],s2[100],s3[100];

        printf("文字入力\n");
        scanf("%s",temp);

        cp(s1, cp(s2,temp));

        printf("s1=%s\ns2=%s\ns3=%s\n",s1,s2,cp(s3,temp));
}

char *cp(char *d, char *s)
{
        char *p=d;

        while(*d++=*s++)
                ;       //何故ここの処理でpが変わるのか?
        return (p);
}


関数cp内でのpが何故コピーされているのか分かりません。
sをdに代入していって、何でpが変化するのですか?
自分なりに考えたのはdをpに代入し、pのアドレスは変わらずにdのポインタがインクリメントされているから・・・
と考えていてもう頭が混乱してしまいました。
関数の返却する値がcharのポインタという部分もいまいち分からないです。
参考書を読んでもあまり詳しく書いてくれていないので、詳しく教えていただきたいです。
よろしくお願いします。
(目的はこのソースの理解で、文字列のコピーではありません。)


No.7773

Re:ポインタでの文字列コピー
投稿者---こん!(2003/06/25 15:35:56)


>関数cp内でのpが何故コピーされているのか分かりません。
>sをdに代入していって、何でpが変化するのですか?

pがコピーされるという意味が分かりませんが?
またpが変化するとは?

見たとこcp()内ではpに対して第一引数のポインタの値を代入し終了時にそのま
ま戻り値として返しているだけの様ですが。

>自分なりに考えたのはdをpに代入し、pのアドレスは変わらずにdのポインタがインクリメントされているから・・・

そこまではあっていますがその後何か不審な点でも?

>関数の返却する値がcharのポインタという部分もいまいち分からないです。

分からないも何もご自分で言われているそのままですよ。
pの値をそのまま返しているだけです。

No.7776

Re:ポインタでの文字列コピー
投稿者---huji(2003/06/25 16:03:44)


レス有難うございます。

>見たとこcp()内ではpに対して第一引数のポインタの値を代入し終了時にそのまま戻り値として返しているだけの様ですが。

まずpにdを入れてますよね。その後にwhileの中でsをdに入れてますよね。
じゃあpには何の影響も出ないのではと思いまして。

>>自分なりに考えたのはdをpに代入し、pのアドレスは変わらずにdのポインタがインクリメントされているから・・・
>そこまではあっていますがその後何か不審な点でも?

この考え方があってるのかも分からなかったので教えていただいて助かりました。つまり*dが変化したらpも変化するということですか?

何故このような質問をしたかというと、このwhile文で回っている時においてpとdはどのような関連があり、何故pに文字列がコピーされているのかという部分で、デバッグを見ても参考書見てもしっくりこないし、しっかり理解できてないので、教えていただければと。
よろしくお願いします。


No.7778

Re:ポインタでの文字列コピー
投稿者---こん!(2003/06/25 16:30:06)


>まずpにdを入れてますよね。その後にwhileの中でsをdに入れてますよね。
>じゃあpには何の影響も出ないのではと思いまして。

出ません。

ちなみにsをdに入れているのではありません。sのポインタが指している先のメモ
リ上のデータをdのポインタが指しているメモリ上に代入しているのです。

>この考え方があってるのかも分からなかったので教えていただいて助かりました。つまり*dが変化したらpも変化するということですか?

しません。つまりの根拠を説明して頂けると誤解が解きやすいかもしれませんね。

>何故このような質問をしたかというと、このwhile文で回っている時においてpとdはどのような関連があり、何故pに文字列がコピーされているのかという部分

この辺の言葉尻を正確に使えるようになればポインタを正確に理解していると言
えるのかもしれません。

まずpはcp()に入ってきた時のdの値を維持します。dはwhile文においてインクリ
メントされていきます。ですから最初の代入がすんでしまえば後の関連は無いと
言ってもいいかもしれません。

また、pに文字列がコピーされているのではありません。何度も言いますがpはポ
インタ変数です。dはpの位置からアドレスを順次進めていき順次その位置にsが指
している先のデータを代入しているのです。

言葉で教えて分かるなら本を読んでも分かると思うのですが、理解したければ出
てきたリストを解析するより、本を読んだその情報を元に自分の理解の元リスト
を作られてデバッグで見るなりなんなりされた方がよろしいかと思います。

その方が解析などという余計な作業をせずとも元々の記述の目的が分かっている
のですからデバッグもしやすいのでは?

No.7779

Re:ポインタでの文字列コピー
投稿者---huji(2003/06/25 17:38:07)


お返事有難うございます。

>言葉で教えて分かるなら本を読んでも分かると思うのですが

dはpの位置からアドレスを進めていって、sの指している中身を代入しているという部分が、レスを頂いて分かってきました(ニュアンスがつかめてきました)。

>出てきたリストを解析するより、本を読んだその情報を元に自分の理解の元リスト
>を作られてデバッグで見るなりなんなりされた方がよろしいかと思います。

おっしゃる通りです。そうします。
自分のレベルと照らし合わせて地道に理解を深めていきます。
教えていただけて助かりました。有難うございました。

No.7780

Re:ポインタでの文字列コピー
投稿者---こん!(2003/06/25 17:48:05)


ついで・・・
メモリマップの概念
     |-----------------------------------------------------------------------------------|
     |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
     |   |   |   |   |   |   |   |   |  メ モ リ  |   |   |   |   |   |   |   |   |   |
     |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
     |-----------------------------------------------------------------------------------|
       |       |       |       |       |       |       |       |       |       |       |
  0x00000000   |  0x00000004   |  0x00000008   |  0x0000000c   |  0x00000010   |  0x00000014
       |  0x00000002      0x00000006      0x0000000a      0x0000000e      0x00000012   |
       |                                                                               |
       |-------------------------------------------------------------------------------|
                                               |
                                               |
                                  この値がアドレス(メモリの番地)
                                  この値を保持するのがポインタ変数

例えば

char    buff[10];
char*    pBuff;
と宣言したとする。
これがコンパイルされ、リンクされ、実行された時点でコンピュータのメモリ上の
どこかにマッピングされる。

それが例えばbuffの配列が0x00000000番地からだとする。(そんな事は絶対ありえないが)
さらにそのすぐ後にpBuffが割り当てられたとする。すぐ後だとすると0x0000000aから4バイト。
(ポインタ変数なので32ビットアプリケーションなら32ビット、つまり4バイト確保される。)

で、

    strcpy( buff, "876543210" );
    pBuff = &buff[3];

とかやると

     |------------------------------------------------------------------------------------|
     |   |   |   |   |   |   |   |   |  メ  モ  リ   |   |   |   |   |   |   |   |   |   |
     |   |   |   |   |   |   |   |   |   |    |   |   |   |   |   |   |   |   |   |   |   |
     |'8'|'7'|'6'|'5'|'4'|'3'|'2'|'1'|'0'|'\0'|00 |00 |00 |03 |   |   |   |   |   |   |   |
     |------------------------------------------------------------------------------------|
       |       |       |       |       |        |           |           |       |       |
       |       |       |       |       |        |           |           |       |       |
       |       |       |       |       |        |-----------|           |       |       |
    buff[0]    |    buff[4]    |    buff[8]           |            0x00000010   |  0x00000014
            buff[2]         buff[6]                pBuff                  0x00000012
 
                                  リトル/ビッグエンディアン全く無視 (^^ゞ
 
と、概念的にはこうなる事が理解出来ますか?

この時に
pBuffは0x00000003が入っているので
*pBuffというのは0x00000003が指す先のデータつまり'5'をあらわしています。

ちなみにここでよく聞くように
scanf()を使ってbuffに対して10バイト以上の文字を入力するとすぐ後ろのpBuffのメモリまで
つぶされてしまいます。これが為にバッファは余裕をもって確保する、又は入力する際にはバッファ
サイズ以上は入力出来ないようにしなければならないのです。

No.7781

Re:ポインタでの文字列コピー
投稿者---TDa(2003/06/25 20:39:46)


char *cp(char *d, char *s)
{
char *p=d;

while(*d++=*s++)
; //何故ここの処理でpが変わるのか?
return (p);
}

ちなみにこれを配列で書くと次のようになります。

char *cp(char *d, char *s)
{
int i;

while (d[i++] = s[i++])
;
return d;
}

配列の方が読みやすいと思うのですがどうでしょう。pははじめのdの値を
覚えておくためだけのものです。


No.7784

Re:ポインタでの文字列コピー
投稿者---こん!(2003/06/25 21:29:32)


>>配列の方が読みやすいと思うのですがどうでしょう。pははじめのdの値を
>覚えておくためだけのものです。

今さらなのですが、自分で組んだものでなくどこからか持ってきたリストの解析
という趣旨でしたので、別にこのリストをどうこうしようという気は無いのか
と・・・

ついでにiを初期化しておいた方が・・・

あれ?両方でインクリメントしちゃまずいような。

No.7786

Re:ポインタでの文字列コピー
投稿者---TDa(2003/06/25 22:58:22)



>今さらなのですが、自分で組んだものでなくどこからか持ってきたリストの解析
>という趣旨でしたので、別にこのリストをどうこうしようという気は無いのか
>と・・・
いやだからこの手のコードは配列で書いた方が読みやすいかとおもってのこと
だったのですが、、、


>
>ついでにiを初期化しておいた方が・・・
>あれ?両方でインクリメントしちゃまずいような。
脳内コンパイルはダメダメでした。
本質的には同等のコードです。

char *cp(char *d, char *s)
{
	int i = 0;

	while (d[i] = s[i])
		++i;
	return d;
}