ページ 1 / 1
初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 00:11
by C言語彷徨い人
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と思って実行したのですが異なるものが解のようです
どうか、考え方の流れを説明していただけませんでしょうか?
かなり初歩的なことだとは思うのですがどうかよろしくお願いします
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 00:36
by みけCAT
- データ
- syohotekinapoinntanokannsetudainyuury-20161204.png (23.47 KiB) 閲覧数: 5566 回
図のような流れになるので、出力は
コード:
2 2 2 2
5 2 5 5
7 2 7 7
7 2 7 2
となるはずですね。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 00:59
by C言語彷徨い人
わかりやすいように表を作っていただいて、ありがとうございます
問1の部分ですが
j = *p;
により
pの値が5になっていることからj = 2からj = 5になるのではないかと思っています。
なぜjの値が変化しないのでしょうか?
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 01:14
by akasann
正しい説明か分りませんが私なりに説明してみます。
先ず考え方としてポインタ型と区別しながら考えたほうが分りやすいのではないかと思いました。
コード:
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 です。
少し間違っていたらすみません。私のイメージはこんな感じです。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 09:19
by C6b14
何が表示されるか?
コード:
#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;
}
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 11:22
by C614b
”初歩的なポインターの間接代入”だとはいえません。プロでも理解が難しいでしょう。p=&i としたら ”p は i に(一時的に)等しくなって*pを変えると i も変わっている” ということですが 機械語でみれば当たり前のことです。C からみると とてもわかりずらいです。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 12:17
by C6b14
ポインターはバグになりやすいので、まずポインターを確認しながらプログラムをします。(手間だけどバグになるよりはいいです)これをプログラムにたくさん書いて覚えました。
コード:
#include <stdio.h>
int main() {
int i=123;
int *p;
p = &i;
printf("アドレス=%d : 実体=%d\n\n", (int)p, *p);
return 0;
}
アドレス=8388248 : 実体=123
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 12:46
by C6b14
ポインターのポインターを考えるとさらに複雑です。慎重に考えない間違えます。
コード:
#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
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 13:52
by YuO
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は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のタイミングでの連番だろうがなんでもよいわけですが。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 14:12
by C6b14
ポインターと int が大きさが違うマシンがありましたか?。C言語はよくしりませんが普通は8進数とか16進数に直して表示はしますが。
https://www.sgnet.co.jp/c/6-3.htmlたしかDxlibに資料があったと思うのであとで調べますね。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 15:07
by みけCAT
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
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 15:11
by みけCAT
このページ(
アーカイブ)に載っているプログラムは、printf()に間違ったデータを渡して未定義動作を起こす間違ったプログラムです。
Webに書かれている情報が全て正しいとは限りません。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 15:58
by ISLe
問題の解き方としてはNo.4の説明で概ねあっていると思います。
変数iを使うとき、iという名前が、その変数の「値を保持する場所」を差す何か、と1対1であり直結している。
だから、変数名を使っているあいだはその何かを意識する必要がない。
アドレス演算子を使うと、その変数の「「値を保持する場所」を差す何か」、を取り出すことができる。
その何かは、ポインタ(変数)に代入できる。
「「値を保持する場所」を差す何か」を持っているポインタ変数に間接参照演算子を使うと、「値を保持する場所」にアクセスする手段を得られる。
変数の場合「値を保持する場所」だけど、実体を伴う『定義』と置き換えれば、配列や関数などもそこらは同じ概念。
定義を差す何か、というものの存在を意識するのがとても大事。
ところが、とても大事なのに、なぜか正式名称がない。
ひとによって、アドレスと言ったり、ポインタ(値)と言ったり、アセンブラの概念を絡めたがったり。
自分でこれと決めて、他人の言ってることは、これのことだな、と文脈から理解できるように努めましょう。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 16:25
by C6b14
そうですか。一般的には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バイトらしいので 間違えた気がします。アセンブラで実際はどうなるか調べてみようと思いますが時間がかかると思います。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 16:47
by C言語彷徨い人
akasannさんの説明、大変わかりやすかったです。
ですが疑問に思ったのが、問3でqのアドレスにjのアドレスを入れていますが、上の式でq = pのアドレスの条件から、pの値も2になるのではと思ってしまいました。
式の上から順に実行される条件を考慮しつつアドレスは条件式として参照、振り返るという認識で問1問2を解いていらっしゃったので同じように考えた場合、qも変わると考えたのですが、、
この考え方が間違っているのでしょうか?
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 17:08
by みけCAT
C言語彷徨い人 さんが書きました:問3でqのアドレスにjのアドレスを入れていますが、上の式でq = pのアドレスの条件から、pの値も2になるのではと思ってしまいました。
「q = pのアドレスの条件」などというものはありません。
j = *p;があるからといって*p = 7;によりjの値が書き換わることは無いのと同様に、
q = p;があるからといってq = &j;によりpの値が書き換わることはありません。
C言語彷徨い人 さんが書きました:式の上から順に実行される条件を考慮しつつアドレスは条件式として参照、振り返るという認識で問1問2を解いていらっしゃったので同じように考えた場合、qも変わると考えたのですが、、
この考え方が間違っているのでしょうか?
「条件式」「振り返る」というのが意味がよくわからないので、間違っている可能性があります。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 19:11
by akasann
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 となります。
何か間違った考え方であったらすみません。とりあえず、私はこんなイメージを持っています。
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月04日(日) 19:32
by C6b14
Re: 初歩的なポインタの間接代入だとはおもうんですが、、、、
Posted: 2016年12月06日(火) 18:36
by かずま
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 の値を変更する文です。