• ベストアンサー

ポインタについてアドバイスお願いします。

C言語の初心者です。 参考書でも書かれている事があるのですが、経験豊富な方でもポインタについて知ってるようで知ってないということがあります。って読んだことがあります。 実際、ポインタの必要性とはどのような時に必要なのでしょうか?? アドレス指定と言う風に、はじめはこの様な理解から入ると習いましたが、私には配列で十分間に合うのでは??って思っちゃうのです。 構造体の出力、アドレスの入れ替え、こんなときには便利なのかもしれませんが、C言語をやる上で絶対に必要・・、いったいなぜ??って考えちゃいます。 初心者なのに生意気な事言ってしまってもうしわけございません。 以前、私もそんな感じで悩んだ事あるって方がいらっしゃいましたら、ポインタとはこんな感じの時に初めて必要だと思うのだ!とご教授お願いいたします。 この様な質問に対しても、笑って答えてくださるような プロフェッショナルの方や、一般の凄い方のアドバイス、お待ちいたしております。 宜しくお願いいたします。

  • yuki22
  • お礼率83% (159/190)

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

  • ベストアンサー
  • ency
  • ベストアンサー率39% (93/238)
回答No.6

まず、ポインタの必要性云々の前に、ポインタ=アドレスという考え方を捨ててください。 ポインタは「型」です。 ただ、「○○型を指すポインタ」といったように、ある基準となる型があって、それをポイントするための型が「ポインタ型」なんです。 単に「ポインタ」といった場合、次の3つの意味をごちゃまぜにして使用していることが多いと思います。 1. ポインタ型 2. ポインタ型の変数 3. ポインタ型の値 たとえば、以下の例を考えてみましょう。 -------------------------------------------------------- int hoge; int *p_hoge; p_hoge = &hoge; -------------------------------------------------------- この場合、p_hoge の型「int *」が「int型を指すポインタ型」になります。 p_hoge は「int型を指すポインタ型の変数」です。 そして、&hoge は「int型を指すポインタ型の値」であり、これを p_hoge に代入しています。 通常、これらひっくるめて「ポインタ」と呼んでいると思います。 この中でよく言う「ポインタはアドレスだ」というのは、「ポインタ型の値」のことですよね。 正直なところ、コードを書いていて「ポインタがアドレスだ」などということを強く意識することはあまりないと思います。 # そもそも C の仕様に「ポインタがアドレス」などということは書かれていません。 # そういう実装の処理系が多いというだけのことです。 せいぜい「これはポインタだから、実体はこの変数を指していて…」といったくらいだと思います。 では、次に C 特有の配列とポインタの微妙な関係についてですが、まず配列とポインタは別物だということをきちんと理解してください。 ただ、コンパイラによって配列がその先頭要素を指すポインタに読み替えられてしまうんです。 # 配列に対して sizeof をとる場合など、いくつか例外はありますが。。。 配列として定義すれば、メモリ上に要素数分連続した領域を確保しますが、それ以降配列の各要素へはポインタとしてアクセスすることになります。 これは、C は配列に対して手を抜いた「配列を配列として扱えない言語」だからです。 それ以上の理由はありません。 しかし、やはり配列とポインタは別物です。 # どこが違うのか、これまでの説明からちょっと考えてみてください。 C の言語仕様に縛られた「ポインタ」の使い方としては、すでに回答にあるように関数への変数の渡し方があると思います。 1. 関数に配列 (文字列を含む) を渡す場合。 2. 関数に変数を参照渡しする場合。 3. 関数に構造体を渡す場合。 ただし、3 については ANSI C であれば構造体そのものを渡すことも可能ではありますが、処理速度やスタックの大きさに制限があるような場合には、今でもよく用いられる方法です。 C の言語仕様に縛られない「ポインタ」の使い方としては、リスト構造があります。 これは最初のうちはそれほど使う機会はないと思いますが、それなりの規模のプログラムを書くようになった場合、必ず必要になるものです。 リスト構造の詳細は割愛しますが、私はリスト構造こそがポインタがもっとも威力を発揮する使い方だと思っています。 長々と失礼いたしました。 ご参考にでもなれば幸いです。

yuki22
質問者

お礼

失礼なんてとんでもないです。 初心者の私にはとってもありがたいです。 ポインタ=アドレスって凄く強くもってしまってるので、その一途な概念は捨てないとなのですね・・。 もちろん、アドレス指定ってことも捨ててはならないってことも伝わってきました。 今回お教えいただけたことを思いながら、努力を重ねていきたいとおもいます。 お返事ありがとうございました。 がんばってみます。

その他の回答 (8)

回答No.9

>素人はC以外のプログラムから始めたほうが良くて、 >手を出すものじゃないと言う事なのでしょうか^^; いや、そういうことではありません。Cは初心者にも適していると思います。プロ向けの包丁のようなものです。s素人にも使えます。しかし、プロ向けの道具を素人の初心者教育に使うのですから、素人にわかりやすくはできていませんよ、ということです。先がとんがっている理由がわからないとか言っていてもしょうがないです。 というより、あなたがなかなか理解できないことをやらない理由を探しているように思えたのです。やるやらないの理由を探すのではなく、まず先に進むべきだというのがアドバイスです。

yuki22
質問者

お礼

ごめんなさい。いろいろ調べてみて解らなくって質問させていただいたのですが、まだあなたの言うところまでの壁には達していないと言うことなのですね・・。 でも、知りたくて、ここでしたらいろんな考え方や知識をもっている方々がいると聞き、自分の努力は高スキルを持つ方には伝わりにくいかもしれなくて、時には叱られるかもしれないけど、導いてくれる方も沢山いるっていろんな友達に教えていただいて質問させていただいてます。 でも、まだまだ甘い考えだったのですね。 この度、nofutureforyouさんにお教えいただいた、”まず先に進むべき”と言う事も、進んでみれば今は解らなくってもいつかわかる時がくると信じてがんばってみようと思います。 初心者だからと言って、お気に触るような質問をしてしまっていたら本当にごめんなさい。 お返事ありがとうございました。

回答No.8

答えは、Cはそのようなものだ、ということです。もともとシロートのための言語ではありません。 K&R にもポインタがなぜ必要かということは書いてあります。

yuki22
質問者

お礼

う~ん、素人はC以外のプログラムから始めたほうが良くて、手を出すものじゃないと言う事なのでしょうか^^; お返事ありがとうございました。

回答No.7

個人的にはよく使うデータ構造のチェーン構造で ポインターの真価か発揮できていると思います。 データのサイズが不規則・・・配列サイズはかっきりしない。 データを途中に挿入するときにポインターの位置を入れ替えるだけですみ配列のようにデータをずらしてデータを 格納させる処理がいらなくなる。 なんかですかね。 このような処理を別の言語でやろうとしたときに ポインターが使えなくて配列で代用するときに 書くコードの量が大幅に違います。

yuki22
質問者

お礼

データ構造のチェーンと言うところが、私レベルには何となくしかわからないのですが、いろいろ面倒を簡単にしてくれるって感じがつかめてきました。 お答えも私レベルにあわせていただいてありがとうございます。 いただいた情報を糧に、がんばってみます。 お返事ありがとうございました。

  • PG_RankB
  • ベストアンサー率40% (12/30)
回答No.5

久々に長文になりますが、ご容赦下さい。 凄く良い質問だと思いましたので。 私も学び始めて1年間位は、ポインタなんて絶対に必要ない。無くたって組めるじゃん。と思っていた時期がありましたよ^^; 結論から言うと、ポインタが無いと物凄く困ります。私はC言語系の技術で飯の種を作っていますが、もしこの概念が消えたら、C言語はすぐにやめます; ライブラリ製作者的に、どれだけ不便になる事か・・・ 確かに、1つの関数(main等)の中で、ポインタなんて使っても、何の意味も無いですからね。 ただアスタリスクを付けたら中身が、付けなければアドレスが表示できます。程度のものです。 それと、ポインタが文字列操作をする時にばかり使う物だと思っていませんか?確かに参考書だと文字列に関する物が多いですからね。 そこだけで考えると、配列と文字列の違いがピンと来ないと思います。文字列操作における配列と文字列の違いは、代表的な所で内部でのメモリの参照方法云々の話なので、あまり表面化されませんし。 初心者・初級者の内は触れることすら無いでしょう。 ですが、文字列に関してもポインタ、つまりアドレスをきちんと扱える事は、C言語プログラマには絶対必須として求められます。 厳しい言い方になりますが、それが出来ない、必要ないと言うなら、C言語プログラマは名乗れないかと。 少なくとも、私はその人をC言語使いとは認められません。 ビットフィールドや可変個引数の関数なんて知らなくても良いですが、アドレスの概念を把握し、応用出来ない事は、C言語でソフトを作る上では致命的です。 もしそのような人がメンバにいたら、その仕事は断りたい位です。絶対迷惑かけられますから。 ポインタを使わないプログラムなら、他の言語でも出来ますので、わざわざCを使う必要が激減します。 ポインタがその真価を発揮するのは、関数を学び始めてからです。 ライブラリ等、共通していつでも使いたいと言う処理を関数化しておくと、ソースを組みやすくなり、効率が何十倍以上にも上がったりするのですが。 初心者との事ですので、関数が分からない場合申し訳無いのですが、少しソースを書いて説明しますね。 例題なので実用的では無いですが、ご容赦下さい。 例)第一、第二パラメータに数値を入力し、大きい方を第三、小さい方を第四パラメータに格納する関数を作り、その第三、第四パラメータの値をmain()で表示する。 そんな機能が欲しかったとします。 以下に、ポインタを使う場合と使わない場合の関数を作ってみます。 #include <stdio.h> /* ポインタを使わない */ void no_pointer( int i_iA, int i_iB, int i_iC, int i_iD ); /* ポインタを使う */ void pointer( int i_iA, int i_iB, int *po_iC, int *po_iD ); int main( void ) { int iMax = 0; int iMin = 0; /* ポインタを使わない */ no_pointer( 10, 30, iMax, iMin ); printf( "iMax = %d\n", iMax ); printf( "iMin = %d\n", iMin ); /* ポインタを使う */ pointer( 10, 30, &iMax, &iMin ); printf( "iMax = %d\n", iMax ); printf( "iMin = %d\n", iMin ); return 0; } void no_pointer( int i_iA, int i_iB, int i_iC, int i_iD ) { if ( i_iA >= i_iB ) { i_iC = i_iA; i_iD = i_iB; } else { i_iC = i_iB; i_iD = i_iA; } } void pointer( int i_iA, int i_iB, int *po_iC, int *po_iD ) { if ( i_iA >= i_iB ) { *po_iC = i_iA; *po_iD = i_iB; } else { *po_iC = i_iB; *po_iD = i_iA; } } 実行くだされば分かると思いますが、no_pointer関数だと、スコープの問題で数値の変化が適用されません。 no_pointerの中だけで処理が完結してしまうからです、その為、pointer関数には、変数のアドレスを渡しています。これで数値の変化が行われます。 表示するだけなら、no_pointer関数の中にprintfを入れれば良いと思われるかも知れませんが、表示の仕方は必ずしもprintfが使われる訳ではないです。 Windowsアプリケーションなら、文字を表示では無く、描画する関数が必要になります。ここでno_pointerの中にprintfが入っていた場合、no_pointerはコンソールアプリケーションでしか使えない関数になってしまいます。 ifdef等で処理分けし、コンパイルオプションで適用させる方法も取れなくは無いですが、一々オプションを設定する等、面倒ごとを嫌うプログラムの世界では、やはり好まれません。 あくまで、数値の大小を比べて、格納すると言う意味を持たせたい時に、ムダに使用可能な領域を狭める様な処理を入れるのは、汎用的な処理が求められるライブラリを製作する場合には好ましくありません。 また、通常は、1つのソフトに関数が数百個と入っています。数の大小を比べたい場所が200個あったとして、それぞれ全てにif(~)と、処理を並べますか? 数値の比較程度なら数行で収まりますが、中には100行以上の処理を要する場合もザラにあります。 一々全て書いていたら、100行*200個で、2万行も書かなければいけません。関数にすれば、100行ですみ、19900行も違いが出ます。 関数1つ呼んで、1行で済むなら楽な物ですし、処理が見やすく、メンテナンスもしやすくなります。 もし関数名を変える事になっても、全てでpointerと書いておけば、エディタのグレップ機能で一括変換してくれます。 ソフトウェアを作る場合、初心者が考える以上に規模が巨大であると言う事(物によってはムダに数百万行とか)と、作る際になるべく楽に、作った後も見やすく、メンテナンスしやすいように。と言う心がけが必要です。 これは、Cに限らず、どの言語でも同じ事です。 プログラム言語の作成者は、我々言語の利用者よりも遥かに深く考え、追求し、その結果を機能に盛り込んでいます。 必要の無い機能なんてそうそうありませんし、ポインタが必要だと熟練者が口を揃えて言うのも、きちんとした苦労談や経験に基づいての事ですので、言語の新しい機能が難しく、用途が分からないと言う理由だけで、ないがしろにするのは、後で困りますよ^^; 経験して真意を掴んだ瞬間に、これ以上無いくらいに理解できると思いますがw

yuki22
質問者

お礼

わわ、こんなに沢山の知識をお分けいただいてありがとうございます。 私的には、ポインタとは工場みたいなもので、そのなかにはいろいろいままでの作業工程などがぎっしり詰まっていて、必要に応じて工場に依頼するって感じにとらせていただいたのですが・・この認識、ぜんぜんちがうよ・・っと怒られちゃいますでしょうか?それとも、そのくらいから入っていってそこからスタートだよ・・って感じでお褒めいただけるのでしょうか・・? 仕事での厳しさも、驕らず、ソフトタッチでいてそれでいて厳しいと言う風にお教えいただけたとおもいます。 参考にさせていただきます。 お返事ありがとうございました。

  • smat7
  • ベストアンサー率46% (7/15)
回答No.4

勉強始めた方にポインタが変数として最初に明示的に現れるのは関数の参照渡しではないかと思います。 C言語の関数引数は値渡し(call by value)ですので、引数をもらった関数側でその引数変数を操作しても呼び出し側の変数に影響を与えません。これは関数の独立性の面で便利なのですが、時には操作内容をそのまま呼び出し側の変数に反映させたいことがあります(複数の値を関数の演算結果として受取りたい場合など)。これを参照渡し(call by reference)と読んでこれは対象の変数のアドレスを値渡しで引き渡すことで実現していますので、ここでポインタという概念が必須となってきます。 C言語はアセンブラレベルにかなり近い記述ができる言語です。アセンブラではすべてがポインタで記述されているというようなもの(やや大げさですが)ですので、ポインタが使えるのはC言語の大きな特徴になっています。しかし、ポインタが使えるから優れているというわけではなく、書こうとするプログラムにあった言語を選ぶことが重要です。ポインタという考え方をする必要のないプログラムも世の中には多くあります

yuki22
質問者

お礼

アドレスも奥が深いので簡単に考えてしまってはだめなのですね。やっぱりC言語の大きな特徴がポインタなのですか・・。 これからもがんばってみます。お返事ありがとうございました。

  • Bonjin
  • ベストアンサー率43% (418/971)
回答No.3

プログラムの経験がない(または少ない)からそのような疑問が出るのでしょうね。 実際にプログラムを組んでみれば疑問は解決すると思いますよ。 とりあえず、色々なプログラムを組んでみて下さい。

yuki22
質問者

お礼

うう、プロフェッショナルなお答えありがとうございます。 努力は何者にも代えがたいものだとおっしゃられるのですね。そのとおりだとおもいます。 お返事ありがとうございました。

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

> 実際、ポインタの必要性とはどのような時に必要なのでしょうか?? 逆に、ポインタなしで済むプログラムというのはまずありません。 "Hello, World!"を出力するだけでもポインタが不可欠です。putsやprintfに文字列を渡すときは、ポインタとして渡さなければならないからです。 どうしてもポインタなしで書くには、 putc('H'); putc('e'); ... のようにするしかありません。 char s[] = "Hello,World!"; int i; for (i = 0; s[i] != '\0'; i++)  putc(s[i]); とすればよいと思われるかもしれませんが、s[i]というのは、実際にはポインタ演算で、暗黙のうちに*((char*)s + i)のように扱われているのです。

yuki22
質問者

お礼

私の知らないうちにポインタに触れているとおもっても よろしいのでしょうか?? でしたら、すこしずつ理解の道を進んでるって思えてくるようでなんとなくうれしいです。 お返事ありがとうございました。参考にさせていただきます。

回答No.1

ファイルから数値の列を読み込み、大きい順に並び替えて出力することを考えます。 そのためにはまず数値の列を"何か"に格納しなければなりません。 int data[N]; さて、要素数がわからないのに、Nをどうしますか? …配列では解決できないシチュエーションはいくらもあります。

yuki22
質問者

お礼

未経験の壁がこの先いっぱいあるってことなのですね・・・。 お返事ありがとうございました。がんばってみます。

関連するQ&A

  • ポインタ

    C言語初心者の者です。 構造体のいくつかのメンバを取り出して 複数の構造体にそのメンバの値をコピーする方法 をポインタのアドレス操作を使ってやりたいのですが さっぱりわかりません。手順を教えていただきたいです。 よろしくお願いいたします。

  • 構造体とポインタ配列

    現在C言語の勉強をしております。 環境はwindowsXP、コンパイラはVC6.0です。 構造体と、ポインタの配列についてなのですが、 以下のような構造体が宣言されている時に、リスト構造にデータがいくつか入っているとします。 // 構造体 typedef struct address { unsigned char names[NAME_SIZE+1]; /* 名前 */ char tels[TEL_SIZE + 1]; /* 電話番号 */ struct address *prev; /* 前へのポインタ */ struct address *next; /* 次へのポインタ */ }Address, *a_pt; そのリスト構造を先頭要素か順番にポインタ配列に格納するには以下の方法ではおかしいでしょうか? /* ポインタ配列を用意する */ Address *array[MAX_COUNT]; /* top_ptは先頭のポインタです */ pt = top_pt; /* データがなくなるまで配列へ格納する */ while(pt != NULL){ array[count++] = pt; pt = pt->next; } /* 配列の最後はNULLとする */ array[count] = NULL; また、配列の中身を確認する方法としては、 printf("配列の中身:%s\n", array[0]->names); では、アドレスが表示されてしまうのかな・・と思ったら、accessViolationで落ちてしまいました・・・。 中身はどうしたらデバッグ出来ますでしょうか? そもそも、以下の2つは何か違いはありますか? Address *ptA[100]; a_pt ptB[100]; 皆さん、どうかよろしくお願いいたします。 理解不能な場合はご指摘ください。

  • アドレスとポインタがどうしても理解できない

    C言語を独学しているのですが、どの参考書読んでも、アドレスとポインタの理解ができません。アドレスとポインタを使わなくても別に開発できるのではないかと思います。どなたか、アドレスとポインタを初心者でも分かるように分かりやすく教えて頂けないでしょうか?

  • 関数へのポインタ渡しでの配列の初期化について

    はじめまして、C言語の基本的な質問をさせてください。 C言語で、外部関数へポインタで引数を渡す場合に、 関数に渡されるのはアドレスですよね? で、渡された関数側でそのポインタの配列の初期化を するときにはアドレスだけの情報だと、要素数がいくつ あるか分からず、領域の破壊をしてしまいそうな気が するのですが?いかがでしょうか? また、関数かなんかで、配列の要素数が分かる関数が あったような気がするのですが、それもアドレスだけ でわかるのでしょうか?

  • ポインタ

    C言語で、 int c[6]と配列を作って、 ポインタでc[100]を指定すると、COREがでますよね? これをやると、コンピュータがフリーズする場合があるのですがなぜですか?

  • C言語のポインタについて

    C言語のポインタ(配列もポインタに含まれるのか?)について質問です。 係数を行列形式で表される状態方程式などの2変数以上の方程式を解くプログラムにおいてよくポインタや配列が使われているのですが、なぜポインタや配列を使うのでしょうか?利点など詳しく教えていただけませんでしょうか。。。 どなたか分かる方よろしくお願いします(*_*)

  • C#で、Cのファイルポインタ(?)のような機能

    プログラムの初心者、かつ、C#の初心者です。  C言語では、ファイルポインタを関数から関数へ渡すことができると思うのですが、C# ではどのようにすれば良いのですか? C#を使っていますが、C言語のような構造でプログラムを書いています。  やりたいことは、ファイルポインタを渡しながら、各関数で、ドカドカと計算結果をファイル内に書いていくということです。  宜しくお願いいたします。

  • 配列とポインタの違いについて

    書籍などで配列とポインタの違いについて勉強していますが、未だによくわかりません。 C言語における配列とポインタの違いについて教えて頂けますでしょうか?

  • VBでC言語のポインタみたいな使い方?

    VBでC言語のポインタみたいに、一個の関数に配列のアドレスを渡して汎用的に使える方法は出来ますでしょうか? 宜しくお願いします。 PS:配列のデータは直線を描く為のデータです。

  • ポインタ変数とポインタのポインタ

    ポインタ変数の宣言 char *a[]; をしたとき僕の中では a[0],a[1]...という、ある文字列A,B,C...の最初のアドレスを指すポインタが、配列になっているものを宣言していると理解していました。 しかしこの次に、ポインタのポインタが出てきました。僕はこれを、 ある変数を指し示すアドレスのアドレスである、と理解しました。 この2つは1つめはいくつかのアドレスを指し示すもの、2つ目は1つのアドレスを指し示すものであるとして、僕の中で異なったものであると理解していましたが、参考書「C標準コースウェア」によると プログラムにおいて、関数でポインタ配列を受け取るときchar *p[]はchar **pとしてもよい と書かれており、またその実例として、 (9-5) #include <stdio.h> void disp (char *p[],int n){ int i; for (i= 1;i<n;i++){ printf("%s\n",p[i]); } } int main(void){ char *girl[] = {"Arica","Candy","Lisa"}; disp (girl,sizeof(girl)/sizeof(girl[0])); return 0; } というプログラムが書かれていました。 ここで一気に訳が分からなくなりました。 char *girl[] = {"Arica","Candy","Lisa"}; と宣言されているため、 girl[0]はAricaという文字列の最初のアドレスを指すポインタ、 *girl[0]はAricaという文字列を直接指し示していると解釈しています。 girlは{"Arica","Candy","Lisa"}という文字列の配列の最初のアドレスを指し示していると考えました。 sizeof(girl)を使った時に不思議なのですが、 girlはどのように配列の終わりを理解しているのでしょうか? (配列の要素数を渡していない点が不思議です。) また、 disp側が受け取ったのは*girl[]であり、いくつかのポインタの配列ですが、渡したものはgirlという要素数がないポインタ1つだけです。 そして最初の疑問が出てくるわけですが、*p[]を**pと書きかえてみると、 文字列のアドレスを示すgirlという名の1つのポインタを渡すと、pという名のポインタのポインタで受け取るというのも、よくわからなくなっています。 おそらくポインタ配列に対する理解がどこかでずれているようですが、自分でどこがわからないのかわからなくなっています。 どうかご教授ください。

専門家に質問してみよう