この質問で言う C とは C99 のこと、 C++ は C++11 のことという前提を置きます。
以下のような配列をポインタで辿るプログラムがあったとして、 &foo[3] の部分は C++ として未規定ではないと考えてよいですか?
cpp
1#include <stdio.h> 2 3int main(void) { 4 int foo[3] = {1, 2, 3}; 5 for(int* p=foo; p<&foo[3]; ++p) { 6 printf("%d", *p); 7 } 8}
私の思考過程をたどる形で質問をもう少し詳細にします。
C では
E1[E2]は(*((E1)+(E2)))と等価である- ポインタと整数の加算の結果は配列の要素か配列の最後の要素をひとつ超えたところを指さなければならない (そうでなければ未定義)
- ポインタと整数の加算の結果が配列の最後の要素をひとつ超えたところを指す場合、評価される単項
*演算子のオペランドとしてはならない - 単項
&のオペランドが単項*の結果である場合は&も*も評価しない (両者を取り除いた場合と同じ結果)
という規則によって &foo[3] は foo+3 と同じ結果となり、配列の最後の要素をひとつ越えた場所を指すポインタが生成されるけれどもそこにアクセスはしないので未定義を踏んでいないと考えられます。
しかし C++ では & と * を相殺する規則が存在しません。 そのかわりに
という規則があり、単項 & のオペランドが単項 * の結果であった場合には結果的にこの変換が起こらないのでオブジェクトが初期化されているかどうかが問題にされるパスを通らず未定義にはあたらないと思います。
この理解で正しいでしょうか? というのが質問の意図です。
C++11 より後の規格でも事情が大きく変わっているところはないと思いますが、もしも特に知る必要がある改定があるならば補足いただけるとありがたいです。
C++の規格書は見てませんが、
> glvalue から prvalue への変換時にオブジェクトが初期化されていない場合の動作は未定義
の記述だけを見ると、これは、X を変数とすると、右辺値を書くところに X と書くと変数から値が取り出される場合のことについて書いてあるように見えます。
(配列でない)変数の変数名は、元々左辺値で、右辺値が要求されるところに書くと自動的にデリファレンスされると理解しています。C/C++の用語として正しいかどうかは分かりませんが。一般論として。
1の部分は、Xの型と関係ない型にキャストしたり、Xに生成したオブジェクトを代入していない状態で値を取り出すと未定義という事など。
2の部分は、&& || ? : などの演算子のオペランドだと評価されないこともあり得て、その場合は値が取り出されないなど。
(左辺値になり得る)変数相当に演算子&を付けることで、右辺値であるアドレスに変わるということとは関係ないのではないでしょうか?
全体の中でのこの節の位置づけが把握できてないので、的外れかも知れませんが。
関係ないです。 この場合は左辺値のままのはずなので未定義を引き起こしうる操作 (右辺値への変換) と関係しませんから大丈夫ですよね? という意味です。
> この場合は左辺値のままのはずなので
C++11では &foo[3] は左辺値と言うことしたか。Cだと右辺値なので、同じかと思っていました。
私のコメントは、&foo[3] が右辺値であるのが前提です。失礼しました。
そうすると、「左辺値から左辺値への変換だけど、途中で一旦右辺値を経由することは無いですよね?」という質問になりますか?そうだとすると、「実装としては無いだろう」とは言えると思いますが、規格でその旨が保証されてるか知りたいというのが質問ポイントですかね。
いえ、 &foo[3] は右辺値です。
& は「左辺値を (左辺値のまま) オペランドとして受け取ってそのアドレス (prvalue) を返す」であって「左辺値を右辺値に暗黙に変換する (prvalue を返す)」という工程を経ませんということを述べてます。
E1[E2] は (*((E1)+(E2))) と等価であるというのがC++でも同じであるなら、
(&foo[2]+1)と&foo[3]は等価なはずなので、
foo[3]が評価されるというのはおかしいでしょうから、そちらの方面で調べてみてはいかがでしょうか。
この定義は再帰的に表現出来ますので、&foo[3]が未定義動作ならここを参照する術が物理的に無くなる気もします。
質問の趣旨としてはfoo[3]が*(foo+3)と同等なので、&*(foo+3)がいいのか?という話ですよね?
このコメントがただの質問の繰り返しであればすみませんm(_ _)m
なるほど。
「デリファレンスをしない」を「左辺値のまま」と表現したのですね。結果は右辺値ですが。
それはちょっと予想外でした。てっきり前提間違いの指摘かと思いました。
そうすると、私がコメントで書いた、
「glvalue から prvalue への変換時にオブジェクトが初期化されていない場合の動作は未定義」の英文は、左辺値を右辺値の場所に書くと自動的にデリファレンスされるという場合だけのことを言っているのであって、演算子による変換は関係ないのでは?
と言うことじゃないでしょうか?この文の範囲内ではそうとは明記されていませんが、デリファレンスについての記述だと思って読むと納得性のある説明文です。
「オペランドが左辺値限定で、デリファレンスを伴わないで、結果が右辺値」という単項演算子は他には無いか。。。
回答2件
あなたの回答
tips
プレビュー