アカウント名:
パスワード:
C++ の「参照」が、「ポインタ'」にすぎないという点には同意。
誰でもいいけど、C++の参照がJava等の参照と異なると言うなら異なる点を列挙せよ。
一旦参照型変数を初期化したオブジェクトを、他のオブジェクトに置き換えれるようなら、(参照というよりは)ポインタ的と言えるだろうけどそれはできないでしょ?
Foo foo1, foo2; Foo *p = &foo1; // ポインタなら
現実にこのような過ちを繰り返している人がいるとすればそのことに驚きですが(何故なら通常コンパイラの警告で除去できるミスなので)、まぁいるんでしょう。
でもこれ本気で書いてる人は(前回の例と同様)スタックフレームをまったく理解してないのでしょうね。
より多くのコメントがこの議論にあるかもしれませんが、JavaScriptが有効ではない環境を使用している場合、クラシックなコメントシステム(D1)に設定を変更する必要があります。
弘法筆を選ばず、アレゲはキーボードを選ぶ -- アレゲ研究家
いい方に考えると (スコア:0)
ポインタバリバリ使ってバギーなコード書かれるよりはましかも。
卒業したらJavaに移行して貰えばいいし。
でもコンピュータサイエンティストなら、アセンブラはやって欲しいけどね。
Re:いい方に考えると (スコア:1, すばらしい洞察)
でも、同じ問題は結局JavaだのRubyだのでも生じますよね。ポインタつーか参照については。
単に症状の出方が違う(いきなりコアダンプするか、NullPointerなんたらExceptionが出るか、の違いというか)だけで。
ポインタって、その概念的原理(つまり、なにかがなにかを「参照」する)と、 C独特の構文と、
の2者さえ理解すれば、逆にいえばたいして難関でもないと思います。
あの程度のことを理解*できない*ような人ならば、情報系の学生だろうが仕事だろうが趣味(フリーソフトとか)だろうが
こっちとしてはちょっと
「ポインタ」は「参照」に非ず (スコア:1)
「ポインタ」は「参照」としても利用できるように設計されていますが、「参照」よりも遥かに広いポテンシャルを持った機構です。ゆえにバグの温床となる。
「ポインタ」はメモリアドレスを示す「値」(+型情報)、「参照」はもっと抽象的なオブジェクトやクラスなどを参照することができるデータ。
例えば、言語屋さんから見ると C 言語には "call by reference" がないように見えるそうです。C 言語が "call by reference" と称しているのは、アドレスを渡す "call by value"。
> でも、同じ問題は結局JavaだのRubyだのでも生じますよね。
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:2, 参考になる)
>と称しているのは、アドレスを渡す "call by value"。
C言語の文法と挙動を正確に理解できるようになるために学ぶ段階において、
C言語がcall by referenceだなんて考えたらドツボにはまっちゃうわけで。
あくまでcall by valueと考えるべき。
そう考えることで、初学者にもベテランにも言語屋さんにも、誰にとっても
矛盾なく使える(かつ、なにか重要なものを欠落させるわけでもない)モデルが
頭ん中に構築できるわけで。
言語屋さんには見える、
Re:「ポインタ」は「参照」に非ず (スコア:1)
> が可能か不可能か、という差がありますね。 他の変数に代入されてる値にアクセスする、ということ
> が、この機能の有無によって、可能か不可能か変わってくる。
>
> でも逆にいえば、それしか差は無いわけで。
それしか、、、ですか。
私はコンパイラ屋で GC 屋ですから、この 2 つの間に 言語のポテンシャルを完全に
左右する違いを見ているのですが、、、
> あ。ごめん。もうひとつだけ差があった。Cのポインタには「演算」が定義されてる。
> ++とかが出来てしまう
コンタミは発見の母
Re:「ポインタ」は「参照」に非ず (スコア:0)
誰でもいいけど、C++の参照がJava等の参照と異なると言うなら異なる点を列挙せよ。
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:「ポインタ」は「参照」に非ず (スコア:0)
Re:「ポインタ」は「参照」に非ず (スコア: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:「ポインタ」は「参照」に非ず (スコア:0)
現実にこのような過ちを繰り返している人がいるとすればそのことに驚きですが(何故なら通常コンパイラの警告で除去できるミスなので)、まぁいるんでしょう。
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:「ポインタ」は「参照」に非ず (スコア:0)
でもこれ本気で書いてる人は(前回の例と同様)スタックフレームをまったく理解してないのでしょうね。
Re:「ポインタ」は「参照」に非ず (スコア:1)
そんな「自分を騙し」てしまいそうなコードは、書かん(書かずに済むように可能な限り持ち込む)です(^^;
というか、大きいの(という判断基準ではないだろうけど)は大抵Heapに取るし。
「使いまわす」ならばスタック上に取る「必要」がむしろ無いわけですから。
たぶん、関数の呼び出しの上下関係が、錯綜というか錯誤しちまったときに、
ポインタや参照のほうについても、そういう類の間違いが発生するですよね。
もともと呼び出しが上下関係で説明できないようなプログラミング
(Object指向的に濃ゆいプログラミングになればなるほど、その傾向は増しますね。
「たがいに」参照しあうという状況が多くなるので。)では、
そんなところで"不用意に"スタック上に領域を取った時点で、敗北の予感っす…
結局、呼び出しの上下関係ってものがきちんとしてることを前提(笑)として無矛盾性を提供する
C++流の参照の考え方って、(よりによってC++がサポートしてることになっている)OOPには
似合わないなあ、と。
ええと。綺麗なコードを(可能な限り)書きましょう、という意味でもあります(^^;
少なくとも、自分が今どういう(上下関係で説明できるのか否かとか)プログラムを
書いているのかを理解して、それに基づいて記述手段を選ぶようにしよう、と。