ポインターが危険と見なされるようになったのはいつですか?


18

プログラミング言語でのポインターの使用については、ポインターが危険であると一般に受け入れられるようになった(完全な「悪」または類似の拡大ではない場合)と考えられるようになったようです。

この考え方の変化の歴史的発展は何でしたか?特定の独創的な出来事、研究、または他の開発がありましたか?

たとえば、CからC ++、Javaへの移行を表面的に振り返ると、ポインタを参照で補完し、完全に置き換える傾向が見られるようです。しかし、実際のイベントチェーンはおそらくこれよりもはるかに微妙で複雑であり、それほど連続的ではありません。それらを主流言語にした機能は、おそらくずっと前に他の場所で生まれた可能性があります。

注:ポインター対参照対他の実際のメリットについては質問していません。私の焦点は、この明らかな変化の理論的根拠にあります。


1
それは教養教育の衰退によるものでした。人々は、すべてのCPUに含まれる、コンピューティングテクノロジーの最も基本的なアイデアの1つである間接参照を理解できなくなりました。

10
ポインター危険です。どうして考え方に変化があったと思いますか?言語機能とハードウェアの改善が行われ、ポインターなしでソフトウェアを作成できるようになりましたが、パフォーマンスの低下はありません。
モニカへの害をやめる

4
@DaveInCaz私の知る限り、特定の開発はポインターの発明でした。
モニカの害

5
@nocomprende:あなたが書いたのは事実でも証拠でもありません、ただ意見です。1970年のプログラマーの数ははるかに少なかったため、「間接参照」で現在の人口が良いか悪いかの証拠はありません。
whatsisname

3
ポインターは、最初から常に危険であると考えられてきました。それらをアセンブリ言語から高レベル言語に移行することは、単なる妥協でした。
フランクヒルマン

回答:


21

理論的根拠は、ポインターの代替案を開発したことです。

内部では、ポインター/参照/などは、メモリアドレス(別名ポインター)を含む整数として実装されています。Cが登場したとき、この機能はポインターとして公開されていました。これは、メモリをアドレス指定するために基礎となるハードウェアができることはすべて、ポインタを使用して実行できることを意味していました。

これは常に「危険」でしたが、危険は相対的です。1000行のプログラムを作成している場合、またはIBMグレードのソフトウェア品質手順を実施している場合、この危険に簡単に対処できます。ただし、すべてのソフトウェアがそのように開発されているわけではありません。そのため、よりシンプルな構造が求められました。

考えてみると、an int&とaはint* const実際に同じレベルの安全性を備えていますが、一方は他方よりもはるかに優れた構文を持っています。 int&また、レジスタに格納されているintを参照できるため、より効率的である可能性があります(アナクロニズム:これは過去に当てはまりましたが、最新のコンパイラは最適化が非常に優れているため、レジスタ内の整数へのポインタを持つことができます実際のアドレスを必要とする機能を使用することはありません(例++

我々が移動するとJavaの、我々はいくつかのセキュリティ保証を提供する言語に移動します。 CおよびC ++は提供していません。Javaは、正当な操作のみが実行されることを保証します。これを行うために、javaはポインターを完全に廃止しました。彼らが見つけたのは、実際のコードで行われたポインター/参照操作の大部分は、参照が十分すぎるほどのものだったということです。ポインタが本当に必要とされたのは、ごく一部のケース(配列の高速反復など)のみでした。このような場合、javaはランタイムヒットを使用してそれらの使用を回避します。

この動きは単調ではありませんでした。 C#はポインターを再導入しましたが、非常に限られた形式でした。これらは「unsafe」とマークされており、信頼できないコードでは使用できません。また、指すことができるものと指すことができないものについて明示的な規則もあります(たとえば、配列の終わりを超えてポインタをインクリメントすることは単に無効です)。しかし、彼らはポインターの高性能が必要な少数のケースがあったので、それらを元に戻しました。

また、そのような概念をまったく持たない関数型言語も興味深いものですが、それは非常に異なる議論です。


3
Javaにポインターがないと言うのが正しいかどうかわかりません。ポインターとは何か、またポインターではないことについて長い議論をしたくありませんが、JLSは「参照の値はポインターです」と言っています。許可されたポインターの直接アクセスまたは変更はありません。これはセキュリティのためだけではありません。GCには、オブジェクトがどこにあるのかを常に追跡するというビジネスから人々を遠ざけることが役立ちます。
ジミージェームズ

6
@JimmyJames本当。この答えの目的のために、ポインターと非ポインターの間の境界線は、ポインター算術演算をサポートしているかどうかでした。ポインター算術演算は、通常、参照ではサポートされていません。
コートアンモン-復活モニカ

8
@JimmyJames ポインターは算術演算を実行できるものであるが、参照はそうではないというCortの主張に同意します。Javaなどの言語で参照を実装する実際のメカニズムは、実装の詳細です。
ロバートハーベイ

3
一般に、CとC ++は、仕様に多くの「未定義の動作」を許可することで、この危険なクラブへのメンバーシップを自発的に受け入れていました。
rwong

2
ところで、ポインターと数字を区別するCPU あります。たとえば、IBM AS / 400の元の48ビットCISC CPUはそれを行います。実際、OSの下には抽象化レイヤーがあります。これは、CPUが数字とポインターを区別し、ポインターの算術を禁止するだけでなく、OS自体もポインターをまったく認識せず、言語も認識しないことを意味します。興味深いことに、これにより、元のAS / 400が1つのシステムになり、Cで高レベルのスクリプト言語からコードを書き直すと、桁違いに遅くなります
ヨルグWミットタグ

10

複雑なプログラムには、何らかの種類の間接指定が必要です(再帰的または可変サイズのデータ​​構造など)。ただし、ポインターを使用してこの間接化を実装する必要はありません。

高レベルのプログラミング言語の大部分(アセンブリではない)はかなりメモリセーフであり、無制限のポインタアクセスを許可しません。Cファミリーはここでは奇妙なものです。

CはBから進化しました。Bは生のアセンブリを非常に薄く抽象化したものです。Bには、単語という単一のタイプがありました。この単語は、整数またはポインタとして使用できます。メモリ全体が単一の連続した配列と見なされる場合、これら2つは同等です。Cはこのかなり柔軟なアプローチを維持し、本質的に安全でないポインター演算を引き続きサポートしました。Cの型システム全体は、後付けになっています。メモリアクセスに対するこの柔軟性により、Cはその主な目的であるUnixオペレーティングシステムのプロトタイピングに非常に適しています。もちろん、UnixとCは非常に人気があることが判明したため、Cはメモリへのこの低レベルのアプローチが実際に必要でないアプリケーションでも使用されます。

C以前のプログラミング言語(Fortran、Pascal、Cobol、LispなどのAlgol方言など)を見ると、それらのいくつかはCのようなポインターをサポートしています。特に、1965年にAlgol Wに対してヌルポインターの概念が考案されました。しかし、これらの言語はいずれもCに似た効率的な低抽象化システム言語にはなりませんでした。業界レベルの言語よりも多くの研究プロジェクトであり、Cobolはビジネスアプリケーションに焦点を当てていました。

ガベージコレクションは、50年代後半、つまりC(70年代前半)のかなり前から存在していました。GCが正しく機能するには、メモリの安全性が必要です。Cの前後の言語はGCを通常の機能として使用していました。もちろん、それは言語をはるかに複雑にし、場合によっては遅くします。これは、メインフレームの時代に特に顕著でした。GC言語は、研究指向(Lisp、Simula、MLなど)であるか、強力なワークステーション(Smalltalkなど)を必要とする傾向がありました。

一般に、より小さく、より強力なコンピューターコンピューティングとGC言語がより一般的になりました。非リアルタイムアプリケーション(およびその場合もある)では、GCが推奨されるアプローチになりました。しかし、GCアルゴリズムも熱心な研究の対象となっています。代替として、特に過去30年間で、GCを使用しないより優れたメモリの安全性もさらに開発されました。注目すべき革新は、RAIIとC ++のスマートポインターおよびRustのライフタイムシステム/借用チェッカーです。

Javaはメモリセーフなプログラミング言語であるために革新されませんでした。基本的に、GCed、メモリセーフなSmalltalk言語のセマンティクスを取り、C ++の構文と静的型付けと組み合わせました。その後、より優れたシンプルなC / C ++として販売されました。しかし、表面的にはC ++の子孫にすぎません。Javaのポインターの欠如は、C ++データモデルを拒否することよりも、Smalltalkオブジェクトモデルに負うところが大きいです。

したがって、Java、Ruby、C#などの「現代の」言語は、Cのような生のポインターの問題を克服するものとして解釈されるべきではありません。またはLisp。


4

私の経験では、ポインターは常に多くの人々にとって挑戦的な概念でした。1970年、私が通っていた大学にはバローズB5500があり、プログラミングプロジェクトにExtended Algolを使用しました。ハードウェアアーキテクチャは、記述子とデータワードの上部のいくつかのコードに基づいていました。これらは明示的に設計されており、配列が最後から抜け出すことを許可されずにポインタを使用できるように設計されています。

名前と値の参照、およびB5500アレイの機能について活発な教室での議論を行いました。すぐに説明を受けた人もいました。他の人はしませんでした。

後に、ハードウェアが暴走したポインタから、特にアセンブリ言語で私を保護しなかったのは、少しショックでした。卒業後の最初の仕事で、オペレーティングシステムの問題の解決を支援しました。多くの場合、印刷されたクラッシュダンプのみがドキュメントでした。私は、メモリダンプ内の暴走ポインタのソースを見つけるためのコツを開発しました。だから、誰もが「不可能な」ダンプを私に与えて、理解しました。発生した問題の多くは、他の種類のエラーよりもポインターエラーが原因でした。

私と一緒に仕事をした多くの人々は、FORTRANを書き始め、その後Cに移り、FORTRANによく似たCを書き、ポインターを避けました。Javaはポインターと参照を内部化しないため、Javaは問題を引き起こします。多くの場合、FORTRANプログラマーにとって、オブジェクトの割り当てが実際にどのように機能するかを理解することは困難です。

現代の言語は、タイプミスやその他のエラーから安全に保ちながら、「ボンネットの下」でポインターを必要とすることをはるかに簡単にしました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.