- ベストアンサー
ポインタについてアドバイスお願いします。
C言語の初心者です。 参考書でも書かれている事があるのですが、経験豊富な方でもポインタについて知ってるようで知ってないということがあります。って読んだことがあります。 実際、ポインタの必要性とはどのような時に必要なのでしょうか?? アドレス指定と言う風に、はじめはこの様な理解から入ると習いましたが、私には配列で十分間に合うのでは??って思っちゃうのです。 構造体の出力、アドレスの入れ替え、こんなときには便利なのかもしれませんが、C言語をやる上で絶対に必要・・、いったいなぜ??って考えちゃいます。 初心者なのに生意気な事言ってしまってもうしわけございません。 以前、私もそんな感じで悩んだ事あるって方がいらっしゃいましたら、ポインタとはこんな感じの時に初めて必要だと思うのだ!とご教授お願いいたします。 この様な質問に対しても、笑って答えてくださるような プロフェッショナルの方や、一般の凄い方のアドバイス、お待ちいたしております。 宜しくお願いいたします。
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
まず、ポインタの必要性云々の前に、ポインタ=アドレスという考え方を捨ててください。 ポインタは「型」です。 ただ、「○○型を指すポインタ」といったように、ある基準となる型があって、それをポイントするための型が「ポインタ型」なんです。 単に「ポインタ」といった場合、次の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 の言語仕様に縛られない「ポインタ」の使い方としては、リスト構造があります。 これは最初のうちはそれほど使う機会はないと思いますが、それなりの規模のプログラムを書くようになった場合、必ず必要になるものです。 リスト構造の詳細は割愛しますが、私はリスト構造こそがポインタがもっとも威力を発揮する使い方だと思っています。 長々と失礼いたしました。 ご参考にでもなれば幸いです。
その他の回答 (8)
- nofutureforyou
- ベストアンサー率9% (25/277)
>素人はC以外のプログラムから始めたほうが良くて、 >手を出すものじゃないと言う事なのでしょうか^^; いや、そういうことではありません。Cは初心者にも適していると思います。プロ向けの包丁のようなものです。s素人にも使えます。しかし、プロ向けの道具を素人の初心者教育に使うのですから、素人にわかりやすくはできていませんよ、ということです。先がとんがっている理由がわからないとか言っていてもしょうがないです。 というより、あなたがなかなか理解できないことをやらない理由を探しているように思えたのです。やるやらないの理由を探すのではなく、まず先に進むべきだというのがアドバイスです。
お礼
ごめんなさい。いろいろ調べてみて解らなくって質問させていただいたのですが、まだあなたの言うところまでの壁には達していないと言うことなのですね・・。 でも、知りたくて、ここでしたらいろんな考え方や知識をもっている方々がいると聞き、自分の努力は高スキルを持つ方には伝わりにくいかもしれなくて、時には叱られるかもしれないけど、導いてくれる方も沢山いるっていろんな友達に教えていただいて質問させていただいてます。 でも、まだまだ甘い考えだったのですね。 この度、nofutureforyouさんにお教えいただいた、”まず先に進むべき”と言う事も、進んでみれば今は解らなくってもいつかわかる時がくると信じてがんばってみようと思います。 初心者だからと言って、お気に触るような質問をしてしまっていたら本当にごめんなさい。 お返事ありがとうございました。
- nofutureforyou
- ベストアンサー率9% (25/277)
答えは、Cはそのようなものだ、ということです。もともとシロートのための言語ではありません。 K&R にもポインタがなぜ必要かということは書いてあります。
お礼
う~ん、素人はC以外のプログラムから始めたほうが良くて、手を出すものじゃないと言う事なのでしょうか^^; お返事ありがとうございました。
- parapara777
- ベストアンサー率16% (39/239)
個人的にはよく使うデータ構造のチェーン構造で ポインターの真価か発揮できていると思います。 データのサイズが不規則・・・配列サイズはかっきりしない。 データを途中に挿入するときにポインターの位置を入れ替えるだけですみ配列のようにデータをずらしてデータを 格納させる処理がいらなくなる。 なんかですかね。 このような処理を別の言語でやろうとしたときに ポインターが使えなくて配列で代用するときに 書くコードの量が大幅に違います。
お礼
データ構造のチェーンと言うところが、私レベルには何となくしかわからないのですが、いろいろ面倒を簡単にしてくれるって感じがつかめてきました。 お答えも私レベルにあわせていただいてありがとうございます。 いただいた情報を糧に、がんばってみます。 お返事ありがとうございました。
- PG_RankB
- ベストアンサー率40% (12/30)
久々に長文になりますが、ご容赦下さい。 凄く良い質問だと思いましたので。 私も学び始めて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
お礼
わわ、こんなに沢山の知識をお分けいただいてありがとうございます。 私的には、ポインタとは工場みたいなもので、そのなかにはいろいろいままでの作業工程などがぎっしり詰まっていて、必要に応じて工場に依頼するって感じにとらせていただいたのですが・・この認識、ぜんぜんちがうよ・・っと怒られちゃいますでしょうか?それとも、そのくらいから入っていってそこからスタートだよ・・って感じでお褒めいただけるのでしょうか・・? 仕事での厳しさも、驕らず、ソフトタッチでいてそれでいて厳しいと言う風にお教えいただけたとおもいます。 参考にさせていただきます。 お返事ありがとうございました。
- smat7
- ベストアンサー率46% (7/15)
勉強始めた方にポインタが変数として最初に明示的に現れるのは関数の参照渡しではないかと思います。 C言語の関数引数は値渡し(call by value)ですので、引数をもらった関数側でその引数変数を操作しても呼び出し側の変数に影響を与えません。これは関数の独立性の面で便利なのですが、時には操作内容をそのまま呼び出し側の変数に反映させたいことがあります(複数の値を関数の演算結果として受取りたい場合など)。これを参照渡し(call by reference)と読んでこれは対象の変数のアドレスを値渡しで引き渡すことで実現していますので、ここでポインタという概念が必須となってきます。 C言語はアセンブラレベルにかなり近い記述ができる言語です。アセンブラではすべてがポインタで記述されているというようなもの(やや大げさですが)ですので、ポインタが使えるのはC言語の大きな特徴になっています。しかし、ポインタが使えるから優れているというわけではなく、書こうとするプログラムにあった言語を選ぶことが重要です。ポインタという考え方をする必要のないプログラムも世の中には多くあります
お礼
アドレスも奥が深いので簡単に考えてしまってはだめなのですね。やっぱりC言語の大きな特徴がポインタなのですか・・。 これからもがんばってみます。お返事ありがとうございました。
- Bonjin
- ベストアンサー率43% (418/971)
プログラムの経験がない(または少ない)からそのような疑問が出るのでしょうね。 実際にプログラムを組んでみれば疑問は解決すると思いますよ。 とりあえず、色々なプログラムを組んでみて下さい。
お礼
うう、プロフェッショナルなお答えありがとうございます。 努力は何者にも代えがたいものだとおっしゃられるのですね。そのとおりだとおもいます。 お返事ありがとうございました。
- jacta
- ベストアンサー率26% (845/3158)
> 実際、ポインタの必要性とはどのような時に必要なのでしょうか?? 逆に、ポインタなしで済むプログラムというのはまずありません。 "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)のように扱われているのです。
お礼
私の知らないうちにポインタに触れているとおもっても よろしいのでしょうか?? でしたら、すこしずつ理解の道を進んでるって思えてくるようでなんとなくうれしいです。 お返事ありがとうございました。参考にさせていただきます。
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
ファイルから数値の列を読み込み、大きい順に並び替えて出力することを考えます。 そのためにはまず数値の列を"何か"に格納しなければなりません。 int data[N]; さて、要素数がわからないのに、Nをどうしますか? …配列では解決できないシチュエーションはいくらもあります。
お礼
未経験の壁がこの先いっぱいあるってことなのですね・・・。 お返事ありがとうございました。がんばってみます。
お礼
失礼なんてとんでもないです。 初心者の私にはとってもありがたいです。 ポインタ=アドレスって凄く強くもってしまってるので、その一途な概念は捨てないとなのですね・・。 もちろん、アドレス指定ってことも捨ててはならないってことも伝わってきました。 今回お教えいただけたことを思いながら、努力を重ねていきたいとおもいます。 お返事ありがとうございました。 がんばってみます。