ページ 1 / 1
【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 18:49
by march3
最近このサイトにハマっています。
詳しい情報がたくさん得られるので、
今特に困っていることではないのですが、私の疑問の解決にご助力いただけると幸いです。
#include <stdio.h>
int main(void){
int a = 100;
int *p1,**p2,***p3;
p1 = &a;
p2 = &p1;
p3 = &p2;
printf("a = %d\n", ***p3);
return(0);
}
このプログラムで p3 が **p3 で実現できないのはなぜだと思われますか。
**p2 は「ポインタのアドレスを入れるもの」で
***p3 も本質的には「ポインタのアドレスを入れるもの」なので、
**p3で問題ないように思うのですが・・・言いたいこと伝わりますでしょうか。
#include <stdio.h>
int main(void){
int a = 100;
int *p1,**p2,**p3; //***p3 → **p3に変更
p1 = &a;
p2 = &p1;
p3 = &p2;
printf("a = %d\n", ***p3); //表示したいときだけ ***p3 で3つ先の値を意味させる
return(0);
}
このプログラムはコンパイルが通らないのですが、
なぜそういう仕様になっているのか疑問じゃありません?
気が向いたらでいいので返信くれるとうれしいです。
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 19:09
by MNS
**p はポインタのアドレスを入れるものであって、
***p は'ポインタのポインタ'のアドレスを入れるものであるからではないでしょうか?
たぶん、ポインタのアドレスと、ポインタのポインタのアドレスは明確に区別されていると思います。
そうでないと、「ポインタ」と「ポインタのポインタ」でも同じことが言えると思いませんか?
あくまで私の考えですが・・、
(こういうことが聞きたかったのではなければ、聞き流してください;)
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 19:12
by toyo
キャストすればコンパイルは通ります
#include <stdio.h>
int main(void){
int a = 100;
int *p1,**p2,**p3; //***p3 → **p3に変更
p1 = &a;
p2 = &p1;
p3 = (int**)&p2;
printf("a = %d\n", *(int*)(**p3)); //表示したいときだけ ***p3 で3つ先の値を意味させる
return(0);
}
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 19:20
by toyo
たまたまintとポインタのサイズが同じだからint型をint*型にできただけですが
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 19:54
by toyo
ポインタの段階でキャストすればいいのか
#include <stdio.h>
int main(void){
char a = 100;
char *p1,**p2,**p3; //***p3 → **p3に変更
p1 = &a;
p2 = &p1;
p3 = (char**)&p2;
printf("a = %d\n", **(char**)(*p3)); //表示したいときだけ ***p3 で3つ先の値を意味させる
return(0);
}
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 20:17
by box
> int a = 100;
> int *p1,**p2,**p3; //***p3 → **p3に変更
このとき、
> p1 = &a;
aはint型ですので、p1はint *型であることを要求します。
> p2 = &p1;
p1はint *型ですので、p2はint **型であることを要求します。
> p3 = &p2;
p2はint **型ですので、p3はint ***型であることを要求します。
ところが、p3の実際の型はint **で、合っていません。
よって、コンパイルエラーが出ます。
> このプログラムはコンパイルが通らないのですが、
> なぜそういう仕様になっているのか疑問じゃありません?
全く疑問に思いません。
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 20:25
by march3
MNSさん
>「ポインタ」と「ポインタのポインタ」でも同じことが言えると思いませんか?
char a;
char *p1, **p2
p1 = &a;
p2 = &p1;
この場合でポインタ演算すると
p1+1 は +1Byte
p2+1 は +4Byte
のように加算される値が異なりますよね。
「ポインタのポインタ」と「変数のポインタ」は、指している先のサイズによって区別する必要があると思いませんか。
でも「ポインタのポインタ」と「ポインタのポインタのポインタ」は
ポインタのサイズが決まっていて、そのアドレスを入れるので、ここに区別は無くてもいいように思ってしまうんですよね。
(言ってることわかんなかったらごめんなさい)
toyoさん
キャストする方法は思いつきませんでした。
でもややこしすぎて実践では絶対に使いたくないですね。
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 20:33
by MNS
それは単純に、
「ポインタのポインタ」と「ポインタのポインタのポインタ」が
区別できなくては、都合が悪いからではないからでしょうか?
少なくとも私は、
「ポインタのポインタのポインタ」に、「ポインタのアドレス」が代入できてしまうのは困ります。
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 20:35
by march3
boxさん
まぁ「そういうもんなんだ」といわれればそこまでなんですが、
そこをあえてなぜなのか問うてみたかったわけでして。。。
具体的に「こういう場合に **p と ***p に区別がないと不便だよね」ってのがあるとわかりやすいんですが
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 20:37
by toyo
Cではポインタ同士の代入は自由にできますがキャストして型をあわせる必要があるということです
例えばmalloc()の戻り値はvoid*型でどのポインタ型にも代入できます
int***をint**に代入するのも問題ないと考えます(意味があるかどうかは別にして)
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月24日(金) 21:02
by box
> 具体的に「こういう場合に **p と ***p に区別がないと不便だよね」ってのがあるとわかりやすいんですが
お書きになった、コンパイルの通らないコードが、
まさにその「区別がないと不便(まずい)」例であると思います。
「何とか型」と「何とか型へのポインタ」、例えば
int型とint *型とが異なることは理解されているのですよね?
だとすると、同じ理屈で、
typedef int ** int_double_ptr;
int_double_ptr p, *q;
と書いたとき、pとqとは型が異なる、つまり
pは実際はint **型
qは実際はint ***型
で、両者は別物
ということも理解できるのではないでしょうか。
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月26日(日) 04:27
by 通りすがり
>具体的に「こういう場合に **p と ***p に区別がないと不便だよね」ってのがあるとわかりやすいんですが
もしできたとしましょう。
-------------------------------------------------------------------------------------------------
int i;
int *p;
int *pp; //これはダブルポインタ用途
int *ppp; //これはトリプルポインタ用途
i = 10;
p = &i;
pp = &p;
ppp = &pp;
printf("%d", ***ppp); //これはよい
//1.ppを間違えてトリプルポインタとして扱って表示しようとした場合
printf("%d", ***pp); //『10』をintのメモリアドレスとして参照しようとして確実に停止する
//2.pppを間違えてポインタとして扱って表示しようとした場合
printf("%d", *ppp); //停止はしないけど&ppのメモリアドレスが表示されて意図した動作をしない
//3.pppを間違えてポインタとして扱って、配列番号として使おうとした場合
int i2;
int hairetsu[10];
i2 = hairetsu[*ppp]; //*pppは&ppのアドレスが入っている。とんでもない数値なのでまず停止
-------------------------------------------------------------------------------------------------
記述を間違えたときに致命的な問題がすぐに起こります。
問題なのは、問題の部分までプログラムを実行してからでないとこれらのエラーに気づけないことです。
コンパイルエラーだとすぐわかるので、効率的でしょう?
上のコードは
pp = (int *)&p;
などのように記述すればコンパイルが通るので、実際に動作を試してみるとよいかと。
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月26日(日) 05:17
by 通りすがり
>上のコードは
>pp = (int *)&p;
>などのように記述すればコンパイルが通るので、実際に動作を試してみるとよいかと。
書いておきます。
#include <stdio.h>
int main(){
int i;
int *p;
int *pp; //これはダブルポインタ用途
int *ppp; //これはトリプルポインタ用途
int i2;
int hairetsu[20];
i = 10;
p = &i;
pp = (int *)&p;
ppp = (int *)&pp;
printf("%d", ***(int ***)ppp); //これはよい
//1.ppを間違えてトリプルポインタとして扱って表示しようとした場合
printf("%d", ***(int ***)pp); //『10』をintのメモリアドレスとして参照しようとして停止
//2.pppを間違えてポインタとして扱って表示しようとした場合
printf("%d", *(int *)ppp); //停止はしないけど&ppのメモリアドレスが表示されて意味を成さない
//3.pppを間違えてポインタとして扱って、配列番号として使った場合
i2 = hairetsu[*ppp]; //*pppは&ppのアドレスが入っている。とんでもない数値なのでまず停止
return 0;
}
Re:【雑談?】ポインタのポインタについて
Posted: 2009年4月27日(月) 08:05
by march3
>問題なのは、問題の部分までプログラムを実行してからでないとこれらのエラーに気づけないことです。
>コンパイルエラーだとすぐわかるので、効率的でしょう?
おぉ、なるほど。
コンパイルのときに気づけるか、実行してバグってからでないと気づけないかの違いですか。
確かに圧倒的な違いですね。納得です。