• ベストアンサー

C言語におけるポインターとconstの関係について

C言語について質問があります。 1. int **p; const int **p1 = p; //型に互換性がないという警告 2. int *p; const int *p1 = p; //OK 上記2パターンありますが、なぜ1のパターンだと警告がでて、2のパターンだと警告が出ないのでしょうか? コンパイラはgccの4系列と3系列で試してみましたが同じ結果になりました。 constで修飾されていても、変数の内容を変更できるかできないかだけで、型情報には影響を与えないと考えておりましたが、実際に警告がでてしまい、疑問に思っております どなたか分かる人がいましたら教えていただけないでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

JIS X3010の6.3.2.3 ポインタには、次のように記述されています。 「任意の型修飾子qに対して非q修飾型へのポインタは, その型のq修飾版へのポインタに型変換してもよい。」 また、次のようにも記述されています。 「オブジェクト型又は不完全型へのポインタは, 他のオブジェクト型又は不完全型へのポインタに型変換できる。」 何か微妙な記述ですが、型T*から型q T*へは変換できるが、型q T*から型T*への変換は明言を避けています。ただし、後者の記述によって、関数型以外へのポインタ同士の相互変換はできるとなっていますから、型q T*から型T*への変換できないわけではありません。 また、前者の記述は、あくまでも型T*から型q T*への変換のことであって、型T**から型q T**への変換のことではありません。型T**は、(T*)*というイメージですし、型q T**なら(q T*)*というイメージだからです。 型q T*から型T*への変換にせよ、型T**から型q T**への変換にせよ、後者の記述にある「オブジェクト型又は不完全型へのポインタ」ですから、相互変換は可能です。その辺の微妙なところを指摘するために警告が出ているものと思われます。 警告というのは、規格が定めるものではなく、処理系が勝手に出しているものですから、その真意を知るには、処理系の設計者に聞くしかないと思いますが、まあ、この解釈で当たらずとも遠からずかと思います。 ちなみに、C++の場合には#3の回答の通りの理由で、変換することができません(警告ではなく、コンパイルエラーになります)。

basserkk
質問者

お礼

ありがとうございます。 規格書を交えた説明は非常に分かりやすく納得できました。 次からは規格書にも目を通して考えて行きたいと思います。

その他の回答 (3)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

#2 です. cppll で調べました. int ** から const int ** に変換できるとすると, 次の場合に破綻するみたい: int *pi; const int **ppci = π const int ci = 0; *ppci = &ci; *pi = 1; これで const なはずの ci の値が pi 経由で変更できます.

basserkk
質問者

お礼

ありがとうございます。 これはなかなかやろうとは思いませんが、危険ですね。 参考になりました。 cppllはC++の話がメインのようですが、機会があれば目を通していきたいと思います。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

よく覚えていないんだけど, 確か 1 のパターンを許すと const にからんで危険なことができちゃったはず. const が合法的に外せるんだっけ....

  • MASATO3
  • ベストアンサー率60% (27/45)
回答No.1

constは型の一部です。ですので、 int*と、const int*は異なる型です。 異なる型ですが、 「const型を指すポインタに非const型を指すポインタを代入できる」というルールがあるので、2のパターンは通るわけです。 ですので以下のような1の変形パターンはOKとなります。 int **p; int * const * p1 = p; なお、「const型を指すポインタのポインタに非const型を指すポインタのポインタを代入できる」というルールが無いのはなぜ?と聞かれると私には分かりません。なんででしょうね。

basserkk
質問者

お礼

「const型を指すポインタに非const型を指すポインタを代入できる」 今回私が疑問に思っていたことがこの1文とjactaさんの返信で理解できました。 ありがとうございます。

関連するQ&A

  • const int i ? int const i ?

    お世話になります 初歩的な事ですがよろしくお願い致します const 修飾子って変数型の前につけるの?後につけるの? //---------------------------------------------------- const int iTest1[] = { 0x0000, 0x0000 }; const int iTest2[] = { 0xFFFF, 0xFFFF }; const int* const piTest[] = { iTest1, iTest2 }; //---------------------------------------------------- const int 型のポインタ配列をロム領域に確保したい場合は変数の後にconst修飾子をつけると思います const const int* piTest[] = { iTest1, iTest2 }; これだとエラーとなるはずです。。。 そこで、疑問に思ったのが、私の書式だと、const intとconst修飾子は前に着けるのが普通だと思ってました //----------------------------------------------------   const int i = 0;   int const i = 0; //---------------------------------------------------- でも、どちらでもコンパイルは通ると思います 配置領域を確認した所、どちらもROMに確保されてました 一般的にどちらが正解なのでしょうか? const const int* piTest[] = { iTest1, iTest2 }; が、エラーになるという事は、int const i が正解なのでしょうか? 教授よろしくお願い致します ちなみに、組み込み系に特化した話になっています windows系とかだとconst宣言は何処にいくんですかね・・・ ヒープじゃない予備領域とかあるんですかね・・・

  • ポインタとポインタのポインタの違い

    #include<iostream> #include<cstdlib> using namespace std; long myclass(const char *start,char **end,int base=10); int main(){ long d; char *p; char ss[]="231231"; d=myclass(ss,&p,16); cout<<d; d=myclass(ss,&p); cout<<d; return 0; } long myclass(const char *start,char **end,int base){ return strtol(start,end,base); } これはあるサンプルプログラムです。 これを自分が少しいじって char *pをchar**pにして&pをpにしました。 すると初期化されていないローカル変数pが使用されますと出て、警告がでました。 自分ではどちらのソースも初期化されていないと思うのですが、実際サンプルプログラムをコンパイルすると警告はでません。 何故なのでしょうか? コンパイラはvisual studio2008です。 よろしくお願いします。

  • C言語のポインタについて教えてください。

    C言語のポインタについて教えてください。 ・pointer1.c  int main(){   int a;   int *p;   p = &a;     a = 123;   printf("%d", *p);   return 0;  } ・pointer2.c   int main(){ int a[100]; int *p; p = &a[0]; int i; for(i = 0; i < 100; i++) a[i] = i; for(i = 0; i < 100; i++) printf("%d", *p++); return 0; } と二つのソースコードがあるとき、pointer2.cの「p = &a[0]」をpointer1.cのように「p = &a」と書けないのはなぜですか?  また、「&a」は動かすことのできなく、「aを指し示す*p」は動かすことができる変数のようなもの、という認識に誤りはないでしょうか?  宜しくお願いします。

  • C言語のポインタのことで

    int num=15; int *p=&num; char h= 'A'; char *p=&h; って、数字や一文字の時は変数を用意しなくてはならないのに、 char *z="K"; ←ダブルクォーテーションで囲むと1文字もOK printf("%c\n",*z); とか、 char *name="名前"; printf("%s\n",*name); とかの場合、変数を用意しなくてもできますよね。 文字はどこか別の場所に保管されているのでしょうか?

  • C言語のポインタ

    あまり意識せずにポインタを使っているせいか,次のプログラムではまってしまいました. #include<stdio.h> #include<stdlib.h> int main(void) {  int *p, q;  p = (int *)malloc(sizeof(int));  q = (int *)malloc(sizeof(int));  *p = 2;  printf("%d\n", *p);  return 0; } コンパイルエラーで実行ファイルが出力されません. このプログラムで変数qはなぜポインタじゃないのでしょうか? 次にtypedefでptr_intという型を定義したプログラムは, 上のようなエラーが出力されず,期待とおりの結果になりました. #include<stdio.h> #include<stdlib.h> typedef int* ptr_int; int main(void) {  ptr_int p, q;  p = (int *)malloc(sizeof(int));  q = (int *)malloc(sizeof(int));  *p = 2;  *q = 3;  printf("%d\n", *p);  printf("%d\n", *q); return 0; } typedefすることでなぜエラーを回避することができるのでしょうか? よろしくおねがいします.

  • C言語のキャストについて

    C言語のキャストについて こんばんわ、質問です。 『キャスト』と言われる型を合わせるルールがあると思います。 あれはコンパイラに 「違う型の変数への代入だけど、これは意図したことですよ」 ってことを分かってもらうための手段という認識でよろしいでしょうか? コンパイル後、実行時には変数の型情報は使われない(残ってない?)と 思っていますが、その道を極めている方、教えてください。 キャストが何に使われるのか知りたいです。

  • ポインタがわからない

    C言語初心者です。 Int num; Int  *p=&num; という変数を宣言したとします。 下は「int *型」と呼び、pは「int型オブジェクトであるnumを指すポインタ」であることはわかりました。 ですが、「int型」と「int  *型」そのものの違いがわかりません。 たとえば、今読んでいるテキストに Printf(“int 型は%uバイトです。\n”,(unsigned)sizeof(int));→結果は2バイト Printf(“int *型は%uバイトです。\n”,(unsigned)sizeof(int *));→結果は4バイト 僕の環境では両方とも4バイトと結果は変わりませんでしたがテキストでは結果は上記のように変わってきてしまうようでした。これはなぜ変わってくるのでしょうか? 「int型」と「int  *型」そのものを比較した際の違いがわかりません。

  • C言語(またはC++言語)についての質問です。

    C言語(またはC++言語)において、以下の変数x、ポインタ変数(*p,**pp, ***ppp)のメモリ上のイメージを記述しなさい。 int x, *p, **pp, ***ppp; x=10; p=&x; pp=&p; pppp=&pp; という問題なのですが、わからなくて困っています。 このプログラムはどのようなものなのでしょうか。 詳しい回答をよろしくお願いします。

  • C言語の基本的な質問ですが、関数へのポインタの宣言

    関数へのポインタの質問です。 下のように、関数へのポインタを使ったプログラムを書きました。 (関数へのポインタを理解するためのものなので、実用的な意味はありません。(*^_^*) また、このプログラムはコンパイルもリンクも実行も問題なく出来ます。) #include <stdio.h> int add_func(int,int); (*func_p0) (int,int); int main(void) { int (*func_p1) (int,int); int (*func_p2) ( ); int hoge0,hoge1,hoge2; func_p0=add_func; hoge0=func_p0(3,5); printf("0 : 3+5は%d\n",hoge0); func_p1=add_func; hoge1=func_p1(3,5); printf("1 : 3+5は%d\n",hoge1); func_p2=add_func; hoge2=func_p2(3,5); printf("2 : 3+5は%d\n",hoge2); return(0); } int add_func(int x, int y) { return(x+y); } func_p0のように戻り値の型を書かない場合と、func_p1やfunc_p2のように戻り値の型を書くのとでは何が違うのでしょうか。 func_p0は外部変数ですが、自動変数にする(main関数の中で同様に宣言。)とコンパイルエラーになります。 それはなぜですか。 func_p1のように引数の型が書いてあるのと、func_p2のように引数の型が書いていないのでは何が違うのでしょうか。 int (*func_p2) ( );というのは、int (*func_p2) (void);とは違うんですよね?

  • C言語の変数の型がわかりません

    C言語の変数の型がわかりません [int]は4バイトを使って整数(-2147483648~2147483648)を格納できる. [long int]は4バイトを使って整数(-2147483648~2147483648)を格納できる. のようなことが書いてあります. [int]と[long int]の違いは何ですか?

専門家に質問してみよう