【雑談?】ポインタのポインタについて

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

【雑談?】ポインタのポインタについて

#1

投稿記事 by march3 » 16年前

最近このサイトにハマっています。
詳しい情報がたくさん得られるので、
今特に困っていることではないのですが、私の疑問の解決にご助力いただけると幸いです。
#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);
}
このプログラムはコンパイルが通らないのですが、
なぜそういう仕様になっているのか疑問じゃありません?

気が向いたらでいいので返信くれるとうれしいです。

MNS

Re:【雑談?】ポインタのポインタについて

#2

投稿記事 by MNS » 16年前

**p はポインタのアドレスを入れるものであって、
***p は'ポインタのポインタ'のアドレスを入れるものであるからではないでしょうか?

たぶん、ポインタのアドレスと、ポインタのポインタのアドレスは明確に区別されていると思います。
そうでないと、「ポインタ」と「ポインタのポインタ」でも同じことが言えると思いませんか?

あくまで私の考えですが・・、
(こういうことが聞きたかったのではなければ、聞き流してください;)

toyo

Re:【雑談?】ポインタのポインタについて

#3

投稿記事 by toyo » 16年前

キャストすればコンパイルは通ります
#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);
}

toyo

Re:【雑談?】ポインタのポインタについて

#4

投稿記事 by toyo » 16年前

たまたまintとポインタのサイズが同じだからint型をint*型にできただけですが

toyo

Re:【雑談?】ポインタのポインタについて

#5

投稿記事 by toyo » 16年前

ポインタの段階でキャストすればいいのか
#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);
}

box

Re:【雑談?】ポインタのポインタについて

#6

投稿記事 by box » 16年前

>     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 **で、合っていません。
よって、コンパイルエラーが出ます。

> このプログラムはコンパイルが通らないのですが、
> なぜそういう仕様になっているのか疑問じゃありません?

全く疑問に思いません。

march3

Re:【雑談?】ポインタのポインタについて

#7

投稿記事 by march3 » 16年前

MNSさん
>「ポインタ」と「ポインタのポインタ」でも同じことが言えると思いませんか?
char a;
char *p1, **p2

p1 = &a;
p2 = &p1;
この場合でポインタ演算すると
p1+1 は +1Byte
p2+1 は +4Byte
のように加算される値が異なりますよね。

「ポインタのポインタ」と「変数のポインタ」は、指している先のサイズによって区別する必要があると思いませんか。
でも「ポインタのポインタ」と「ポインタのポインタのポインタ」は
ポインタのサイズが決まっていて、そのアドレスを入れるので、ここに区別は無くてもいいように思ってしまうんですよね。
(言ってることわかんなかったらごめんなさい)


toyoさん

キャストする方法は思いつきませんでした。
でもややこしすぎて実践では絶対に使いたくないですね。

MNS

Re:【雑談?】ポインタのポインタについて

#8

投稿記事 by MNS » 16年前

それは単純に、
「ポインタのポインタ」と「ポインタのポインタのポインタ」が
区別できなくては、都合が悪いからではないからでしょうか?

少なくとも私は、
「ポインタのポインタのポインタ」に、「ポインタのアドレス」が代入できてしまうのは困ります。

march3

Re:【雑談?】ポインタのポインタについて

#9

投稿記事 by march3 » 16年前

boxさん

まぁ「そういうもんなんだ」といわれればそこまでなんですが、
そこをあえてなぜなのか問うてみたかったわけでして。。。
具体的に「こういう場合に **p と ***p に区別がないと不便だよね」ってのがあるとわかりやすいんですが

toyo

Re:【雑談?】ポインタのポインタについて

#10

投稿記事 by toyo » 16年前

Cではポインタ同士の代入は自由にできますがキャストして型をあわせる必要があるということです
例えばmalloc()の戻り値はvoid*型でどのポインタ型にも代入できます
int***をint**に代入するのも問題ないと考えます(意味があるかどうかは別にして)

box

Re:【雑談?】ポインタのポインタについて

#11

投稿記事 by box » 16年前

> 具体的に「こういう場合に **p と ***p に区別がないと不便だよね」ってのがあるとわかりやすいんですが

お書きになった、コンパイルの通らないコードが、
まさにその「区別がないと不便(まずい)」例であると思います。


「何とか型」と「何とか型へのポインタ」、例えば
int型とint *型とが異なることは理解されているのですよね?
だとすると、同じ理屈で、

typedef int ** int_double_ptr;
int_double_ptr p, *q;

と書いたとき、pとqとは型が異なる、つまり

pは実際はint **型
qは実際はint ***型

で、両者は別物
ということも理解できるのではないでしょうか。

通りすがり

Re:【雑談?】ポインタのポインタについて

#12

投稿記事 by 通りすがり » 16年前

>具体的に「こういう場合に **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:【雑談?】ポインタのポインタについて

#13

投稿記事 by 通りすがり » 16年前

>上のコードは
>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;

}

march3

Re:【雑談?】ポインタのポインタについて

#14

投稿記事 by march3 » 16年前

>問題なのは、問題の部分までプログラムを実行してからでないとこれらのエラーに気づけないことです。
>コンパイルエラーだとすぐわかるので、効率的でしょう?

おぉ、なるほど。
コンパイルのときに気づけるか、実行してバグってからでないと気づけないかの違いですか。
確かに圧倒的な違いですね。納得です。

閉鎖

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