無論、私も代替可能である機構のすべてが言語のポテンシャルを広げることに貢献していないとは考えていません。C++ のクラスも C 言語を使って実装可能です(実際初期の C++ 処理系は C++ から C へのトランスレータでした)。しかし、C++ はカプセル化などの機能をプログラマに提供するという意味で、言語のポテンシャルを上げていると考えています。
> それは間違ってますね。The C Programming Language, 2nd ed, 1988. の索引で見ると call by
> reference の項目は唯一1ページだけ、27ページに言及があります。その内容は
> In C, all function arguments are passed "by value." ... This leads to some different
> properties than are seen with "call by reference" languages like Fortran or with var
> parameters in Pascal, ...
> と、参照渡しが C にはないことを明確に示しています。
Bacon 先生の論文は循環参照の解決というよりは、SMP 上でのスループットが mark & sweep(or compact?) とタメを張るのが主眼(だったはず)です。
ここで使われている巡回参照の解決方法は Rafael D. Linus の "A multi-processor shared memory architecuture for parallel cyclic reference counting", Microprocessing and Microprogramming, 1992 に基づくものです。
巡回参照の部分だけに興味があるなら、こっちを読んだ方が早いと思います。
いい方に考えると (スコア:0)
ポインタバリバリ使ってバギーなコード書かれるよりはましかも。
卒業したらJavaに移行して貰えばいいし。
でもコンピュータサイエンティストなら、アセンブラはやって欲しいけどね。
Re:いい方に考えると (スコア:1, すばらしい洞察)
でも、同じ問題は結局JavaだのRubyだのでも生じますよね。ポインタつーか参照については。
単に症状の出方が違う(いきなりコアダンプするか、NullPointerなんたらExceptionが出るか、の違いというか)だけで。
ポインタって、その概念的原理(つまり、なにかがなにかを「参照」する)と、 C独特の構文と、
の2者さえ理解すれば、逆にいえばたいして難関でもないと思います。
あの程度のことを理解*できない*ような人ならば、情報系の学生だろうが仕事だろうが趣味(フリーソフトとか)だろうが
こっちとしてはちょっと遠慮願いたいものだ、と思ったりします。
#ポインタって結局は、四角と矢印の組み合わせからなる図で、ぜんぶ説明できますよね。
#四角はメモリ領域(byteやWord)。矢印はポインタがどこかの領域を指してる様子。
そういう意味では、ポインタバリバリでバギーなコードを書いちゃった時点で、その人の
(Cに限らない)プログラミング能力を、俺は疑いますね。
ただ、Cがメインの学習カリキュラムってのもそれはそれで痛いってことならば、同意。
あとは、都市伝説よりきちんとした定義を学ぶ、ということを知ってる人でないと駄目ですね。
C言語なりポインタなりについて(というか他のどれでもそうだが)、
正しい理解をしている人は、たいていいつも正しいコードを書き、
あやふやでバギーな理解(^^;をしてる人は、書くコードもバギーです。
自分が抱えてる知識(やそれへの参照)が正しいかどうか?を、きちんと身辺整理できる人でないと、ちょっとね。
>でもコンピュータサイエンティストなら、アセンブラはやって欲しいけどね。
それってハードウェアサイエンティストのことですか?ソフト(上層)ではなくて?
エンジニア向けと一般向け (スコア:1)
サイエンティストがアセンブラやるべきかどうかは分からないけど、エンジニアならやっておくべきかも。
例えばここで話題になっているC言語のポインタにしても、最初に
なんていう風に端から抽象化して説明されちゃうと、かえって分かりづらくなると思うのです(JavaやLispのような動的メモリ管理をしてくれる言語だとそれでもなんとかなるかもしれませんが)。最初はむしろ具体的に、インデックスレジスタ(アドレスレジスタ)でアドレッシングするんだよ、というような、 という方に近い説明の方が直截的で分かりやすいと(私は)思います。「C言語とは高級アセンブラの一種である」なんていうことを言う人もいますが、後でCを使うのならアセンブラから入るのが反って分かりやすい(場合もある)というのが私の考えです。ま、この辺り、メモリ上の具体的なイメージさえ湧くなら、アセンブラやらなくてもC言語をやれば良いということも言えますけどもね。C言語と聞いてまず「ポインタ! 理解不能!」と思っちゃう人は、たぶんアセンブラを知らない人に限られるんじゃないかなあと思います。(検証したことはないですが)
ただ、以上は「泥臭いプログラミングもできなきゃいけない」エンジニアにとっては学ぶべき範疇と思いますが、大学の(コマ数の限られた)教養で学ぶプログラミングの知識として適切かというと、それはまた別の問題ですね。そういう意味では、個人的には、やっぱLispを推しちゃうかな。あるいはMLとかもいいかなあ。
ま、ぢつはLispもアセンブラ知ってた方が分かりやすいって面もあるけどネ^^;。なにせCARだのCDRだのの語源がそもそもアセンブラらしいし……。抽象的議論に耐え、かつぢつは結構泥臭い、てところがLispのミ・リョ・ク☆ :-) どうせ限られた数の言語しかやらないなら、後で訳もなく愛せてしまう言語を選ぶってのもいいカモネ! :-) :-)
その点C#はどうなのかな?
Re:エンジニア向けと一般向け (スコア:1)
そう?俺はそのほうが判り易いと、昔も今も思っているけど?
四角だの矢印だのの絵を、いきなり具体的な(具体的ゆえに、レジスタだのメモリだのといった
記憶領域の種類が複数有って不均質だったり、銘柄ごとに非互換だったりする)モデルに
マップして考えようとすると、その具体的ぶりに「振り回されて」しまうと思います。
俺もCの後で少しアセンブラやったりしましたが、
アセンブラをやる前と後とで、Cを書いたときに出すバグの量が
劇的に変わった(減った)なんてなことは、無かったです。
つまりCへの理解モデルは、アセンブラに影響されなかった(される必要がなかった)わけです(^^;
「メモリつーものがあるらしいぞ。1次元配列らしいぞ。」「ふーん」
という程度の具体性しかないモデルで理解しておくほうが、楽でしたね。
実際、C言語がメモリについて担当している抽象化は、まさにそういう部分なのであって。
実際、例えば、CPUが備えているIndexレジスタの数を遥かに超えた数のポインタ変数を作れるC言語
を理解するにあたって、Indexレジスタ(とメモリとの値のやりとり!(レジスタが指す領域の話じゃないですよ))を
いちいち意識するほうが「理解」が進むとは、ちょっと思えません。
なお、四角の絵には工夫があります。#って、べつに俺がした工夫ではなく、たしかK&Rでもそうなってたような…
「連続した」メモリ領域は、四角もまた「連続して」並べて描く。
「連続しない」領域は、四角も離して描く。
こうして、絵に「適度な(過剰でない)」具体性を与えることで、それは十分な情報を人に伝えられる媒体になる、と思えます。
過剰な具体性はウザイだけです。
>メモリ上の具体的なイメージさえ湧くなら、アセンブラやらなくてもC言語をやれば良いということも言えますけど
>もね。C言語と聞いてまず「ポインタ! 理解不能!」と思っちゃう人は、たぶんアセンブラを知らない人に限られるんじゃないかなあ
「抽象的な」イメージでもOK(なことも有る;-P)と思います。
アセンブラ知ってればポインタOKってのはTRUEなのかも知れません(俺は知りません)が、
逆にポインタOKになるための条件としてアセンブラが有るわけでは、ないと思います。
>ま、ぢつはLispもアセンブラ知ってた方が分かりやすいって面もあるけどネ^^;。なにせCARだのCDRだのの語源がそもそもアセンブラらしいし……。
出自に絡んでたまたま設定された用語の1つ2つですから、関係ないと思うけどな;-P
上記の抽象化されたメモリのモデルで、(C以上に)あっさり説明可能であって、それ以上は要らないっす。
Re:エンジニア向けと一般向け (スコア:1)
Re:エンジニア向けと一般向け (スコア:1)
「ポインタ」は「参照」に非ず (スコア:1)
「ポインタ」は「参照」としても利用できるように設計されていますが、「参照」よりも遥かに広いポテンシャルを持った機構です。ゆえにバグの温床となる。
「ポインタ」はメモリアドレスを示す「値」(+型情報)、「参照」はもっと抽象的なオブジェクトやクラスなどを参照することができるデータ。
例えば、言語屋さんから見ると C 言語には "call by reference" がないように見えるそうです。C 言語が "call by reference" と称しているのは、アドレスを渡す "call by value"。
> でも、同じ問題は結局JavaだのRubyだのでも生じますよね。ポインタつーか参照に
> ついては。単に症状の出方が違う(いきなりコアダンプするか、NullPointerなんたら
> Exceptionが出るか、の違いというか)だけで。
いいえ。
例えば、下記のようなコードの問題は Java では生じません。記述もできません。
これは大きな違いです。
int a = 0;
int b = 0;
int c = 0;
int* p = &b;
*(p + 1) = 1;
printf( "a=%d, c=%d\n", a, c );
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:2, 参考になる)
>と称しているのは、アドレスを渡す "call by value"。
C言語の文法と挙動を正確に理解できるようになるために学ぶ段階において、
C言語がcall by referenceだなんて考えたらドツボにはまっちゃうわけで。
あくまでcall by valueと考えるべき。
そう考えることで、初学者にもベテランにも言語屋さんにも、誰にとっても
矛盾なく使える(かつ、なにか重要なものを欠落させるわけでもない)モデルが
頭ん中に構築できるわけで。
言語屋さんには見える、という狭い話じゃなくて、誰にでもそれは事実であるのでは?
>例えば、下記のようなコードの問題は Java では生じません。記述もできません。
>int* p = &b;
ああ。これは失礼。
CのポインタとJavaとかの参照との違いとして、「データじゃなく変数(データの器)を」指すということが
可能か不可能か、という差がありますね。
他の変数に代入されてる値にアクセスする、ということが、この機能の有無によって、可能か不可能か変わってくる。
でも逆にいえば、それしか差は無いわけで。
あ。ごめん。もうひとつだけ差があった。Cのポインタには「演算」が定義されてる。
++とかが出来てしまう。
で、それらの機構を「参照」に盛り込むことが、べつに論理的に不可能なわけではない。
ただ実用性において無意味だから誰もやらない(Cを真似ない)だけで。
余談:
C++の参照と、それ以外の多くの言語(JavaとかRubyとかDelphiとかLispとか…)の参照とを混同している人が
近傍に居るようですが(^^;、まぁそれはそのぉ…って感じ。
C++って、用語の変さという意味でも、痛い言語なんだよな。時として他の言語を学ぶのを妨げかねない。
Re:「ポインタ」は「参照」に非ず (スコア:2, すばらしい洞察)
ただ,現実に多くの人がポインタの理解で苦しんでいるのは事実のようです.またC/C++のポインタはプログラムの解析を行う際にも様々な困難を引き起こすのですが,そのことも使うのが難しいということを裏付けていると思います.
Re:「ポインタ」は「参照」に非ず (スコア:1)
そりゃまあそうですが、ポインタについては、難しいですかね?
K&Rを正しく(=変に我流で憶測せずに字面どおりに寸分(?)違わず)理解すれば(^^;、べつに躓いた記憶が無いんですけど。
#逆にいえば、そういう「完全に信頼した上での」読み方が出来る本(や先生)がもし無ければ、辛いですけど。
ポインタの難しさは、なんというか、Fragileというか、触り方を間違えたら壊れる、という難しさは感じますね。
しかし、それは個々の手の動きレベルの問題で、それ以前の概念の段階では、別に難しくないような。
前述の四角モデル以上のものを必要だと感じたことが無いです。
#Cのポインタまわりの文法という意味では、難しいというか、煩雑というか、わけわかだとは思いますけどね(T_T)
>G7さんはおそらくJavaやLispに
>おける参照が,実装レベルでどのように(ある制約にしたがった)ポインタとして扱われているかを理解しているので,あま
>りC/C++でも困っていないのではないでしょうか(憶測モード).
うーん。動く言語(文法も内部表現も)を作った事が有る(実装手段はC)からには、理解してるってことになるんでしょーけど、
そういう問題じゃないような気がするなあ。
CもJavaも、全く同じじゃなく微妙に違うものの、どちらも四角モデルで表現できちゃうわけで。
>ただ,現実に多くの人がポインタの理解で苦しんでいるのは事実のようです.
現象としてそれは真ですが、理由の説明が正しいかどうかは別じゃないかな。
俺は、もっと違う理由によって、ポインタに躓く人が生まれてるんじゃないか、と思っています。
で、俺の邪推では、それは、誤解の伝播なのではないかと。つまり教える側が誤解してて
その誤解が生徒にも伝播する、のサイクルを繰り返してると。
あとは別口でも書いたような「都市伝説」問題かな。誤解を正さないまま放置してるという。
#ポインタは難しいというの自体が伝説だと思いますね。それを聞いた人がポインタの理解を途中で放棄しちゃう。
そしてその根底には、理解を我流の憶測で補完しようとする日本人(?)の悪癖の問題が、横たわっていると思う。
つまり、正しく(かつ出来れば平明に)教えてる人の言葉を、正しく聞けば、
そう簡単にハマらないようなプログラマになれると思うんですけどね。とりあえずポインタについては。
そういや逆にいえば、Cポインタだけじゃなく、Javaとかでいう意味の参照もまた、
理解に苦しんでいる人が結構居るようですよね。うーん。困ったもんだ。
#WWWが流行してるんだから、参照というもの自体が人間に理解しにくい、という訳じゃ無い筈なんだが…
>またC/C++のポインタはプログラムの解析を行う際にも様々な困難を引き起こすのですが,
>そのことも使うのが難しいということを裏付けていると思います.
うーん。解析ってのは、そのプログラムにおけるそのポインタの意味を「読む」ほうの問題ですよね?
一方でプログラマの卵(?)が悩むのはポインタの意味を「書く(作る)」ほうの問題ですよね。
読むのが楽(大変)なものと、書くのが楽(大変)なものとは、必ずしもイコールではない、のではないでしょうか?
Re:「ポインタ」は「参照」に非ず (スコア:1)
> が可能か不可能か、という差がありますね。 他の変数に代入されてる値にアクセスする、ということ
> が、この機能の有無によって、可能か不可能か変わってくる。
>
> でも逆にいえば、それしか差は無いわけで。
それしか、、、ですか。
私はコンパイラ屋で GC 屋ですから、この 2 つの間に 言語のポテンシャルを完全に
左右する違いを見ているのですが、、、
> あ。ごめん。もうひとつだけ差があった。Cのポインタには「演算」が定義されてる。
> ++とかが出来てしまう。
C 言語のポインタ演算は syntax sugar にすぎないでしょう。
ないと不便ですけど。
#define POINTER_INC(P) ((P)=(((char*)P)+sizeof(*P)))
p.s.
> 余談:
> C++の参照と、それ以外の多くの言語(JavaとかRubyとかDelphiとかLispとか…)の参照とを混同して
> いる人が近傍に居るようですが(^^;、まぁそれはそのぉ…って感じ。
本当だ。しきい値を上げているので分かりませんでした。
C++ の「参照」が、「ポインタ'」にすぎないという点には同意。
p.p.s.
JDK1.4 以降の Java には「ポインタ」が入ってきます。
Buffer というやつ。トホホ。
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:1)
>左右する違いを見ているのですが、、、
まあそうなんですが、逆にいえば、一言で説明(要約)できるってことは、
その1つの概念を使う(or使わない)べきであるタイミングに
使う(使わない)という選択を明示的かつ自由に行うことが
容易かも知れないことが期待できる(^^;わけで、
実際これについては期待通りなのではないかと実感してまして。
#実感したころにはもう遅い、という説も有りそうだが。
>C 言語のポインタ演算は syntax sugar にすぎないでしょう。
>#define POINTER_INC(P) ((P)=(((char*)P)+sizeof(*P)))
整数との加算、を含めて「ポインタ演算」と呼ぶと記憶していますけど。
++はその派生形っすね。
あとポインタ同士の差という演算も。あれは「ポインタ+整数=ポインタ」という式を
ちょいと移項(懐かしい言葉だ)すれば「ポインタ-ポインタ=整数」という式になるわけで、
説明も理解も(算数を知ってる人なら)簡単。
>C++ の「参照」が、「ポインタ'」にすぎないという点には同意。
C++の「参照」って、ポインタとも所謂参照とも、似てるようで違うようで、
「なんでこんなふーになってんの?」って感じで、いろいろ面倒に感じます(^^;
>JDK1.4 以降の Java には「ポインタ」が入ってきます。
あれ?1.4はまだ全然触ってなかったんですが、Bufferってポインタなのでしたっけ?
生データアクセスを「ラップ」したスマート配列っぽいものかと思ってたんですが…
後で見とくとします。
http://java.sun.com/j2se/1.4/ja/docs/ja/api/java/nio/Buffer.html
http://java.sun.com/j2se/1.4/ja/docs/ja/api/java/nio/ByteBuffer.html
#あ。日本語manはもう出来てたのか。
Re:「ポインタ」は「参照」に非ず (スコア:1)
>>#define POINTER_INC(P) ((P)=(((char*)P)+sizeof(*P)))
>
> 整数との加算、を含めて「ポインタ演算」と呼ぶと記憶しています
> けど。
> ++はその派生形っすね。
あ、すいません。本当に書きたかったのは、下のような手法。
#define POINTER_INC(P) ((P)=(((intptr_t)P)+sizeof(*P)))
ポインタが値であり、適当な整数型との相互キャスト機能を持っていれば、他のポインタ演算は全部代用可能。
>>JDK1.4 以降の Java には「ポインタ」が入ってきます。
> あれ?1.4はまだ全然触ってなかったんですが、Bufferってポインタなのでしたっけ?
> 生データアクセスを「ラップ」したスマート配列っぽいものかと思ってたんですが…
Buffer を Java 単体で使う場合は、その認識でもいいと思います。
でも 本当に Buffer が必要なのは、JavaVM と外部のプログラムとのデータのやり取り。
Buffer は、JNI の先にあるプログラムから見るとポインタでアクセス可能なメモリ空間、同時に Java プログラムからみると配列的なデータ構造をもったオブジェクトに見えます。
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:1)
intptr_tって見覚えないなぁと焦って(^^;探したら、これC99の新機能っすか。
http://seclan.dll.jp/c99d/c99d09.htm
なんだかなあ。整数とポインタの相互代入というCのもうひとつの暗部(笑)に、お墨付きを与えたんですね。
所謂ポインタ演算については、使いにくいかも知れないとはいえ、いちおう算数的(?)にマトモだとは思えますが、
整数とポインタの直接変換ってのは、最初から与太だなとしか思えなかったです。与太ゆえに便利だと言ってしまえばそれまでですが。
>ポインタが値であり、適当な整数型との相互キャスト機能を持っていれば、他のポインタ演算は全部代用可能。
ポインタは常に「値」であるような気が…。値でないポインタって何でしたっけ?
あと、C99になってやっと導入された仕組みを使ってはじめて(合法に安全に移植可能に)「全部代用可能」になる、
といわれても、いまいち釈然としないものを感じる古さ(笑)が、俺には有ります。ちょー後付けだなというか。
intptr_t ってそんなにマイナーかしら? (スコア:1)
intptr_t ってそんなにマイナーかしら?
C90 でも OS 側で準備している環境は多いと信じていましたが。
32ビットと64ビット環境を混在させている Solaris や IRIX にはありますし、
Tru64 UNIX でも使えたはずです。HP-UX や AIX にもあると聞くのですが。
# C99 規格の先取りなのか、すでに存在する手法を C99 が取り入れたのか、
# 歴史的経緯はよく知りません。
ポインタを整数型で受ける場合に、直に int 型で受けたりはしないでしょうから、
マクロなり typedef なりで別名をつけますよね。
みなさん、どういう名前を付けているのでしょうか?
ptrdiff_t で受けているのかしら?
p.s.
よく考えれば intptr_t は Ver6 以前の VC にはなかった。
コンタミは発見の母
ポインタ型 ←→ 整数型 (スコア:1)
> を与えたんですね。
前からお墨付きはあったのでは?
ポインタが適切な長さの整数型にキャストできること、その逆変換ができること、
相互変換によって情報が失われないこと、は C コンパイラが保証すべきことだったと記憶していますが。
> > ポインタが値であり、適当な整数型との相互キャスト機能を持っていれば、他のポイ
> > ンタ演算は全部代用可能。
> ポインタは常に「値」であるような気が…。値でないポインタって何でしたっけ?
> あと、C99になってやっと導入された仕組みを使ってはじめて(合法に安全に移植可能
> に)「全部代用可能」になる、といわれても、いまいち釈然としないものを感じる古さ(笑)
> が、俺には有ります。ちょー後付けだなというか。
移植性が問題なのでしょうか?
コンピュータ言語の表面的な文法の話ではなく、ポテンシャルの話をしているのだと思っていたのですが、、、
もう1度繰り返すと、
#148039 [srad.jp]
> あ。ごめん。もうひとつだけ差があった。Cのポインタには「演算」が定義されてる。
> ++とかが出来てしまう。
> で、それらの機構を「参照」に盛り込むことが、べつに論理的に不可能なわけではない。
> ただ実用性において無意味だから誰もやらない(Cを真似ない)だけで。
ポインタがメモリアドレス値を格納するシステムだとすれば、必然的にアドレス値を整数とみなせて相互変換する機能があるはずです
(でないと決めれたアドレスにある VRAM へのアクセスのような使い方ができなくなる)。
もし その機能を持っていれば、++、-- のような他のポインタ演算を代替できます。
ゆえに、C 言語のポインタ演算は「ポインタ」と「参照」を分ける主要な理由とは考えられないということです。
p.s.
私のポインタに対する感覚を把握してもらうためにいいますが、
私は 4 バイトアライメントのシステムではポインタの下位2ビットが必ず 0 になるので、そこをフラグ領域として転用するようなプログラムを書いたりしています。
上位30ビットだけで情報を保持できる「ポインタ」の無節操さと、「参照」を同一視することが私にはできかねます。
コンタミは発見の母
Re:intptr_t ってそんなにマイナーかしら? (スコア:1)
環境依存の分については、極力「目を向けない」ようにしていますので(^^;、知りませんでした。
#自分でdefineしてたら同罪だろ、という指摘は正しいとは思いますが…
># C99 規格の先取りなのか、すでに存在する手法を C99 が取り入れたのか、
環境依存サイドの需要を聞いて、のちに規格が仕組みを供給するようになる、ってのは
まあ珍しいことではないでしょうね。特にこういう感じ(^^;の機能については。
>みなさん、どういう名前を付けているのでしょうか?
そういやGLibにも有りませんでしたっけ。gpointerだっけ?
Re:ポインタ型 ←→ 整数型 (スコア:1)
そでしたっけ?だったら誤認でしたのですんません。
>移植性が問題なのでしょうか?
>コンピュータ言語の表面的な文法の話ではなく、ポテンシャルの話をしているのだと思っていたのですが、、、
移植性も無い、状況しだいで動いたり動かなかったりする(^^;コードに
「潜在」能力を期待するわけにはいかないと思います。
むしろ「潜喪失」能力ってんでしょかね。いつか意図しない形で無くなりかねない(^^;
>ポインタがメモリアドレス値を格納するシステムだとすれば、必然的にアドレス値を整数とみなせて相互変換する機能があるはずです
そういう問題とは別だと思います。
というのは、ポインタに整数を加減できるという性質と、ポインタを整数にCASTできるという性質は、
別々に成立(or不成立)させることが可能だからです。
整数とは別の、あくまでポインタという世界があって、そいつが整数との加減や
++(というか、これも整数との加減の亜種ですね。+=1なのですから。)を定義されている。
ソレが証拠(?)に、ポインタを整数にCASTしたものを++して再び元のポインタにCASTしたものと、
ポインタをそのまま++したものとでは、しばしば(詳細は略していいですね?)同じ値になりません。
整数との加減算は、ポインタを「進める/戻す」という性質のために存在し、
CASTとは別問題であるはず。
>上位30ビットだけで情報を保持できる「ポインタ」の無節操さと、「参照」を同一視することが私にはできかねます。
少なくとも「保持できる」ことは、関係ないし、無節操でもなんでもないと思いますし。
そういやrubyは、それこそ使われない下位bit(最下位1bitだが)を立てることで、ポインタもとい参照を整数に見なすそうです。
Re:ポインタ型 ←→ 整数型 (スコア:2)
>> コンピュータ言語の表面的な文法の話ではなく、ポテンシャルの話をしているのだ
>> と思っていたのですが、、、
> 移植性も無い、状況しだいで動いたり動かなかったりする(^^;コードに
> 「潜在」能力を期待するわけにはいかないと思います。
> むしろ「潜喪失」能力ってんでしょかね。いつか意図しない形で無くなりかねない(^^;
なぜ、私の話をそらすのかなぁ?
コードのポテンシャルの話をしているのではなく、言語機構のポテンシャルの話をしているのです。
過去のコメントの中に、G7さんは「独自の言語を設計し、その実装を行った」とありましたが、その際に言語の中にある別の機能で代用不能な機能と、便利な言い換えを区別して設計しませんでしたか?
>> ポインタがメモリアドレス値を格納するシステムだとすれば、必然的にアドレス値を
>> 整数とみなせて相互変換する機能があるはずです
> そういう問題とは別だと思います。
なぜ?
> 整数とは別の、あくまでポインタという世界があって、そいつが整数との加減や
> ++(というか、これも整数との加減の亜種ですね。+=1なのですから。)を定義されて
> いる。整数との加減算は、ポインタを「進める/戻す」という性質のために存在し、
> CASTとは別問題であるはず。
「目的」や「使用方法」の問題ではないのです。
C 言語の for/while/do-while 文は、if と goto 文とラベルがあれば代替可能です。
for/while/do-while はループをあわわすために存在します。しかし、言語のポテンシャルを増やしている分けではないでしょう?
# for/while/do が不要だといっているわけではない。
無論、私も代替可能である機構のすべてが言語のポテンシャルを広げることに貢献していないとは考えていません。C++ のクラスも C 言語を使って実装可能です(実際初期の C++ 処理系は C++ から C へのトランスレータでした)。しかし、C++ はカプセル化などの機能をプログラマに提供するという意味で、言語のポテンシャルを上げていると考えています。
> というのは、ポインタに整数を加減できるという性質と、ポインタを整数にCAST
> できるという性質は、別々に成立(or不成立)させることが可能だからです。
問題にしているのは、「ポインタに整数を加減でるという性質」と「ポインタを整数にCASTし、その逆変ができる」という性質です。妙なところで端寄るのはやめましょう。
上の2つの機能は独立ではあるが同条件ではありません。「ポインタを整数にCASTし、その逆変ができる」を言語仕様から外すと、処理系非依存の依存の機能を用いない限り、言語のポテンシャルは小さくなります。つまり、実現できない機能がでてきます。
> ソレが証拠(?)に、ポインタを整数にCASTしたものを++して再び元のポインタにCAST
> したものと、ポインタをそのまま++したものとでは、しばしば(詳細は略していいで
> すね?)同じ値になりません。
詳細をあげください。無論、char 型より大きい方のポインタでは一致しないなんては駄目ですよ。私はすでにポインタの指す型のサイズ(sizeof(*pointer))を加えた例を挙げているのですから。このとき両者は同じ値ですね。
それとも、ポインタは参照と型の両方を持っている点が違うといいたいのですか? ポインタ演算を行っている場所では、そのポインタの指す型がどこでも手に入りますよ。つまり、ポインタを整数にキャストして足す時とした場合、その足し幅が手に入るということです。なぜなら、この型の情報を持っているのは実行コンテキストではなく、プログラムのフローだからです。
p.s.
追加しおきますが、ポインタを整数型にキャストして演算し戻し場合と、ポインタ演算の結果が異なる可能性は確かに存在します(C 言語の仕様上)。
おそらく G7 さんが意図したこととは異なるし、この議論とは関係ないので詳細は述べませんが。
# *((int*)0) = 0;
# どのメモリアドレスに値を書き込もうとしているか分かりますか?
p.p.s.
#147884 [srad.jp]の
>>ポインタバリバリ使ってバギーなコード書かれるよりはましかも。
に
> でも、同じ問題は結局JavaだのRubyだのでも生じますよね。ポインタつーか参照に
> ついては。 単に症状の出方が違う(いきなりコアダンプするか、NullPointerなんたら
> Exceptionが出るか、の違いというか)だけで。
と答えたあたりから疑っていたのですが、G7 さんはC言語から Java へ乗り換えることによって、どのようなC言語特有のバグがどのように減るのか
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:1)
ちょっと工夫すると、参照型とポインタは交換可能です。
#include <stdio.h>
int goo( int& a, int& b ){
// a b が実体を参照しているとは限らない
printf("a=%d, b=%d\n", a, b );
}
int main(int argc, char** argv){
int a=0, b =1;
goo(a,b);
(*(int (*)(int*, int*))goo)(&a, &b);
(*(int (*)(int, int))goo)(1234,5678);
return 0;
}
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:1)
その証拠に、ポインタが原因となって発生するバグのほとんどが、参照型でも発生するでしょう?
無論、C++ がポインタやキャストを禁止すれば、参照型は「ほぼ」参照となりえますが、現実的にこれらの機構はある。
さらに言えば、実装的には参照型はポインタに皮を被せているだけでしょう。
> 一旦参照型変数を初期化したオブジェクトを、他のオブジェクトに置き換えれるようなら、
> (参照というよりは)ポインタ的と言えるだろうけどそれはできないでしょ?
別に指し直せないことが「参照」の要件ではないのでは。。。
とはいえ、参照型のインスタンスが別のオブジェクトを指し直す危険があることは指摘しておきます。
#include <stdio.h>
int& foo(int& dummy){
int local_var = 0;
return local_var;
}
int& goo(int& r){
int another_local_var = 0;
printf("Before: %d\n", local_var );
r=1;
printf("After: %d\n", local_var );
return r;
}
int main(int argc, char** argv){
int dummy;
int& r = foo(dummy);
goo(r);
return 0;
}
このバグが発火するかどうかは実装依存ですが、この手のバグはあちらこちらに散見します。
コンタミは発見の母
訂正 (スコア:1)
int another_local_var = 0;
printf("Before: %d\n", another_local_var );
r=1;
printf("After: %d\n", another_local_var );
return r;
}
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:1)
他の人も言っていますが、それ、関数のシグネチャをキャストで騙してるだけでわ?
てゆーか、その騙しが成立するかどうかすら、不定だか実装依存だか、でわ?
ポインタと参照が「たまたま」似た実装になってるかどうかに依存してるよね、そのコードって?
>(*(int (*)(int, int))goo)(1234,5678);
この行って、コアダンプする、で正解ですか?
当方(cywinでg++)ではコアダンプしました。
Re:「ポインタ」は「参照」に非ず (スコア:1)
> 他の人も言っていますが、それ、関数のシグネチャをキャストで騙してるだけでわ?
> てゆーか、その騙しが成立するかどうかすら、不定だか実装依存だか、でわ?
> ポインタと参照が「たまたま」似た実装になってるかどうかに依存してるよね、そのコードって?
参照をセットしているほうは騙しですが、関数 goo からみると、参照だと思って受け取った引数に「参照」ならざるものが入っているのが問題です。
騙しを使わなくても #148379 [srad.jp]のような原因で、いつのまにか invisible (*1) になっていうパターンはあります。
スタック上に取った大きめのインスタンスを、コピーせずに使いまわそうと参照で受け渡しているうちに、原因不明のバグが発生したという経験は G7 さんにはありませんか?
> >(*(int (*)(int, int))goo)(1234,5678);
> この行って、コアダンプする、で正解ですか?
> 当方(cywinでg++)ではコアダンプしました
エラーとなるのが正解です。
C++ の参照には この程度のチェックもないよ、という例です。
# でも、この例は他の言語でも対処できないかも...
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:2, 参考になる)
> (何故なら通常コンパイラの警告で除去できるミスなので)、まぁいるんでしょう。
おお!これは私に対する挑戦ですね。いいでしょう。
警告を出させずにローカル変数の参照を運び出してみせましょう。
◎ パターン1
int& foo(int& i) {
return i;
}
int& goo() {
int i;
return foo(i);
}
まぁ、このぐらいならコンパイラの警告なしでも気づきますよね。
◎ パターン2
パターン1 の応用です。
class Sample1 {
int& index;
public:
Sample1(int& i) : index(i) {}
};
Sample1* moo() {
int i=0;
return new Sample1(i);
}
◎ パターン 3
暗黙の型変換を使います。
class Sample2 {
int& index;
public:
Sample2(int& i) : index(i) {}
};
Sample2 boo(int i) {
return i;
}
どうでしょう?
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:1)
そんな「自分を騙し」てしまいそうなコードは、書かん(書かずに済むように可能な限り持ち込む)です(^^;
というか、大きいの(という判断基準ではないだろうけど)は大抵Heapに取るし。
「使いまわす」ならばスタック上に取る「必要」がむしろ無いわけですから。
たぶん、関数の呼び出しの上下関係が、錯綜というか錯誤しちまったときに、
ポインタや参照のほうについても、そういう類の間違いが発生するですよね。
もともと呼び出しが上下関係で説明できないようなプログラミング
(Object指向的に濃ゆいプログラミングになればなるほど、その傾向は増しますね。
「たがいに」参照しあうという状況が多くなるので。)では、
そんなところで"不用意に"スタック上に領域を取った時点で、敗北の予感っす…
結局、呼び出しの上下関係ってものがきちんとしてることを前提(笑)として無矛盾性を提供する
C++流の参照の考え方って、(よりによってC++がサポートしてることになっている)OOPには
似合わないなあ、と。
ええと。綺麗なコードを(可能な限り)書きましょう、という意味でもあります(^^;
少なくとも、自分が今どういう(上下関係で説明できるのか否かとか)プログラムを
書いているのかを理解して、それに基づいて記述手段を選ぶようにしよう、と。
Re:「ポインタ」は「参照」に非ず (スコア:1)
>> C 言語が "call by reference" と称しているのは、アドレスを渡す "call by value"。
>
> C言語の文法と挙動を正確に理解できるようになるために学ぶ段階において、
> C言語がcall by referenceだなんて考えたらドツボにはまっちゃうわけで。
>
> あくまでcall by valueと考えるべき。
>
> そう考えることで、初学者にもベテランにも言語屋さんにも、誰にとっても
> 矛盾なく使える(かつ、なにか重要なものを欠落させるわけでもない)モデルが
> 頭ん中に構築できるわけで。
>
> 言語屋さんには見える、という狭い話じゃなくて、誰にでもそれは事実であるのでは?
無論 そうですが、C はポインタを引数にした関数呼び出しを "call by reference" で
あると言っていますよね。実際、そう習う人も多いはずです。
しかし、通常の C の実装系の挙動を考えるなら、究極的には "call by value" と考えざるえない。それは「ポインタ」と「参照」の間にある違いに起因している、と言いたいわけです。
実際、ポインタの引数渡しを "call by value" として、捕らえなければプログラムというのは、他のポインタのない言語ではコーディングできない場合が多いでしょう。
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:1)
>参照」の間にある違いに起因している、と言いたいわけです。
それはまた微妙に違う問題であるような…
JavaやRubyの参照だって、参照という名の値(Value)をコピってるわけでして。
「変数」を(代入を直接記述する以外の手段で)書き換え可能か、それとも書き換え可能なのは参照先の「データ」だけなのか、
という差があるのでわないかと。
ちなみにPascalのVar渡しは変数を書き換え可能っすね。Fortranはどうだっけ?
Re:「ポインタ」は「参照」に非ず (スコア:1)
> reference の項目は唯一1ページだけ、27ページに言及があります。その内容は
> In C, all function arguments are passed "by value." ... This leads to some different
> properties than are seen with "call by reference" languages like Fortran or with var
> parameters in Pascal, ...
> と、参照渡しが C にはないことを明確に示しています。
プログラミング言語C では ちゃんと触れていましたか。
K&R は引越しの荷物の底に眠ったままだたので、チェックなしで
書いてしまいました。すいません。
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:1)
フレームと膏薬はどこにでもつきます。ナイーブな人には何でもそう見えます。
それに、いちいち気にするの面倒じゃん。
「自由」にするのを俺はお勧めしてます。つまりナイーブでなくなるのを。
言われて「悔しければ」、それについて鍛えればいいんです。たとえば参照について自分の誤解を直すとか。
>GC(メモリー管理)に触れてなければ言われても仕方ないかと。
そういう問題じゃないでしょう、もし本当にフレームのもとというものを心配するならば。
Re:いい方に考えると (スコア:0)
>それってハードウェアサイエンティストのことですか?ソフト(上層)ではなくて?
ソフトもです。
自分の書いたコードがどのように解釈されて実行されてるの
Re:いい方に考えると (スコア:1, 興味深い)
必要な場所で必要な知識があればよし。それ以上を求めてもきりがない。
理想と現実は違うものだよ。技術者としての理想と、仕事としてのスキルは全くの別問題。
Re:いい方に考えると (スコア:1)
もっとも現役のコンピュータサイエンティストと言われている人達は,いざとなればアセンブラでプログラムを書くことくらいはできると思いますがね.
案外そうでも無いのかな? :-P
Re:いい方に考えると (スコア:1)
>した時対処出来ない。
>こういうのはVB屋さんとかにダメな人が多いね。
こういう文脈でVB屋を引き合いに出すなんて、コンパイラ屋さん(なのかな?)のやることじゃないですね。
優劣はさておくとしても「畑」が違いすぎるんだから、その畑での勝負は、結果が目に見えている出来レースです。
逆に、下層に強い人だからといって、上層もすらすらこなせるとは限らないと思うんですが、どうなんでしょう?
下層でやってることを再帰的に積み重ねる「だけ」で上層も説明できるわけでは、無い筈ですから。
よだん:
パフォチューといえばDBマガジンなんかでも(もちろんDB畑について)よく記事が乗るようです。
まぁ色々あるということで。
よだん2:
ここでは関係ない話であることを期待します(笑)が、
「自分のバグを見逃して、コンパイラのバグだと騒ぐ」人も結構いるらしいですね。
上層やってる側から言えば、いかにコンパイラのバグみたいなものを「踏まず」にいられるような
素朴(笑)なコードで実装を済ますか?を気にしたりは、します。
Re:いい方に考えると (スコア:0)
下層がわからなくて上層はできないと思うのですが。
それってたまたまうまくいってるだけとかできてるつもりなだけなのでは?
Re:いい方に考えると (スコア:1)
資源的にそれが許されない場合が多いし、下層がわからないとメモリリークとか違法参 照とかしまくる状況なのが今のコンピュータソフトですからね…
はっきり言えるのは、難問に直面した場合、アセンブラやハードウェアをやった人の方 が的確な答えに到達するのが速いと言う事ですね。
その状況が5年先、10年先も続くかどうかは明言できないですが…
Re:いい方に考えると (スコア:1)
#まぁ滅多にそんなことにはならないですけど
Re:いい方に考えると (スコア:1)
Javaとかみたいな仮想マシンの「上」の世界の話も指してますね。
>アセンブラやハードウェアを知っていれば解決しやすいメモリリークとか違法参照なんてありますか?
ぎゃはは。鋭い。スラド風にいえば「すばらしい洞察」!
モデレは(もし権利あっても)しない主義なので、ここでこうして生の言葉で賞賛させて頂きますm(__)m。
>直下層の知識は役に立つことが多いと思います。
>が、なんでもかんでも最下層の知識を求めようとすると
(以下略)
御意。なるほど、直下かあ。
Javaとかで、参照の意味的なバグを出しちゃった状態で、アセンブラレベルの知識が直接役立つのか?と聞かれると、
たしかに首をかしげます。
それぞれのバグには、そのバグに見合った抽象度(^^;の対策が、有るってことでしょうね。
それより上すぎても下すぎても、対策として的外れなものになっちゃう、と。
そしてCのポインタにしても、抽象的な理解によって退治できる(すべき)バグと、
ハード寄りの理解によって退治できる(すべき)バグと、が有るんでしょうね。
ただし、高級言語たるものの役目はもともと抽象化であるわけですから、
その役割を全うしてる限り頻繁に必要とはならないはずの具体的な理解
(さもないと「シゴトで使える言語」という前提が崩れます。ポインタみたいな重要なところでバグにぶちあたるコンパイラなんて、
速攻で直す(そして明日からはバグに悩まされない)か、速攻で捨てるか、回避コードで逃げるか、どれかしないと仕事にならんでしょ)
を常時持っていることよりも、
むしろ抽象的な理解を常時持っていることのほうが、数段重要であるのではないかな。
余談:
「理解の深さ」と、ハードからアプリにいたるまでの「階層の深さ」とを、混同してる人が居るような気がする…
Re:いい方に考えると (スコア:0)
> それってたまたまうまくいってるだけとかできてるつもりなだけなのでは?
全てのJavaプログラマはJava仮想マシンを勉強する必要がありますか?
Re:いい方に考えると (スコア:0)
# 何の為の抽象化?
Re:いい方に考えると (スコア:1)
他のかたの言葉を借りるならば「直下層」、つまり、
自分がちょうど居る場およびそのすぐ隣だけを
理解していればいい、という反論になると思います。
必要なのは、一方という大きなくくりではなく、その一方の中でも特に自分に近い薄皮の部分、なのではないでしょうか?
底板一枚めくったらいきなりハードが見えるような世界(全部合計して2層しかない?)での仕事なら別ですが、
それ以外では、めくっても少々具体度が高い"ソフト"が見えるだけでしょうね。
余談:
俺が個人的に「MSX症候群」と呼んでいる(精神)状態があります。
「全体が見渡せたから」「MSX全体を支配できたから」良かったよなぁ、という文脈でMSXを回顧する人を指します。
ま、Cすらろくに無い(認知されてない)時代/環境でしたから…
??? (スコア:0)
ンなことはないって.
ポインタ(Cみたいな素のポインタ)が問題なのは
Re:??? (スコア:1)
とゆーても、リファレンスカウント自体が色々と痛い代物なんで、間違えなくても不安が心配っす。
Re:??? (スコア:1)
> とゆーても、リファレンスカウント自体が色々と痛い代物なんで、間違えなくても不安が心配っす。
#148022 [srad.jp]の人の言う COM のリファレントカウントというのはIUnknown の AddRef、Release のことかしら?
リファレンスカウントの痛さというのが、ナイーブなアルゴリズムでは巡回参照オブジェクトが除去できないとか、MT-safe にやるのはオーバーヘッドが大きいとかいう問題だとすると、
最近では以下のようなものが出ています。
ACM SIGPLAN PLDI'01
"Java without the coffee breaks: a nonintrusive multiprocessor garbage collector"。
リファレントカウント方式は再び脚光を浴びつつあるのです。
コンタミは発見の母
Re:??? (スコア:1)
でGoogleしてみたら3件ひっかかりました。PDFの奴が本文なのかな。
後で読んでみます。ほんと(^^;にRefCountのみで全部やれるというなら、たしかにそれに越したことは無いので。
循環参照問題とかは、どう解決したのかな?わくわく。
あ。そういやケロっと忘れてました(^^;が、
http://todo.org/cgi-bin/jp/tiki.cgi?c=v&p=Python%A4%CEGC
こんなのも有ったみたいです。
Bacon の論文 (スコア:1)
> 越したことは無いので。
> 循環参照問題とかは、どう解決したのかな?わくわく。
Bacon 先生の論文は循環参照の解決というよりは、SMP 上でのスループットが mark & sweep(or compact?) とタメを張るのが主眼(だったはず)です。
ここで使われている巡回参照の解決方法は Rafael D. Linus の "A multi-processor shared memory architecuture for parallel cyclic reference counting", Microprocessing and Microprogramming, 1992 に基づくものです。
巡回参照の部分だけに興味があるなら、こっちを読んだ方が早いと思います。
ご存じかもしれませんが、巡回参照問題は高度な解決方法が色々提案されています。個人的には、部分的に marking をやるやり方が一番将来性がると信じています。
ついでに言うと、前世紀に行われた GC の研究はJones本 [amazon.co.jp]にほんとんどまとめられているといって過言ではないでしょう。
コンタミは発見の母
( ゜д゜)ポカーン (スコア:1, 興味深い)
あるいはコンパイラ/VMが管理するかで全然ちがってくるよ
「プログラマが管理しきれなければプログラムは動かない」ってこっとが
「管理が楽になればプログラマが動かせるプログラムの『規模』が変わる」
っつーことだという事に気付かなかった?
野分
Re: ( ゜д゜)ポカーン (スコア:1)
たしかにそういう面はありますが、ポインタについては、そういう問題とは別の問題だと思うんですけど。
てゆーか、1つ前の文の論点は、そこではなかったように思います。
少なくとも、
>ポインタ(Cみたいな素のポインタ)が問題なのは,プログラムの挙動を
>管理しきれないっつうことだよ.
ってのは、なんか違うと思いますんで。
素であってもCのポインタ程度(^^;なら、管理し切れるのでは?
なにせ、単純(ひねくれてはいますが、ひねくれの少ない部分を切り出してそれしか使わないのは十分可能だし)
な文法に馬鹿正直に則ってコーディングしてれば、動くんですから、
あるいは、プログラマが弱くて管理しきれない(そういうプログラマが居る)、
という面に目を向けているならば、
それはプログラムの「どんな」部分においても生じえる問題なので、
ことさらにポインタについてだけ言う意味が無い、ということになります。
ポインタの判らないプログラマもいれば、Classの判らないプログラマ、
Javaとかの参照が判らないプログラマ、継続の判らない(以下略…)と、色々居るわけですから。
Cのポインタは、素といっても、あれはあれなりに色々な事柄が抽象化済みです。
結局程度問題でしかないというならば、いろいろな「程度」が存在する中で、なぜ
そこ(ポインタ)だけを殊更に考える「必要」があるのかを
いったん考えたほうが良いような気がしています。
余談:
ポインタの管理は難しくない。ただし、それと常に対になるfree責任の問題が難しいですね。
なので、Cでガベコレを使えるようになるBoehmGCとかに逃避(笑)すると、がぜん楽勝鼻歌の世界になります。
>気付かなかった?
そういう主張には、なってなかったように思えましたので。
その事柄自体は俺も知っています。
Re: ( ゜д゜)ポカーン (スコア:1, すばらしい洞察)
(素の)ポインタの原理を理解したとしても,100とか200のポインタの
挙動を管理しきれるもんじゃないよ.
#私はこの辺が限界……
そして,(素の)ポインタだと,コンパイラ/VMの助けは期待できないし,
現象の再現しづらい地獄のデバッグが待っている……
完璧なプログラム/ライブラリだったら負担も少ないけど,
そんなことないからね.
#デバッグ半分,設計半分
『ポインタバリバリ』使った時点で『バギーなコード』になる
リスクはたかいよ.
野分
Re: たくさんのポインタ (スコア:1)
どこからでも参照/変更出来る変数が、そんなにあるんだったら、ポインタ変数だろうが非ポインタ変数だろうが、10万行程度のプロジェクトでも収拾がつかなくなりませんか?だとしたら、単に設計が悪いってことなんじゃないかな。それはポインタ変数の善し悪しとは別の問題だと思います。いや、超大規模プロジェクトではそういうことがあるかも知れませんが、そこまで大規模なのは経験がないので分かりません。
構造体の中にポインタ変数があって、その構造体がいくつもある…という状況のことを指しているとしたら、英文の単語数えなんていうのが、それでできそうですね。うちの会社では新人研修生に演習課題としてよくやらせています。単語種別が数1000語ほどあるテキストファイルを入力として与えると、1つの構造体に文字列へのポインタと次の単語構造体へのポインタを持っているのが典型的パターンなんですが、その2000個ほどのポインタをちゃんと処理してくれるプログラムを書いてくれますよ。(笑)
vyama 「バグ取れワンワン」
Re:例外とコアダンプ (スコア:1)
1:エンドユーザーが当てる「パッチ」って、自分がコードを(意味を理解して)修正するわけではないですよね?
じゃあ言語がどういう代物かは関係ないと思います。
2:そもそも、セキュアにしにくい言語とライブラリで、セキュアでないと駄目なソフトを書く、というのが
間違ってるんだろうなあ>世の上級者諸兄
ポインタ「は」難しくないですが、ポインタ(だけ)じゃ力不足(!)だから、オーバーランとかのバグは頻出するっぽい。
Javaとかで大丈夫なのは、ポインタがどうこういう問題じゃなくて、readとmalloc(ってのか?)を適切に行うライブラリが
用意されてるかどうかの差っすね。
Cでおなじみのread系(?)関数って、「アプリ(?)プログラマが」決めた領域にデータを書くよね。
ということは、決め損ねればオーバーランの憂き目に遭うということであり、
それを決める部分もライブラリにやらせりゃ極楽ってことになる。
まあ若干効率は落ちるし、開放責任問題の面倒さもあるんで、C文化(ぉ)ではそれを嫌った、と。