- ベストアンサー
ポインタのメリット・デメリットとは?なぜポインタを使うのか?
- ポインタのメリットは、メモリを多く確保しなければならないオブジェクトについて、省メモリでインタフェースできることです。
- ポインタのデメリットは、アクセス可能な場所が多くなり、値が変更されやすくなること、可読性が低くなることです。
- なぜならば、熟練のC言語プログラマが昔ながらの記述を踏襲しているため、ポインタを使うことが多いからです。
- みんなの回答 (14)
- 専門家の回答
質問者が選んだベストアンサー
> 今の時代で、なぜメモリに介入するようなレベルから部品を作るのか? > それは企業から見て、投資対効果は得られているのか? なら「Cを使わない」という選択肢もあります。 おっしゃる通り、昨今のPC事情を考えれば、ポインタのような低次元な操作をしなくても、十分に実用的な速度で動作します。 ですから、必要が無いなら、C言語ではなく、もっと「高級」な言語を使えばいいのです。 しかし、Cが有効な場面はあります。 ・効率をもっと上げたい ・ハードウェアを直接操作したい そういうときには、やはり、ポインタを使わざるをえない場面が出てきます。 あと。 ポインタが無い言語でも、内部ではポインタ的なものが使われています。 例えば、Javaのオブジェクトは、オブジェクトの場所を表わす参照値が使われています。 メソッド呼び出しでは、参照値が渡されます。 これは、直接操作はできないものの、「ポインタ」そのものです。
その他の回答 (13)
わたしは、趣味の電子工作で、8ビットや16ビットのマイコン(組み込み系のマイクロコントローラ)の回路を自作しますが、使うのは専らアセンブラとC言語です。マイコンは、爪ほどの大きさですが、その処理能力は黎明期のパソコンをはるかに超えています。 とは言え、メモリの節約や処理速度の向上のため、ポインタ変数も多用しますし、関数ポインタも使います。 十分にパソコンの処理能力が上がれば不要になる用途も増えるかもしれませんが、その一方で処理能力が上がった分、能力の限界まで使いたい需要も出てくるのかなと思います。例えば、限られた時間で出来るだけ多くの処理をこなしたいなど。
お礼
ありがとうございます! 普段、PCのように十分な処理能力のマシン用にしか開発していなかったので、 「ポインタを使うところと、使わないところ」を分けた方がよいという発想しかありませんでした。 確かに、組み込み系だと、ポインタ変数の多用は不可避なのだろうと想像できます。 (関数の使用箇所、関数ポインタを使うとメモリの節約になるのですね。勉強不足でした。) ありがとうございました。 .
- wormhole
- ベストアンサー率28% (1626/5665)
>ビット数とか、short/longでそれぞれ違うようですが、 >intは、4バイト、0からFFFFFFFHまで(-214783648~214783647)のようですね。 すでに指摘がありますが、あるCの実装だけを見てそう思い込むのは間違いの元です。 >配列の要素数が、4バイト以上にはならない想定の、可変長配列だったのかもですね。 「配列の要素数が、4バイト以上~」ってどういう意味でしょうか。 まだ malloc(sizeof(int)) の意味を理解されていないようにしか思えませんけど。
お礼
理解していなかったので調べました。 malloc(sizeof(int))は、int型のサイズ分だけメモリを確保し、その確保したメモリのアドレスを返す。とわかりました。 ありがとうございます。 参考サイト http://www.kis-lab.com/serikashiki/C/C03.html .
- Tacosan
- ベストアンサー率23% (3656/15482)
考えてみてください. 配列を関数に渡すときに, 「C++ でいうところの参照渡し」でどのように渡しますか? ちなみに, 規格上「intは、4バイト、0からFFFFFFFHまで(-214783648~214783647)のようですね」とも言えない.
お礼
◆呼び元 intの配列は、 int* i_array = new int[size] で宣言 ◆関数定義側(ポインタの場合) void func(int *param) または void func(int param[] ) ◆関数定義側(参照渡しの場合) void func(int ¶m[配列の要素数のハードコーディング] ) というのはわかりました。 (参考サイト http://d.hatena.ne.jp/pknight/touch/20100317/1268796690) 値の変更がないときは、const付きの参照渡しにしたいと考えます。 いままでしっくりこなかった理由が少しわかりました。 C++では、ポインタを宣言しつつ、そこにn個のインスタンス化をすると、配列として扱えるようですね。 ありがとうございます。
- Glory_777
- ベストアンサー率50% (105/208)
ちょうど同じ事を考えていました。歴史的な変遷であると思います。 過去にホットな話題とされ、後継されずに消えてしまった事柄もあります。 少し懐かしい話をし、考察してみます。 1.「過去の常識を思い返してみる」 私が高校生だった頃は、今で言うPCはマイクロ・コンピュータと言われていました。 実稼動性能で1Mips程度。市販機種は安くても10万円くらいのお値段。 学生にとっては大変高価なものでした。 CPU、MPUと言われていたLSIのビット数は4ビットから8ビット。16ビットは業務用で30万~50万円。 まともなUnixを搭載した32ビットのフレームワークは2千万円くらいしましたね。 一般の人では関わる事自体が難しい。 私の所属するクラブでは、マイコンを自作することで何とかしておりました。 設計図を自分で引いて、ジャンクでチップを集めてくると原価が1万円程度になります。 安い基盤を買ってきて配線し、自作のマイコンを作るわけです。 これに、ディスプレイ用のIOボードやカセットインタフェース、キーボードを自作して、 マシン語モニタをプログラムし、最後は自分でゲームを作り文化祭に出展すると言うのが活動内容。 ご存知の通り、C言語を触るには、C言語と同時に作られたUnixを手に入れる必要があります。 となると、これはどこか別の世界の出来事に感じますね。 8ビット自作機種のメモリは16Kバイト程度で我慢していました。 製作途中で壊れる事があるためです。64kフル実装するためには勇気が必要。 アセンブラを使うためには、アルファベットが入力できるフルキーボードが必要でした。 これは高価なので、16進テンキーだけのキーボードを作ります。 これでもマシン語コードを直接叩き込めば何とか成ります。 ソフトウェアは各CPU、MPUのマシン語コードを暗記し、バイト数とサイクル数まで知り尽くし、 16進コードを直接打ち込んでいきます。 打ち込みながら総クロック数を頭の中で合計し、数サイクルでも早いプログラムを書きます。 同じ結果を生み出しても、不必要なサイクル数が混入している場合は、完全ではないとし、 後輩を厳しく指導した覚えがあります。 (この時代の人は、けっこうやってたはず) ソフトウェアにバグなどがあれば、一発で暴走します。打ち込んだプログラムは全て消えます。 それが故に、瞬間的にバイナリのイメージを脳内で作り、一気にプログラムとして吐き出す能力は、 プログラマの必須条件であり、その技能を持つが故に専門職であるとされていました。 (画面を見ながらコードを確認すると言う事自体が、ハードやソフトが無くてできない) 何も無くて、何でも自分で作る時代でありました。 いまでこそ当たり前に語られる内容は、当時は夢として語らっていたわけです。 このあと、DOS-V機が登場し、Windowsが登場し、ネットワークが一般に開放されます。 しかるにC言語は、私が学生の時代に既に存在していたわけですから、とても古い技術だと言えます。 2.「今の常識を疑ってみる」 俯瞰して見ると、私の先輩の世代にはキーボードと言うのが無く、 当然ながら言語と言うものもありません。 コンピュータとタイプライターを組み合わせ、パンチカードから脱却したアイデアは衝撃的です。 これがインスピレーションを刺激し、多くの中間言語やコンパイラ言語が生まれました。 (こうした言葉ですらデファクトであり、流行語です) ソフトウェアの開発手法も考案され、進化し、昨今の時代を形作ります。 そこで考えて見てください。ソフトウェアと言うのは、入力デバイスによって形態が変わるのです。 少し前には、グラフィックデバイスの進化とマウスなどの普及によって、 ”言語による開発”と言うもの自体が古いのではないか? とされていました。 (プログラミング言語と言うのはタイプライターが新しかった時代の技術ですから) このあたりからオブジェクト指向言語へ(つまり言語系の人が必死に粘ってアイデアを出してきた) と言うムーブメントがおきます。 しかしこのオブジェクト指向言語というのが、荒唐無稽な発想です。 物体であるオブジェクトを、シーケンシャルで直列的な言語で表現するのは乱暴です。 C言語以降の言語は、瞑想を極め、オブジェクト指向言語により生産性が低下した時期がありました。 多くの人が普及させるべきではなかったとし、無しにしようと動いています。 特にC++は不完全であり、生産性の低下を引き起こします。 3.「次の時代を考えて見る」 ほとんどの先端技術者は、言語による開発が、 いずれパンチカードの様に時代遅れになると直感しているようです。 (故に延命を企む提唱が多く、一般からかけ離れつつある) 「誰がこれを為すか?」 (つまりプログラミング言語を時代遅れとしてしまえるか) ライトウェアイと言語と言うのも、その焦燥から生まれたものだと思えます。 既読性や直感的という言葉が多用されるのも、これらの圧力に対する、 先行者の焦りと感じられます。 未来のビジョンを明快に出している提唱があるか分かりませんが、 不完全なソフトウェアの部品化が進んでいるのは間違いないでしょう。 ”プログラミング言語をなくす” 絵空事であっても、為さねば成らないとプレッシャーを、私も感じます。 矢継ぎ早に更新される開発フレームワークや提唱、仕様をみても、 こうしたストレスが蔓延していると思えます。 理想的には、自律性を持ったソフトウェア部品。 アーキテクチャにより概念化され、抽象化され、ハードとソフトの識別が意味を成さない世界。 これがM2Mなどと提唱されているわけでしょう。 RESTを基本としたHttpの世界は既に限界を向かえ、 XMLへ転換を果たそうとしていても、 マークアップ言語が言語であるが故に、古い概念の延命です。 ネットワークはP2Pを進化させた自律分散の概念に発展してゆき、 全てのシステムは自律性と曖昧さをもって、 有機的な接続をしていくものへと変化していくようです。 4.「質問の内容にもどる」 ほとんどのコンピュータは、「1.」で述べた時代と変わらない構造を持っています。 ノイマン型の技術では、論理回路を節約するために、命令と言う概念を導入しています。 これは、命令を受けて、内部の部品化された回路を接続しなおし、再構成する仕組みです。 命令を組み込むことで、擬似的に専用ハードをウェアを作り出すわけですね。 これをプログラミングと読んでいます。 ポインタと言うのは、このノイマン型の仕組みの中で生まれたテクニックの一つです。 それが故に、本来は非ノイマン型のオブジェクト(自律分散型)を表現するのに向いていません。 物体と言うのは、自律的に行動し、必要に応じて緩やかに連携をとります。 ところがポインタ(アドレッシング)こそがノイマン型の仕組み上もっとも重要なアイデアであり、 直感的という発想と相反するものなのです。 明快に間違いなく場所を指定する事で、本来存在していない回路をアーカイブから呼び出し、 組み替えて(自転車操業ですね)仮想的に大きな回路に見せかけます。 非ノイマン型のアーキテクチャの中で、プログラミングを生業とし、ソフトウェアの形態変化 を直感して、オブジェクトに進み、プログラムの本質であるアドレッシングを解決できない。 究極の自己矛盾を引き起こしているわけです。 そこで、ソフトウェアによるマネージャーを構築し、擬似的に非ノイマンを実現する階層を作ろうという 動きが生まれています。(しかし、マネージド言語を開発するところがまた見苦しい) 質問者の質問は、こうした背景からおきる素直な疑問です。 ポインタを使用するか、値を渡すかの区別に関して、 パンチカードの時代に比べればどちらも先進的です。 しかし、言語をマネージメントして、 非ノイマンに見せかけようと構想しているグループにとっては、都合が悪いのです。 これらはグラフィカルであり直感的にソフトが作れる時代を予感し、 今のプログラマが職を失なわないように、言語を延命する施策の一環ではないかと思います。 傘下のプログラマを指導しすぎるため、これに関して目的が分からないまま、 議論がされていると感じます。 とても商業的な意図で、各陣営がプログラマを指導しています。 本来のソフトウェアのプロは、顧客を想定して道具を選び、手段を選ばないはずです。 どの様な道具を使おうとも、計画性と試験により、製品として完成します。 しかし、時代の方向性として、製品単体が主力として売れる時代から、 部品が売れる時代に変わりつつあります。 ところが部品自体が自律的に連結しないため、部品を選定しコネクションする専門化が生まれ、 彼らが中間マージンを取って生活するようになりました。 これが行き過ぎて、原価を相当圧迫しているため、今はパッシングの対象となりつつあります。 先ほど述べた不完全な部品化の弊害です。TCRの観点では、”見えないコスト”ですね。 昨今は自律的に連結する部品が求められ、これの安全性をどう確保するかが興味の対象となっています。 どうすると安全なのかを認知できず、スコープやコンポーネント化を主張した時代があります。 これはあてずっぽうですね。本質的には、命令で組み合わる言語での開発に問題があります。 本当の回答を出すと淘汰されてしまうため、身内同士でパッシングする。 私は、いずれ消えるものとして、楽しむのが肝要と思っています。
お礼
ありがとうございます! 古き時代のお話、とても面白かったです。 >安い基盤を買ってきて配線し、自作のマイコン >16進テンキーだけのキーボード >打ち込みながら総クロック数を頭の中で合計し、数サイクルでも早いプログラム このような時代をご存じの方が、 ニューロコンピュータ、量子コンピュータ、DNAコンピュータ等、 未来のお話もされておられて、とても感服いたしました。 /********************************************/ 私は、プログラムは、企業の利益を上げることに役立ってこそ、 価値がでると捉えています。 少ない予算で、ビジネスを大きく変革したいです。 ⇒ 業務要件を、エンドユーザが実現できるようなプログラミング言語 (グラフィカルであり直感的にソフトが作れる時代)であったり、 業務を、ある程度柔軟にシステム化が可能なERPであったり、 >部品自体が自律的に連結しないため、部品を選定しコネクションする専門化が生まれ、 ⇒ SOAで、部品間・機能間のインタフェースがきっちり決まって 簡単に差し替えができるようなシステム の方向に、徐々にシフトしていると捉えています。 だからこそ、 今の時代で、なぜメモリに介入するようなレベルから部品を作るのか? それは企業から見て、投資対効果は得られているのか?を知りたいと感じて、 【なぜポインタを使うのか】の質問に至りました。 ポインタを使うべき状況とは、どういうときなのか? を理解したいと思います。
- wormhole
- ベストアンサー率28% (1626/5665)
>int *a = malloc(sizeof(int)); >のケースも見ます。 これから >intは、32767 ~ -32768 と決まっているのだから、サイズの確保のため、なぜわざわざmallocで書くのでしょうか? この発想になるのかわかりませんが。 intの取りうる値の範囲と、intのサイズを混同されていませんか? それに私はint 1つだけのために、わざわざmalloc()で領域確保することはしませんので理由を聞かれてもわかりません。 可変長配列を実現するための最初の仮の確保とかだったりはしませんか?
お礼
>intの取りうる値の範囲と、intのサイズを混同されていませんか? 混同していました。 ビット数とか、short/longでそれぞれ違うようですが、 intは、4バイト、0からFFFFFFFHまで(-214783648~214783647)のようですね。 >可変長配列を実現するための最初の仮の確保とかだったりはしませんか? 私も色々知識不足で読んでいてため、そうだったのかもしれません。 配列の要素数が、4バイト以上にはならない想定の、可変長配列だったのかもですね。
- Tacosan
- ベストアンサー率23% (3656/15482)
質問文が C と C++ のどちらを主なターゲットにしているのかわからないんですが, C では void increment(int &n) { n = n + 1; } なんて書けません. あと, 配列を関数に渡すときに (C++ でいうところの) 参照渡しはしないなぁ.
お礼
C++です。 >あと, 配列を関数に渡すときに (C++ でいうところの) 参照渡しはしないなぁ. なぜなのでしょうか?
- Tacosan
- ベストアンサー率23% (3656/15482)
規格上 「intは、32767 ~ -32768 と決まって」 などいません. そんな馬鹿なことを言うやつは, しばいてやっていいです. 「-32767以上32767以下の整数を表せる」ことは決まっている.
お礼
そうだったのですね。 ありがとうございます。
- KEIS050162
- ベストアンサー率47% (890/1879)
#5です。改めて読み返してみたら、いろいろ誤植などがあり混乱させてしまいましたね。 値参照 ⇒ 値渡し です。値渡しにするとどんどん複製が増えていくことになることです。 いろいろ検索していたら、とても簡潔に、また具体的に解説されている投稿を発見しました。 http://note.chiebukuro.yahoo.co.jp/detail/n3121 最後の「まとめ」のところにもありますが、C言語は低水準言語であり、アセンブラ言語ととても親和性が良いのですが、その理由としてポインタの使い勝手が良いという点であろうと思います。 個人的な見解をまとめられておられますが、私もとても共感出来る内容だと思います。 ご参考に。
お礼
ありがとうございます。 勉強させていただきます。
- KEIS050162
- ベストアンサー率47% (890/1879)
>よっぽどメモリを多く使う…(もしくは速度を重視…) その通りでしょう。 なんでもかんでもポインタにする必要はないですし、本来C言語は値を入れると、値が返る、というのが基本ですね。 >度々目にするソースは、何でもかんでもポインタ… そうでもないと思いますが、簡単な処理(単に値を入れると値が返る処理)では、単純な値渡しも多いと思います。 ただし、プログラムが大きくなり、扱うデータも複雑で大容量のデータを扱うことになれば、一度にやり取りするデータ(戻り値)も多くなり、ポインターは不可欠になってくるでしょうね。 簡単なサンプルコードを除けば、圧倒的にポインターを使ったコードが多いということにはなると思います。 また、古今を問わず、プログラム(プログラマー)は、出来るだけメモリ領域を節約し、高速化することが課題になってきますので、目にするプログラム類は必然的にポインタを多用しているということになるのだと思いますよ。 例えば、数十バイト程度の構造体でも、それを呼ぶ側、呼ばれる側、更にその下の関数郡まですべての階層で値参照で渡していたら、その各階層ごとに無駄なコピーを延々と作っていくことになりますし、そんな無駄な作り方はしないのが普通です。 受け渡しの処理速度も、いちいち参照地スタックに積んで、終わったらスタックを開放して、などを各階層で繰り返します。 >熟練のC言語プログラマが、昔ながらの記述を踏襲… 従って、これは当然そうなります。先人達が、処理速度・資源(メモリ)をもっとも効率化した結果が残っているので、多くの良く出来たコードはポインタが駆使されている、ということになりますね。 恐らく、未だ、扱うデータやその型などもあまり複雑なものは使われていないと思いますが、構造体、共用体、など使っていくうちにポインター(参照渡し)がいかに便利なものが(というか、ポインターを駆使しないと記述が無理なケースもあります)理解されることと思います。 ご参考に。
お礼
ありがとうございます! #6にてお礼申し上げます。
補足
>例えば、数十バイト程度の構造体でも、それを呼ぶ側、呼ばれる側、 >更にその下の関数郡まですべての階層で値参照で渡していたら、 >その各階層ごとに無駄なコピーを延々と作っていくことになりますし、 >そんな無駄な作り方はしないのが普通です。 値参照で渡すときって、渡されているのはアドレスだと思っていましたが、違うのでしょうか?? 値をコピーしているのは、値渡しだけという認識でした。 参照設定をして、int& a = b のとき、aの格納先のアドレスと、bの格納先のアドレスは、同じであるという認識でした。 >受け渡しの処理速度も、いちいち参照地スタックに積んで、 >終わったらスタックを開放して、などを各階層で繰り返します。 これは、他の回答者様の「関数の呼び元と、呼び先でのみ、値の変更が可能」というのと絡んでくるのでしょうか?? >>熟練のC言語プログラマが、昔ながらの記述を踏襲… >従って、これは当然そうなります。先人達が、処理速度・資源(メモリ)を >もっとも効率化した結果が残っているので、多くの良く出来たコードは >ポインタが駆使されている、ということになりますね。 確かに、、 今まで「読みにくくて嫌だな」とばかり思っていましたが、 慣れるのが不可欠なのかもですね。。
- aozakana_dha
- ベストアンサー率45% (76/168)
C言語の関数では、そのアウトプットを返却値でしか受け取れないのです。 なので、関数が複数のデータを出力させたい場合は、 (1)書き込み先アドレスを渡してそこに書き込んでもらうか、 (2)構造体にまとめて一つの変数としてリターンしてもらうか、 (3)おぞましくもグローバル変数に書き込んでもらうか、 そんなところになるかと思います。 そして関数の返却値は、たいていは処理が正常に終了したかどうか等のコードで 使用されてしまいますから、それ以外のデータを受け取る場合は、 (1)の手段、つまり関数の引数にポインタを使用することになります。
お礼
>C言語の関数では、そのアウトプットを返却値でしか受け取れないのです。 なんと!そうだったのですか! 他の回答者様の内容なども、しっくりきました。 だからポインタ渡し、参照渡しをよく見かけていたのですね。。
- 1
- 2
お礼
ありがとうございます。 処理スピードを追及したり、ハードウェアを直接操作したいときに使うというご回答、すごくしっくりきました。 ありがとうございます!