みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

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することでなぜエラーを回避することができるのでしょうか?

よろしくおねがいします.

投稿日時 - 2011-09-06 16:21:46

QNo.6993451

すぐに回答ほしいです

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

「スキっとした答え」と「そうでない答え」というのはどこで線を引けばいいでしょうか>#7. 「言語仕様」というのはある意味「スキっとした答え」と言えそうですが....

ということで #7 の疑問に対する説明:

まず「宣言」の基本形は
type-specifier declarator, declarator, ...;
です. で type-specifier (型指定子) は基本型/void/構造体・共用体/列挙型/typedef名のいずれか. declarator は間接参照の「*」や配列を指定する「[]」や関数を表す「()」などなどを含みます.

つまり int は型指定子だけど int * は (型だけど) 型指定子ではありません. 「int *」は字面の空白の有無とは無関係に (つまり「int*」だろうと「int *」だろうと)
・型指定子である int
・宣言子の一部である *
と分けて考えなければなりません. その結果,
int* p, q;

・型指定子 int
・2つの宣言子 *p, q
と分解できて
・p は int へのポインタ
・q は int
と解釈されます.

余談ですが宣言の文脈で「*」や「,」を「演算子」と思っちゃダメです. 「*」は宣言子の一部だし, 「,」は宣言子と宣言子の区切りです. 関数を呼び出すときに
printf("%d\n", *q);
の「,」を「演算子」とは思わないよね. それと同じこと.

ちなみに「p と q の両方をポインタにしたい」というときに
int *p, *q;
とした方が
int *p, q;
とするよりも混乱は少ないと思います. つまり
int *p;
とだけあったときに*何かの事情で* int の変数 q を追加するときに, 思わず
int *p, q;
ってやりたくなりませんか?

投稿日時 - 2011-09-07 11:48:21

お礼

int* q,p;の解釈は理解できましたが,なぜtypedefはとおるのでしょうか?
typedefの書式はtypedef 既存の型 別名だった気がするのですが・・・
そうだったらint*は型として扱われますよね?

投稿日時 - 2011-09-07 18:13:47

ANo.8

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(12)

ANo.12

おやおや、私の発言でお気を悪くされた方がいらっしゃったのなら、申し訳ありません。
私自身、はっきりと判っていなかったので、まさしく#8様のような解説を期待しておりました。
よく理解できた気がします。
ありがとうございました。

投稿日時 - 2011-09-08 15:11:49

ANo.11

「typedef でなぜいけるのか」の話:

typedef名は「typedef がなければその型である」ような型です. つまり,
typedef int *ptr_int;
の場合 typedef を消して
int *ptr_int;
なら ptr_int は int * 型なのでptr_int という typedef名は int * と同じものを表します.

なお, 「typedef の書式」は「typedef 既存の型 別名」ではありません. typedef名が (新たな, 別の型ではなく) 既存の型の別名として扱われるのはその通りですが, 複雑な型を考えると「typedef 既存の型 別名」ではうまくいきません. 例えば「int を要素とする大きさ 5 の配列」という型を
typedef int IntArray[5];
と作ることができるのですが, これは明らかに「typedef 既存の型 別名」のような形式にはなっていません.

投稿日時 - 2011-09-07 23:16:50

ANo.10

typedef int* ptr_int;
ptr_int p, q;



int *p, q;

では意味が違います。
typedefは新しい「型」を定義します。
この場合はintポインタ型ですね。
前者はintポインタ型としてpとqを宣言しています。

一方でint *p,q;はあくまでもint型です。
修飾子として*がついているpだけが
ポインタとして解釈され、qは純粋なint型
として解釈されます。



> typedefすることでなぜエラーを回避することができるのでしょうか?

typedefのそういった仕様により、エラーが発生しなくなったと言えます。

投稿日時 - 2011-09-07 21:32:53

ANo.9

文法的にはA No.8の方の説明が正確なんですけど…誰が説明したってスッキリはしないんじゃないかな。
そういうルールだってことで、納得してもらえないですかね?
もちろん <int *> という型はありますよ。でも、

int *a;
という宣言が、<int *> までが型名で a が変数名という『型名 変数名;』って認識に当てはめたいのでしょうが実際のCのコンパイラの構文解析はそういう風にはとらえないんですよ。
例えば、
int a, *b;
とか、
int *c, **d;
という宣言がなされたら、どう解釈すればよいのでしょう。

もちろん、正解は
<a : int型> <b : int *型> <c : int *型> <d : int **型>
なんですが、あなたの最初の認識だと、d なんて <int *> の ** になって訳分からないでしょ。

でも、<int *> 型はありますし、typedef してちゃんとその型で定義できますよ。
(あなたが質問で指摘されているように int* 型を int_ptr と typedef して、
ptr_int p, q;
と宣言する方法は、全く正しいやり方です。)

投稿日時 - 2011-09-07 21:18:12

ANo.7

回答者諸氏もスキっとした答えが出せないでいるようですね。気になっていた質問で、回答ではないですが、ちょっと思ったことを書いてみます。

今質問のキモは、
int *p,q; は、何故、
(int *)型のpとq、と解釈されないのか?と言うことですよね?

宣言時の構文解釈が、
変数の名前はpであり、それは(*が付いてるので)ポインタであり、その指し示しているのは、intである。
変数の名前はqであり、(*が付いていないので)直接の変数でその型は、intである。
となっているのですね。

考えづらいですが、int *というのは、型として解釈されていない、と言うことですよね。
演算子の優先順位的には、*の方が,より優先順位が高いから、とも思えますが、変数宣言の文脈でこれを演算子として考えていいのか?ちょっと断言できません。

より詳しい説明は、どなたか詳しい人が現れるのを期待しています。、

typedefすると、完全にint *がptr_intという型として解釈されるので、構文上双方に効くのは、当然なんでしょうね。

投稿日時 - 2011-09-07 02:35:17

ANo.6

>int *p, q;

邪道かもしれませんが、
*pはint型(よって、pはintへのポインター)
qはint型
って読んでみると、今回の間違いの原因に気づきやすくなるかもしれません。

投稿日時 - 2011-09-06 23:42:11

ANo.5

int a,b ;→ int a; int b;
int *c,d ;→ int *c; int d ;
int_ptr e, f ; → int_ptr e; int_ptr f ;→ int *e ; int * f ;

この例でわかるといいのですが。

投稿日時 - 2011-09-06 21:05:58

ANo.4

>typedefすることでなぜエラーを回避することができるのでしょうか?

それは、「intへのポインター」という型にptr_intという別名を付けているからです。

投稿日時 - 2011-09-06 20:50:54

ANo.3

int型、char型、float型などは、変数の形そのものですが、
ポインターとはその名のごとく、指し示すものということです。
なので、intのポインター、charのポインター、floatのポインターなどがあります。

intのポインターというのは、intの変数が入っている場所を指し示すもの、と考えれば良いですね。

意識せずポインターを使っておられるとのことですが、今のうちにポインターをしっかり復習されておいた方が良いですね。
積極的にポインターを使うと、コーディングの幅がぐっと広がります。
http://www9.plala.or.jp/sgwr-t/c/sec10.html

ご参考に。

投稿日時 - 2011-09-06 19:34:38

ANo.2

単純な記述のミスと思えるのですが、

 int *p, q; を  int *p, *q;

として試してみてください。

 int *p, q; のままだと、 p はintのポインター型、q はint型 として定義されてしまいます。

ご参考に。

投稿日時 - 2011-09-06 16:39:38

補足

int *型というものがあるというわけではなくint型でポインタが定義できるということでしょうか?

投稿日時 - 2011-09-06 16:51:25

ANo.1

「変数qはなぜポインタじゃないのでしょうか」といわれたら「だってそういう構文なんだもん」というのが最も適切だったりする.

int *p, q;

int *p; int q;
と同じ意味.

投稿日時 - 2011-09-06 16:36:01