K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[796] 左辺値としての配列名
投稿者:yuya
2007/02/20 02:13:25

こんにちは。Cの「配列名」について、質問があります。 「ポインタ完全制覇」には、 char hoge[5]; hoge = "abcd"; がダメな理由は、hogeが右辺値だから、とあります。 配列名は式の中では先頭要素へのポインタ(右辺値)に読み替えられる、 という規則は、代入演算子の左側のような、 「左辺値が要求される場所」においてもそうなのでしょうか? 配列名は、右辺値(先頭要素へのポインタ)に読み替えられる前は、 配列オブジェクトそのものを表す変更不可能な左辺値なので、 「hoge = "abcd";がダメなのは、hogeは左辺値だけど変更不可能だから」 という説明であれば納得できるのですが、 どちらの説明が正しいのでしょうか?
[この投稿を含むスレッドを表示] [この投稿を削除]
[797] Re:左辺値としての配列名
投稿者:(ぱ)
2007/02/20 02:13:25

>配列名は式の中では先頭要素へのポインタ(右辺値)に読み替えられる、 >という規則は、代入演算子の左側のような、 >「左辺値が要求される場所」においてもそうなのでしょうか? JIS X3010の6.2.2.1によれば、 | 左辺値がsizeof演算子のオペランド、単項&演算子のオペランド、文字配列を初期化 | するのに使われる単純文字列リテラルまたはwchar_tに適合する要素型をもつ配列を | 初期化するのに使われる文字列リテラルである場合を除いて、型"~型の配列"をもつ | 左辺値は、型"~型へのポインタ"を持つ式に型変換する。 とあります。 もし、代入演算子の左側のような「左辺値が要求される場所」においては配列から ポインタへの読み替えが行われないのだ、とするのなら、上記の3つの条件の中に 単項&演算子のオペランドを含める必要はないはずです。&演算子のオペランドは もともと左辺値が要求される場所だからです。 ということで、上記3つの箇所を除いて、(左辺値が要求されようがされまいが) 式の中では配列はポインタに読みかえられる、と解釈するのが自然ではないでしょうか。 # とはいうものの、「変更可能な左辺値」の定義からわざわざ配列が除かれている # ところからすると、配列に代入できないのは変更可能な左辺値ではないからだ、 # という解釈もできるような気が…
[この投稿を含むスレッドを表示] [この投稿を削除]
[798] Re:左辺値としての配列名
投稿者:774RR
2007/02/20 02:13:25

char a[10]; char (*p)[10]=&a; // である場合に *p=0; あるいは *p={0}; であれなんであれ *p は代入式の左辺に置けません。 JIS X 3010:2003 6.5.16 代入演算子 [制約] 変更可能な左辺値でなければならない。 JIS X 3010:2003 6.3.2.1 変更可能な左辺値とは...[配列を含まない] なので >配列に代入できないのは変更可能な左辺値ではないから であり、それ以上でも以下でもないです。 ただしこの話はスレ題の「左辺値が要求される場所においても読み替えが起こるか」とは無関係。 無関係ではありますが、やはり同じく JIS X 3010:2003 6.3.2.1 型"~の配列"を持つ式は、型"~へのポインタ"の式に型変換する。 それは配列オブジェクトの先頭の要素を指し、左辺値ではない。 とありますから、スレ題に関して言えば「どちらでもいい」となります。 起こると解釈しても起こらないと解釈しても、いずれにせよ代入式の左辺に置けないことに違いは無い。 ところで JIS X 3010 は 2003 版が発行済で 2003 版には 6.2.2.1 が無かったです。 引用元は 1990 版でしょうか?
[この投稿を含むスレッドを表示] [この投稿を削除]
[799] Re:左辺値としての配列名
投稿者:yuya
2007/02/20 02:13:25

前橋さん、774RRさん、ありがとうございます。 > 配列に代入できないのは変更可能な左辺値ではないから なるほど、 「配列に代入できないのは、配列は左辺値だが変更不可能だから」 と考える必要はないんですね。 「『変更可能な左辺値』ではないもの」には、 「変更不可能な左辺値」だけでなく「右辺値」も含まれますから。 (a)変更可能な左辺値 (b)変更不可能な左辺値 (c)右辺値 (d)その他 に分けたとして、 ・「ポインタ完全制覇」での説明:    「配列に代入できないのは、(c)だから」 ・私が[796]で書いた説明:    「配列に代入できないのは、(b)だから」 ・おそらく最も正確な説明:    「配列に代入できないのは、(a)でないから」 「(a)でない」を満たしさえすれば 配列に代入ができないことには変わりなく、 その実態が(b)なのか(c)なのか、あるいは(d)なのかは 別問題である、ということですね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[800] Re:左辺値としての配列名
投稿者:774RR
2007/02/20 02:13:25

規格書の一部はコンパイラ作者向けに、一部はユーザ向けに書かれている。 で、当該項はユーザ向けだと、俺は思う。 俺がコンパイラ作るなら、左辺値が必要とされる文脈で右辺値変換するなんて無駄なことはしない。 単項 & や sizeof はまさに左辺値が必要とされるところであるから、そこで 無駄な右辺値変換を行わなければ規格書どおりの動作となるわけだ。 同じことが代入式の左辺でもいえる。 だから、俺的には配列名が代入式の左辺に現れたときに右辺値変換されるとは思わない。 「変更不可能」だから左辺におくとエラーと考えるほうが自然。 まあどう解釈しても結果は同じだからどうでもいいけど。
[この投稿を含むスレッドを表示] [この投稿を削除]
[801] Re:左辺値としての配列名
投稿者:774RR
2007/02/20 02:13:25

あう sizeof は右辺値でもいいのか。 だったら「sizeof の係る式が左辺値の場合には右辺値変換するな」って旨を コンパイラ作者に通知する条文でもあるわけだな。 結論は変わらん。
[この投稿を含むスレッドを表示] [この投稿を削除]
[805] Re:左辺値としての配列名
投稿者:NykR
2007/02/20 02:13:25

私自身は「変換する」と書いてあるんだから変換するんだろうと思ってますが、例えばこんなコードをgccでコンパイルすると int main(void) { int a[1]; a++; return 0; } array_inc_test.c: In function `main': array_inc_test.c:4: wrong type argument to increment と、型に問題がある、というメッセージが表示されます。 a++を(&a)++と、ポインタ値のインクリメントに置き換えると array_inc_test.c:4: invalid lvalue in increment と表示されますからgccではaはポインタではないみたいです。 ついでに、K&Rの附録Aでは制約が書き加えられて変換しないことになっています。
[この投稿を含むスレッドを表示] [この投稿を削除]
[808] Re:左辺値としての配列名
投稿者:(ぱ)
2007/02/20 02:13:25

774RRさん: >ところで JIS X 3010 は 2003 版が発行済で 2003 版には 6.2.2.1 が無かったです。 >引用元は 1990 版でしょうか? そうです。 NykRさん: >私自身は「変換する」と書いてあるんだから変換するんだろうと思ってますが、 >例えばこんなコードをgccでコンパイルすると コンパイラがそう実装するのは当然なので、特定の3つのケースを除き「変換する」と 書いてありそれに含まれないのだから(文法上は)変換すると解釈するのが自然だろうと 私は思いますが、変換するかどうかはさておき、代入できない理由としては、 「変更可能な左辺値ではないから」という理由も挙げておくべきであるように 思います。今は無理ですが、近々Web上で対応します。 >ついでに、K&Rの附録Aでは制約が書き加えられて変換しないことになっています。 日本語版p.245ですね。規格と違う説明をするのはいかがなものかと思いますが… ポインタ完全制覇を書いているとき、これに気付いてなかったのかなあ… 今となってはすっかり忘れています。
[この投稿を含むスレッドを表示] [この投稿を削除]
[810] Re:左辺値としての配列名
投稿者:774RR
2007/02/20 02:13:25

なんとなくネタ的に面白いので追加検証。gcc ならソースコードがあるので読んでみた。 gcc-3.4.5 を追っかけ。 gcc-4 では大幅な変更が入っているので変わっているかもしれない。 int array[1]; array=0; なるソースコードに対する挙動 C の場合 gcc/c-typeck.c Line 3677 でエラーメッセージを出力 これは convert_for_assignment() 関数の末尾である。この関数の先頭部分で if (TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE || TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE) rhs = default_conversion (rhs); なるコードが実行されている(すなわち右辺はデフォルト変換が実施されている) のに対して左辺のデフォルト変換を行うコードは見当たらない。 =代入式の左辺においては「配列名を先頭へのポインタに読み替える」変換は実施していない C++ の場合 gcc/cp/typeck.c Line 5295 でエラーメッセージを出力 これは build_modify_expr() 関数の一部であり if (TREE_CODE (lhstype) == ARRAY_TYPE) の内側にある。この例では右辺左辺の型が異なるのでこのエラー。 もし array=array; であるなら、型一致なのでこの行ではなく先に進んで ISO C++ forbids assignment of arrays エラーが発生。 いずれにせよ左辺のデフォルト変換を行う関数は呼ばれていない。
[この投稿を含むスレッドを表示] [この投稿を削除]