初歩的なポインタの間接代入だとはおもうんですが、、、、

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
C言語彷徨い人

初歩的なポインタの間接代入だとはおもうんですが、、、、

#1

投稿記事 by C言語彷徨い人 » 7年前

code

int i, j;
double d;
int *p, *q;

i = 2;
p = &i; /* 正しい */
q = p; /* 正しい */
j = *p; /* 正しい */
printf("%d %d %d %d\n", i, j, *p, *q);
/* 2 2 2 2 が表示される */
i = 5;
printf("%d %d %d %d\n", i, j, *p, *q);
/* 問 1: 何が表示されるか? */
*p = 7; /* *p には int 型の値が代入できる */
printf("%d %d %d %d\n", i, j, *p, *q);
/* 問 2: 何が表示されるか? */
q = &j;
printf("%d %d %d %d\n", i, j, *p, *q);
/* 問 3: 何が表示されるか? */


/code
の問1,2,3の解を
5555
5775
5777と思って実行したのですが異なるものが解のようです
どうか、考え方の流れを説明していただけませんでしょうか?
かなり初歩的なことだとは思うのですがどうかよろしくお願いします

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#2

投稿記事 by みけCAT » 7年前

syohotekinapoinntanokannsetudainyuury-20161204.png
データ
syohotekinapoinntanokannsetudainyuury-20161204.png (23.47 KiB) 閲覧数: 5540 回
図のような流れになるので、出力は

コード:

2 2 2 2
5 2 5 5
7 2 7 7
7 2 7 2
となるはずですね。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

C言語彷徨い人

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#3

投稿記事 by C言語彷徨い人 » 7年前

わかりやすいように表を作っていただいて、ありがとうございます
問1の部分ですが
j = *p;
により
pの値が5になっていることからj = 2からj = 5になるのではないかと思っています。
なぜjの値が変化しないのでしょうか?

akasann

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#4

投稿記事 by akasann » 7年前

正しい説明か分りませんが私なりに説明してみます。
先ず考え方としてポインタ型と区別しながら考えたほうが分りやすいのではないかと思いました。

コード:

             i = 2;
	p = &i; 
	q = p; 
	j = *p;
問1.
i,jについて
i=5 だけが代入されているため、i,j は 5,2 が出されています。ここで注意することとして、プログラムは上から順に読み込まれていきます。なので、ここでは j=*p が読み込まれていないので j はそのまま2ということになります。
ポインタ
 上記を見てみると、pの中身が i と同じで、また q の中身は p の中身と同じということになっています。そのため、p,q の中身は 5,5 と表示されます。
 ここで、i,j と比較してみます。ポインタはアドレス(住所みたいな感じ)なので、上記に書かれていることが成り立ちます。成り立つという言い方はあまりベストではないかもしれませんが・・
 言い方としてはアドレスが i と p と q が同じというほうが正しいと思います。
 結果として、5 2 5 5 となります。
    
問2.
i,jについて
*p=7 が代入されています。ここで、問1と違うことについて、説明してみます。プログラムは上から順に読み込まれますが、上記を見てみると、i と p のアドレスが同じため、i,j は 7,2 が出されています。
ポインタ
p の中身が 7 ということなので、これは、p,q の中身は 7,7 と表示されます。
結果として 7 2 7 7 ですね。

問3.
i,jについて
q=&j です。これは、i と j に関係ないので、問2と同じ値が出力されます。
ポインタ
q と j のアドレスが同じということなので、p,q の中身は 7,2 と表示されます。
結果として 7 2 7 2 です。

少し間違っていたらすみません。私のイメージはこんな感じです。

C6b14

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#5

投稿記事 by C6b14 » 7年前

何が表示されるか?

コード:

#include <stdio.h>

int main() {

	int i, j;
	double d;
	int *p, *q;
	int** pp=0;
	int** qq=0;

	i = 2;
	p = &i; /* 正しい */
	q = p; /* 正しい */
	j = *p; /* 正しい */
	pp = &p;
	qq = &q;
	j = *p; /* 正しい */
	printf("%d %d %d %d\n", i, j, *p, *q);
	printf("..%d %d %d %d\n", i, j, **pp, **qq);
	/* 2 2 2 2 が表示される */
	i = 5; p = &i; pp = &p; j = **pp;
	printf("%d %d %d %d\n", i, j, *p, *q);
	printf("..%d %d %d %d\n", i, j, **pp, **qq);

	
	/* 問 1: 何が表示されるか? */
	*p = 7; /* *p には int 型の値が代入できる */
	/* これは i = 7 と同義 */
	*&j = *p = **pp = 7; *&i = *q = 5; p = &j;
	printf("%d %d %d %d\n", i, j, *p, *q);
	printf("..%d %d %d %d\n", i, j, **pp, **qq);

	/* 問 2: 何が表示されるか? */
	q = &j;
	printf("%d %d %d %d\n", i, j, *p, *q);
	printf("..%d %d %d %d\n", i, j, **pp, **qq);
	/* 問 3: 何が表示されるか? */
	
	return 0;
}

C614b

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#6

投稿記事 by C614b » 7年前

”初歩的なポインターの間接代入”だとはいえません。プロでも理解が難しいでしょう。p=&i  としたら ”p は i に(一時的に)等しくなって*pを変えると i も変わっている” ということですが 機械語でみれば当たり前のことです。C からみると とてもわかりずらいです。

C6b14

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#7

投稿記事 by C6b14 » 7年前

ポインターはバグになりやすいので、まずポインターを確認しながらプログラムをします。(手間だけどバグになるよりはいいです)これをプログラムにたくさん書いて覚えました。

コード:

#include <stdio.h>
 
int main() {

    int i=123;
    int *p;

	p = &i;

	printf("アドレス=%d : 実体=%d\n\n", (int)p, *p);

    return 0;
}

アドレス=8388248 : 実体=123

C6b14

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#8

投稿記事 by C6b14 » 7年前

ポインターのポインターを考えるとさらに複雑です。慎重に考えない間違えます。

コード:

#include <stdio.h>
 
int main() {

    int i=123;
    int *p;

	p = &i;

	printf("i-アドレス=%d : i-実体=%d\n\n", (int)&i, i);

	printf("p-が指すアドレス=%d : p-が指すアドレスの実体=%d\n\n", (int)p, *p);

	printf("p-ポインターのアドレス=%d : p-ポインターの実体=%d\n\n", (int)&p, (int)p);
    return 0;
}
i-アドレス=7338868 : i-実体=123

p-が指すアドレス=7338868 : p-が指すアドレスの実体=123

p-ポインターのアドレス=7338856 : p-ポインターの実体=7338868

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#9

投稿記事 by YuO » 7年前

C言語彷徨い人 さんが書きました:問1の部分ですが
j = *p;
により
pの値が5になっていることからj = 2からj = 5になるのではないかと思っています。
なぜjの値が変化しないのでしょうか?
jには,*pの値が代入されるのであって,pへの参照が代入されるのではありません。
つまり,j = *p;を実行した時点での*pでの値である2がjに代入されます。
その後,iの値 (=*p, *qの値) が5に変化しても,jの値は*pとは無関係なので変化しません。


根本的に,プログラムにバグがあるような場合を除いて,オブジェクトの値はそのオブジェクトを変更しない限り変更されません。

今回のプログラムでは,名前付きのi, j, p, q, dというオブジェクトがあります。
iとjは独立した別のオブジェクトであるため,iの値を変更してもjの値は変更されません。
そして,

コード:

p = &i;
*p = 5;
のようなことをした場合,pにはiへのポインタという値が代入され,
*pはiという名前のついたオブジェクトそのものになります。
このため,*p = 5;によって,iというオブジェクトの値が変更されます。
先にも書いたとおり,iとjは独立した別のオブジェクトであるため,たとえ*pという形でiの値を変更してもjの値は変更されません。
オフトピック
ポインタ値に単項*演算子を適用すると,ポインタが示す先のオブジェクトになります。
また,オブジェクトは必要に応じてその値になります (代入演算子の右辺にあるときなど)。
C6b14 さんが書きました:

コード:

    int i=123;
    int *p;

	p = &i;

	printf("アドレス=%d : 実体=%d\n\n", (int)p, *p);
とりあえず,現状においてsizeof(int) == sizeof(int *)とかの前提を設ける時点でまともなプログラムではないです。
ポインタを読める値で出力したいのであればポインタをvoid *にキャストした上で%pを使うべきでしょう。
まあ,%pの出力結果は後にfscanf系で同一のポインタに戻せる事のみが条件であるため,fprintfのタイミングでの連番だろうがなんでもよいわけですが。

C6b14

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#10

投稿記事 by C6b14 » 7年前

ポインターと int が大きさが違うマシンがありましたか?。C言語はよくしりませんが普通は8進数とか16進数に直して表示はしますが。https://www.sgnet.co.jp/c/6-3.htmlたしかDxlibに資料があったと思うのであとで調べますね。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#11

投稿記事 by みけCAT » 7年前

C6b14 さんが書きました:ポインターと int が大きさが違うマシンがありましたか?
はい。LP64の環境では異なります。
テストプログラム

コード:

#include <stdio.h>

int main(void) {
    printf("sizeof(int) = %zu\n", sizeof(int));
    printf("sizeof(int*) = %zu\n", sizeof(int*));
    return 0;
}
Wandboxでの出力

コード:

sizeof(int) = 4
sizeof(int*) = 8
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#12

投稿記事 by みけCAT » 7年前

C6b14 さんが書きました:https://www.sgnet.co.jp/c/6-3.html
このページ(アーカイブ)に載っているプログラムは、printf()に間違ったデータを渡して未定義動作を起こす間違ったプログラムです。
Webに書かれている情報が全て正しいとは限りません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#13

投稿記事 by ISLe » 7年前

問題の解き方としてはNo.4の説明で概ねあっていると思います。


変数iを使うとき、iという名前が、その変数の「値を保持する場所」を差す何か、と1対1であり直結している。
だから、変数名を使っているあいだはその何かを意識する必要がない。
アドレス演算子を使うと、その変数の「「値を保持する場所」を差す何か」、を取り出すことができる。
その何かは、ポインタ(変数)に代入できる。
「「値を保持する場所」を差す何か」を持っているポインタ変数に間接参照演算子を使うと、「値を保持する場所」にアクセスする手段を得られる。

変数の場合「値を保持する場所」だけど、実体を伴う『定義』と置き換えれば、配列や関数などもそこらは同じ概念。
定義を差す何か、というものの存在を意識するのがとても大事。

ところが、とても大事なのに、なぜか正式名称がない。
ひとによって、アドレスと言ったり、ポインタ(値)と言ったり、アセンブラの概念を絡めたがったり。
自分でこれと決めて、他人の言ってることは、これのことだな、と文脈から理解できるように努めましょう。

C6b14

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#14

投稿記事 by C6b14 » 7年前

そうですか。一般的にはintとポインターがサイズが違えばマシン語上不利だと思うのですが。Dxlibでは32ビットマシンと64ビットマシンを見分けるのに「intのサイズ=ポインターのサイズ」で、intのサイズで見分けるという説明だったと思ったのですが見つからないのでコードをみると

コード:

public static int  MV1SetMaterialDrawAlphaTest( int  MHandle, int  MaterialIndex, int  Enable, int  Mode, int  Param)
		{
			if( System.IntPtr.Size == 4 )
			{
				return dx_MV1SetMaterialDrawAlphaTest_x86( MHandle , MaterialIndex , Enable , Mode , Param );
			}
			else
			{
				return dx_MV1SetMaterialDrawAlphaTest_x64( MHandle , MaterialIndex , Enable , Mode , Param );
			}
		}
System.IntPtr.Size サイズので見分けているので違うようです。64 ビット版 Windows では、ポインタ型はすべて 8 バイト(32 ビット版 Windows のサイズの 2 倍)なのですが。
「Visual C++ の 32 ビットおよび 64 ビット コンパイラは、この記事の次の表に示す型を認識します。(MSDNたぶん機械翻訳です)
•int (unsigned``int)•__int8 (unsigned``__int8)•__int16 (unsigned``__int16)•__int32 (unsigned``__int32)•__int64 (unsigned``__int64)」でありintといえば4バイトらしいので 間違えた気がします。アセンブラで実際はどうなるか調べてみようと思いますが時間がかかると思います。

C言語彷徨い人

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#15

投稿記事 by C言語彷徨い人 » 7年前

akasannさんの説明、大変わかりやすかったです。
ですが疑問に思ったのが、問3でqのアドレスにjのアドレスを入れていますが、上の式でq = pのアドレスの条件から、pの値も2になるのではと思ってしまいました。
式の上から順に実行される条件を考慮しつつアドレスは条件式として参照、振り返るという認識で問1問2を解いていらっしゃったので同じように考えた場合、qも変わると考えたのですが、、
この考え方が間違っているのでしょうか?

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#16

投稿記事 by みけCAT » 7年前

C言語彷徨い人 さんが書きました:問3でqのアドレスにjのアドレスを入れていますが、上の式でq = pのアドレスの条件から、pの値も2になるのではと思ってしまいました。
「q = pのアドレスの条件」などというものはありません。
j = *p;があるからといって*p = 7;によりjの値が書き換わることは無いのと同様に、
q = p;があるからといってq = &j;によりpの値が書き換わることはありません。
C言語彷徨い人 さんが書きました:式の上から順に実行される条件を考慮しつつアドレスは条件式として参照、振り返るという認識で問1問2を解いていらっしゃったので同じように考えた場合、qも変わると考えたのですが、、
この考え方が間違っているのでしょうか?
「条件式」「振り返る」というのが意味がよくわからないので、間違っている可能性があります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

akasann

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#17

投稿記事 by akasann » 7年前

C言語彷徨い人 さんが書きました:akasannさんの説明、大変わかりやすかったです。
ですが疑問に思ったのが、問3でqのアドレスにjのアドレスを入れていますが、上の式でq = pのアドレスの条件から、pの値も2になるのではと思ってしまいました。
式の上から順に実行される条件を考慮しつつアドレスは条件式として参照、振り返るという認識で問1問2を解いていらっしゃったので同じように考えた場合、qも変わると考えたのですが、、
この考え方が間違っているのでしょうか?
説明になるか分りませんが、解答してみます。q=&j のとらえ方はおそらくですが、C言語彷徨い人さんのとらえ方であっていると思います。q のアドレスにj のアドレスを入れるみたいな感じでいいと思います。
わかんないですが、条件式として、とらえるのはやめたほうが良いのではないかと思います。プログラムは数学に近い考え方ではありますが、少し違います。
ここで、説明のための例えを出してみます。ご存知かもしれませんが int 型の a,b があったとして、a の値と b の値を入れ替える動作を行ってみたいと思います。式の上から順に実行される条件があることを忘れないでください。

コード:

int a=3,b=7;
a=b;
b=a;
というようなやり方はできません。a に b を入れてから、b に a を入れているからです。数字で見ていくとa に 7 を入れてから、b に 7 ( a に 7 を入れたため) を入れています。この結果として a,b ともに 7 となると思います。正しくはこんな感じです。

コード:

int a=3,b=7,temp;

temp=a;
a=b;
b=temp;
これだと、temp に a を最初に入れています。その後に a に b を入れ、次に b に temp を入れています。数字で見ていきます。最初に temp に 3 (a の 3)を入れて、次に a に 7 (b の 7)を入れて、最後の動作として b に 3 (最初に入れた temp の 3)を入れています。結果として a と b の値が入れ替わっていると思います。

ここで言いたかったのは、(左辺)に(右辺)を入れるみたいな考え方です。この考え方で問題をやってみます。問1 のほうで「アドレスが i と p と q が同じ」と言いましたが、正しくは p のアドレスに i のアドレスを入れ、q のアドレスに p のアドレスを入れたという感じです。

問3では q=&j は q のアドレスに j のアドレスを入れています。ここで忘れてはならないのは、上から順に実行されるということです。つまり、この問3の時点で 「q のアドレスは j のアドレスに変わったよ」みたいな考え方です。で、結果的に 7 2 7 2 となります。

何か間違った考え方であったらすみません。とりあえず、私はこんなイメージを持っています。

C6b14

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#18

投稿記事 by C6b14 » 7年前

言われてることは左辺値のことで数学とコンピューターの違いでその通りだと思います。http://detail.chiebukuro.yahoo.co.jp/qa ... 1248058362

かずま

Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、

#19

投稿記事 by かずま » 7年前

C言語彷徨い人 さんが書きました:どうか、考え方の流れを説明していただけませんでしょうか?

コード:

#include <stdio.h>

int main(void)
{
    int i, j;
    int *p, *q;

    i = 2;  // i の値は、2 になった
    p = &i; // p の値は、「i のアドレス」になった
    q = p;  // q の値は、p の値、すなわち「i のアドレス」になった
    j = *p; // j の値は、*p が i だから、2 になった
    printf("%d %d %d %d\n", i, j, *p, *q);
    // 2 2 2 2 が表示される。*p は i、*q は i だから。

    i = 5;  // i の値は、5 になった。
    printf("%d %d %d %d\n", i, j, *p, *q);
    /* 問 1: 何が表示されるか? */
    // 5 2 5 5 が表示される。*p は i、*q は i だから。

    *p = 7; // *p は i なので、i の値は 7 になった
    printf("%d %d %d %d\n", i, j, *p, *q);
    /* 問 2: 何が表示されるか? */
    // 7 2 7 7 が表示される。*p は i、*q は i だから。

    q = &j; // q の値は、「j のアドレス」になった
    printf("%d %d %d %d\n", i, j, *p, *q);
    /* 問 3: 何が表示されるか? */
    // 7 2 7 2 が表示される。*p は i、*q は j だから。

    return 0;
}
p の値が「i のアドレス」のとき、*p は i です。
C言語彷徨い人 さんが書きました: 問1の部分ですが
j = *p;
により
pの値が5になっていることから
p の値は 5 になりません。
p の値は「i のアドレス」になったあと、一度も変更されていません。
*p は i です。
*p = 5; により、i が 5 になります。
*p = 5; は、p の値を変更する文ではありません。
*p の値を変更する文です。

閉鎖

“C言語何でも質問掲示板” へ戻る