C言語のポインタとコンパイルエラーの解消法

このQ&Aのポイント
  • C言語のポインタを意識せずに使用するとコンパイルエラーが発生することがあります。
  • ポインタを正しく扱うためには、変数の宣言やメモリの割り当てに注意する必要があります。
  • typedefを使用することでポインタの扱いを簡潔にすることができ、エラーを回避することができます。
回答を見る
  • ベストアンサー

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することでなぜエラーを回避することができるのでしょうか? よろしくおねがいします.

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

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

「スキっとした答え」と「そうでない答え」というのはどこで線を引けばいいでしょうか>#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; ってやりたくなりませんか?

Oforest
質問者

お礼

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

その他の回答 (11)

回答No.12

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

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.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 既存の型 別名」のような形式にはなっていません.

  • jjk65536
  • ベストアンサー率59% (66/111)
回答No.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のそういった仕様により、エラーが発生しなくなったと言えます。

  • a_kwn
  • ベストアンサー率34% (8/23)
回答No.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; と宣言する方法は、全く正しいやり方です。)

回答No.7

回答者諸氏もスキっとした答えが出せないでいるようですね。気になっていた質問で、回答ではないですが、ちょっと思ったことを書いてみます。 今質問のキモは、 int *p,q; は、何故、 (int *)型のpとq、と解釈されないのか?と言うことですよね? 宣言時の構文解釈が、 変数の名前はpであり、それは(*が付いてるので)ポインタであり、その指し示しているのは、intである。 変数の名前はqであり、(*が付いていないので)直接の変数でその型は、intである。 となっているのですね。 考えづらいですが、int *というのは、型として解釈されていない、と言うことですよね。 演算子の優先順位的には、*の方が,より優先順位が高いから、とも思えますが、変数宣言の文脈でこれを演算子として考えていいのか?ちょっと断言できません。 より詳しい説明は、どなたか詳しい人が現れるのを期待しています。、 typedefすると、完全にint *がptr_intという型として解釈されるので、構文上双方に効くのは、当然なんでしょうね。

  • asuncion
  • ベストアンサー率33% (2126/6286)
回答No.6

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

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.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 ; この例でわかるといいのですが。

  • asuncion
  • ベストアンサー率33% (2126/6286)
回答No.4

>typedefすることでなぜエラーを回避することができるのでしょうか? それは、「intへのポインター」という型にptr_intという別名を付けているからです。

  • KEIS050162
  • ベストアンサー率47% (890/1879)
回答No.3

int型、char型、float型などは、変数の形そのものですが、 ポインターとはその名のごとく、指し示すものということです。 なので、intのポインター、charのポインター、floatのポインターなどがあります。 intのポインターというのは、intの変数が入っている場所を指し示すもの、と考えれば良いですね。 意識せずポインターを使っておられるとのことですが、今のうちにポインターをしっかり復習されておいた方が良いですね。 積極的にポインターを使うと、コーディングの幅がぐっと広がります。 http://www9.plala.or.jp/sgwr-t/c/sec10.html ご参考に。

  • KEIS050162
  • ベストアンサー率47% (890/1879)
回答No.2

単純な記述のミスと思えるのですが、  int *p, q; を  int *p, *q; として試してみてください。  int *p, q; のままだと、 p はintのポインター型、q はint型 として定義されてしまいます。 ご参考に。

Oforest
質問者

補足

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

関連するQ&A

  • C言語 ポインタ

    以下の問題について質問です。 次のプログラムを実行すると、画面にどのように表示されるかを書け。 #include <stdio.h> int main(void){ int s=2, t=3, u=7; int *q=&s, *p=&t, *h; u-=*q; *q=*p; *p=u+1; h=p; p=q; q=h; *p+=*q; printf("%d %d %d\n",s,t,u); return 0; } これで、9行目までにq=3,p=6,u=5になって、10~12行目でqとpの値を交換してるから、実行結果は 6 9 5 になるかと思ったのですが、実際に動かしてみると 9 6 5 と表示されました。 どこで間違えているのかが分からないので、上から順にどういう処理が行われているのかを書いていただけるとありがたいです。

  • C言語、配列とポインタとアスタリスクの関係

    ちょっと行き詰まっています。 苦しんで覚えるCで勉強しているのですが、まさに苦しんでいます。 http://9cguide.appspot.com/19-01.html #include <stdio.h> #include <stdlib.h> int main() { int i; int *heap; heap = (int *)malloc(sizeof(int) * 10); if (heap == NULL) exit(0); for (i = 0;i < 10;i++) { heap[i] = i; } printf("%d\n",heap[5]); free(heap); return 0; } int *heap; ここで int ポインタを宣言しています。 heap = (int *)malloc(sizeof(int) * 10); ここでヒープを確保しています。(int *) のキャストも sizeof(int) も理解できました。 for (i = 0;i < 10;i++) { heap[i] = i; } まず1点目の疑問はここです。 変数 heap は「ポインタ変数」です。それでいて配列です。 ポインタ変数は、プログラムの文中で通常の変数として使うときには「*heap」のように先頭にアスタリスクを付けなければならかなったと記憶しています。 アスタリスクなしの「heap」はアドレス格納用の変数ではないでしょうか。 printf("%d\n",heap[5]); そして、その疑問をよそに、この命令が成り立っているようです。 画面上に出される結果は「5」であり、変数「heap」がただの配列として機能しているように見えます。 この printf 次のように書き換えると、エラーが出てコンパイルできませんでした。 書き換え実験1 printf("%p\n",*heap[5]); アスタリスクを付けて、通常の変数として扱い、受ける方も「%d」から「%p」に書き換えてアドレスを表示してみようと思ったのですが、 「「pointer」を付け忘れています。」というエラーが表示されました。 書き換え実験2 printf("%p\n",heap[5]); 受ける方を「%d」からポインタを受ける「%p」にしましたが、変数の方はアスタリスクなしです。 すると、結果はアドレス「00000005」が返ってきました。 (変数にアスタがないのになぜ?) 書き換え実験3 printf("%d\n",*heap[5]); これはもうめちゃくちゃですが、一応やってみました。コンパイルエラーで、 「「pointer」を付け忘れています。」というエラーが表示されました。 つまり、こういうことです。 0:printf("%d\n",heap[5]); //5 1:printf("%p\n",*heap[5]); //エラー 2:printf("%p\n",heap[5]); //00000005 3:printf("%d\n",*heap[5]);//エラー この結果から推測するに、アスタリスクはそもそも付けるとエラーになり、アドレスを表すか、そのアドレスに格納された値を表すかを切り替えるには、単にその変数を受ける「%d」や「%p」を変えるだけ、ということになるのだと思います。 mallocで返ってくるのは、ポインタ変数(の配列)だと思うので、変数のモードを切り替えるためにアスタリスクが必要なのだと思っていましたが、どこかで重大な勘違いをしているようです。 この件について、どなたか教えていただけないでしょうか。

  • ポインタと配列 助けてください

    #include <stdio.h> int main(void) { int a[5] = {1, 2, 3, 4, 0}; int i, *ptr; ptr = ######; while (*ptr != 0){ ######; ######; }      for (i = 0; i < 5; i++) printf("a[%d]=%2d &a[%d]=%p\n", i, #####,i, #####); putchar('\n'); ptr = ######; for (i = 0; i < 5; i++){ printf("ptr値 =%d ptrアドレス =%p \n", ####, ####); ####; } printf("\n"); return (0); } 一次元配列a[]の一番目の要素以外を0にするプログラム(ただし、最後の要素は0)を作成したいのですが、#の部分に何をあてはめたらいいかわかりません。 助けてください。

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • C言語 ポインタのポインタ

    失礼します。現在ある確保したメモリに対してCSVから得た値を配列として入れていきたいのですが、うまくいきません。エラーとしてはコマンドプロンプトに何も表示がされていない状態です。 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include<string.h> int main(void) { FILE *fp; double *fname = "./testfile.csv"; int i = 0; double **p; p = (double *)malloc(1000); //1000個のdouble(11行分) for (i = 0; i < 11; i++) { //行の先頭要素にアクセスして1000個のメモリを確保 p[i] = malloc(1000 * sizeof(double)); } fp = fopen(fname, "r"); if (fp == NULL) { printf("%sファイルが開けません¥n", fname); return -1; } double c; int column = 0; int row = 0; while ((c = fgetc(fp)) != EOF) { if (c == ',') { column++; //putchar('\n'); } else if (c == '\n') { row++; column = 0; //putchar('\n'); } else { p[row][column] = c; } } printf("%1f", p[0][1]); free(p); fclose(fp); return 0; } 何卒よろしくお願いします。 CSVの内容は、 gagdgj,gasgag,fgdagg fgaafgaf,gfaggfag,gfagd といった形です。 通報する

  • C free関数の開放について。

    独学でCを勉強し始めてる初心者です。 以下の構文で、最後に、 free(p); とあり、確保したメモリ「p」を開放していますが、 確保したメモリ「q」は開放しなくて良いのでしょうか? この場合は同じ部分のメモリを確保しているからqは開放しなくて良いということなんでしょうか…宜しくお願い致します。 #include <stdio.h> #include <stdlib.h> int main(void) { int *p; p=malloc(sizeof(int)*3); if(p==NULL) exit(1); p[0]=10; p[1]=20; p[2]=30; printf("%d\n",p[0]+p[1]+p[2]); free(p); return=0; }

  • ポインタ

    #include<stdio.h> #include<stdlib.h> struct number{ int value; struct number *next; }; int main(void){ FILE *f; struct number *p; int n=0,i; f=fopen("number.txt","r"); p=malloc(sizeof(struct number)); if(p==NULL){ printf("error"); return 1; } while(fscanf(f,"%d",&(p+n)->value)!=EOF){ (p+n)->next=malloc(sizeof(struct number)); if(p+n==NULL){ printf("error"); return 1; } p+n=(p+n)->next; n++; } for(i=0;i<n;i++) printf("%d\n",(p+i)->value); free(p); fclose(f); return 0; } のp+n=(p+n)->next;の部分でバグが出てしまうのですがアロー演算子と通常の演算子を同時に使うことができないなどのような規則があるのでしょうか、それとも俺がバカなだけなのでしょうか?ご指摘お願いします。

  • C言語のポインタについての質問です。

    C言語のポインタについての質問です。 2つのプログラムを作り、片方で数値を入力し、 もう片方でその数値を読み取りたいと思っています。 数値入力のプログラムは次のようになっています。 #include<stdio.h> void main(void){ int A=1; int *p; p=&A; printf("%p\n",p); } この実行結果は「0012FF88」となりました。 次にこの「0012FF88」というアドレスを使って「1」を読み取る 別のプログラムを作りたいと思っています。 #include<stdio.h> void main(void) { int add; int a; printf("アドレスは?\n"); scanf("%x",&add);    //「0012FF88」と入力 a=*(int*)add; printf("%x---->%d",&add,a); } このようなプログラムを作ってコンパイルできたのですが、 実行してアドレスを打ち込むと「Win32の例外が発生しました」 となって実行できません。 何かよい方法やプログラムの問題などありましたら 教えて頂けませんか?

  • C言語 リスト

    (1) /* list.c */ #include <stdio.h> #include <stdlib.h> struct node { int num; struct node *next; }; main() { struct node *head, *p; int x; head = NULL; while(scanf("%d",&x) != EOF) { p = (struct node *)malloc(sizeof(struct node)); p->num = x; p->next = head; head = p; } // リストの要素のnumの値を先頭から順に表示する p=head; while(p!=NULL) { printf("%d\n" ,p->num); p = p->next; } } (2) struct node *q; q = head; while(q->next != NULL) q = q->next; (1)を(2)を使い新しいノードをリストの最後に追加するようにしたいのですが どう書いたら良いのか教えていただきたいです

  • 配列について(C言語)

    配列の要素において一番目の要素以外を0にするプログラムを以下のように作成したのですが(while文を*b != 0という条件で作りたい)、while文内のところが間違っているためだと思われるのですが、うまく実行されません(実行しても何も表示されない)。どのようにしたらいいのでしょうか。または、何かが抜けている、ほかの部分が間違っている等もあるためにうまく実行されないということなのでしょうか。 #include <stdio.h> int main(void) { int a[5] = {5, 78, 2, 9, 0}; int i, *b; b = &a[0]; while (*b != 0){ if(*!=a[0]) *ptr=0; else *ptr=a[0]; } for (i = 0; i < 5; i++){ printf("ptr値 =%d ptrアドレス =%p \n", *ptr, ptr); } printf("\n"); return (0); }

専門家に質問してみよう