配列型に対する&演算子について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
sys
記事: 27
登録日時: 14年前

配列型に対する&演算子について

#1

投稿記事 by sys » 14年前

char array[2][3] = { "ab", "cd" }; と宣言したとき、
「array」 の返す値は、配列の先頭要素のアドレスを返している。型は、char[2][3]
「&*array」 の返す値は、配列の先頭要素のアドレスを返している。型は、char(*)[3]
※型は typeid(array).name()、 typeid(&*array).name で確認。

JIS X3010 6.5.3.2 原文
  • [制約]
    単項&演算子のオペランドは、関数指示子、[]演算子若しくは単項*演算子の結果、又は左辺値でなければならない。
    左辺値の場合、ビットフィールドでもなく、register記憶域クラス指定子付きで宣言されてもいないオブジェクトを指し示さなければならない。
    [意味規則]
    (1)単項&演算子は、そのオペランドのアドレスを返す。
    (2)オペランドが型"〜型"をもっている場合、結果は、型"〜型へのポインタ"をもつ。
    (3)オペランドが、単項*演算子の結果の場合、*演算子も&演算子も評価せず、両演算子とも取り除いた場合と同じ結果になる。ただし、その場合でも演算子に対する制約を適用し、結果は左辺値とならない。
    同様に、オペランドが[]演算子の結果の場合、単項&演算子と、[]演算子が暗黙に意味する単項*演算子は評価されず、&演算子を削除し[]演算子を+演算子に変更した場合と同じ結果になる。
    (5)これら以外の場合、結果はそのオペランドが指し示すオブジェクト又は関数へのポインタとなる。
定義(3)の「単項*演算子の結果の場合、*演算子も&演算子も評価せず、両演算子とも取り除いた場合と同じ結果になる。」から
「え?型変わってるから同じ結果じゃないじゃん。」と思いました。

なぜ型が変わるのでしょうか?

私は、
  • (1)「&*array」は「*array」を評価し次に「array of char(要素型はchar)」に対して&演算子を評価しているから「配列(char[3])へのポインタ」を返し、そして型はchar(*)[3]となる。と一見読めるが、これでは両演算子を評価をしてしまっているので定義(3)に反している。
    (2)では、最初から考えて、まず演算子の優先順位から「*array」は評価されていると思う。
    (3)次に&演算子を評価するときに対象は何かなぁ〜?あっ「*array」は*演算子の結果じゃん。
    じゃぁ(返す値の)結果は両演算子とも取り除いた場合と同じ(返す値の)結果になるよ。
    (4)けど定義(2)「オペランドが型"〜型"をもっている場合、結果は、型"〜型へのポインタ"をもつ。」だから
    「*array」を評価した型は、配列型(array of char(要素型はchar))だから、(型の)結果は「配列型へのポイント」になるよ。
    (5)結論
    返す値は、両演算子を取り除いた「array」と同じ「配列の先頭要素のアドレスを返している。」
    型は、char(*)[3]
と考えましたが、合っているかがわかりません。
ご教示お願い致します。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: 配列型に対する&演算子について

#2

投稿記事 by ISLe » 14年前

typeidのオペランドは、sizeof演算子と同じく、評価対象式ではないのでポインタに型変換されません。
→JIS X3014:2003(ISO/IEC 14882:2003) 3.2 単一定義規則 2

sys
記事: 27
登録日時: 14年前

Re: 配列型に対する&演算子について

#3

投稿記事 by sys » 14年前

(本文中とcode内の変数名の統一がされてなかったので訂正)
ISLe さんが書きました:typeidのオペランドは、sizeof演算子と同じく、評価対象式ではないのでポインタに型変換されません。
→JIS X3014:2003(ISO/IEC 14882:2003) 3.2 単一定義規則 2
  • JIS X3014
    ・式は、次のいずれかの場合を除いて、評価対象式という。
     ---式が、typeid演算子の演算対象となっており、多相クラス型の左辺値を表していない。
  • JIS X3010 6.5.3.4 sizeof演算子
    オペランドの型が可変長配列型である場合、オペランドを評価する。そうでない場合、オペランドは評価せず、その結果は、整数定数とする。

コード:

char array[2][3] = { "ai", "ue" };
int x = 1, y = 0;
y = sizeof(++x);
printf("x = %d  y = %d\n", x, y);
printf("array → 型:%s サイズ:%zd\n", typeid(array).name(), sizeof(array));
printf("&*array → 型:%s サイズ:%zd\n", typeid(&*array).name(), sizeof(&*array));
printf("&array → 型:%s サイズ:%zd\n", typeid(&array).name(), sizeof(&array));
printf("array [%zd] *two_array [%zd] **array [%zd]\n", sizeof(array), sizeof(*array), sizeof(**array));
[実行結果] (環境:Xcode 3.2.5 64bit)
x = 1 y = 4
array → 型:A2_A3_c サイズ:6
&*array → 型:PA3_c サイズ:8
&array → 型:PA2_A3_c サイズ:8
array [6] *array [3] **array [1]

となります。
x の値が変わってないことから前置加算演算子は評価されてないのが確認出来ます。
しかし、*演算子、&演算子によって結果が変わっていることから、*演算子と&演算子は評価されているように読めます。

sizeofの定義に出てくる「オペランド」とは全ての演算子を指していないということなのでしょうか?
typeidも結果が変わっているので *演算子、&演算子は評価されているのではないでしょうか?
最後に編集したユーザー sys on 2011年5月26日(木) 02:52 [ 編集 1 回目 ]

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 配列型に対する&演算子について

#4

投稿記事 by bitter_fox » 14年前

sys さんが書きました:
ISLe さんが書きました:typeidのオペランドは、sizeof演算子と同じく、評価対象式ではないのでポインタに型変換されません。
→JIS X3014:2003(ISO/IEC 14882:2003) 3.2 単一定義規則 2
  • JIS X3014
    ・式は、次のいずれかの場合を除いて、評価対象式という。
     ---式が、typeid演算子の演算対象となっており、多相クラス型の左辺値を表していない。
  • JIS X3010 6.5.3.4 sizeof演算子
    オペランドの型が可変長配列型である場合、オペランドを評価する。そうでない場合、オペランドは評価せず、その結果は、整数定数とする。
sizeofの定義に出てくる「オペランド」とは全ての演算子(オペランド)を指していないということなのでしょうか?
厳密な規格は存じ上げないので議論に入れないのですが、気になった点があるので・・・

演算子(オペランド)とありますが、
演算子(+-*/etc...)はオペレータで、x + y - zのx, y, zなどをオペランドと呼ぶのですがどちらを指しているのでしょうか?

sys
記事: 27
登録日時: 14年前

Re: 配列型に対する&演算子について

#5

投稿記事 by sys » 14年前

bitter_fox さんが書きました: sizeofの定義に出てくる「オペランド」とは全ての演算子(オペランド)を指していないということなのでしょうか?
演算子(オペランド)とありますが、
演算子(+-*/etc...)はオペレータで、x + y - zのx, y, zなどをオペランドと呼ぶのですがどちらを指しているのでしょうか?
あっ、、、失礼しました。大きな間違いです。
オペランド(operand) ... 演算項目、演算対象、演算の対象となる値や変数のこと
オペレータ(operator) ... 演算子
ですね。...恥ずかしい間違いです。
  • ※原文の(オペランド)は削除しました。
sizeof演算子のオペランドは評価せず、はどういう意味なのでしょうかね、、、。
ますますわからなくなりました。
最後に編集したユーザー sys on 2011年5月26日(木) 04:04 [ 編集 1 回目 ]

かずま

Re: 配列型に対する&演算子について

#6

投稿記事 by かずま » 14年前

sys さんが書きました: char array[2][3] = { "ab", "cd" }; と宣言したとき、
「array」 の返す値は、配列の先頭要素のアドレスを返している。型は、char[2][3]
「&*array」 の返す値は、配列の先頭要素のアドレスを返している。型は、char(*)[3]
※型は typeid(array).name()、 typeid(&*array).name で確認。
違いますね。

array は関数ではありませんから「返す」という用語は不適切です。
array も &*array も式であり、その式を「評価する」と値や型が得られます。
「評価する」は「計算する」とほぼ同じです。
演算子の評価は、関数と似たようなものですから、「返す」といってもいいでしょう。

何も演算子がついていない array の評価結果は、その配列の先頭要素へのポインタです。
値は、array[0] (すなわち "ab") のアドレス。
型は、array[0] へのポインタで、char (*)[3]

&*array を評価する場合、まず、array は上記の評価結果となる。
*array の評価結果は、値が array[0] すなわち "ab"。型は char [3]。
&*array の評価結果は、値が array[0] のアドレス。
型は、array[0] へのポインタで、char (*)[3]

したがって、array と &*array の評価結果は同じです。

typeid(array) を評価すると、array はポインタに変換されず、配列のままなので
typeid 演算子が返した typeinfo オブジェクト(正確には const typeinfo &) の中に
array の型 char [2][3] という情報が格納されています。

typeid(&*array) を評価すると、&*array がポインタですから、
typeid 演算子が返した typeinfo オブジェクトの中に
&*array の型 char (*)[3] という情報が格納されています。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: 配列型に対する&演算子について

#7

投稿記事 by ISLe » 14年前

オペランド式は評価されませんが、オペランド式の型にもとづいた結果が返るわけなので、それを求めるために型変換は行われます。
ただしいくつかの例外があって、配列名が単独で現れたときのポインタへの変換などは行われません。

(追記)
型変換(だけ)が行われるというのはおかしな表現ですね。
型変換の規則にもとづいてオペランド式の型が求められる、というのが正しいですかね。
最後に編集したユーザー ISLe on 2011年5月26日(木) 02:00 [ 編集 2 回目 ]

かずま

Re: 配列型に対する&演算子について

#8

投稿記事 by かずま » 14年前

sys さんが書きました: sizeof演算子のオペランドは評価せず、はどういう意味なのでしょうかね、、、。
計算しない、実行しない、という意味です。

int i = 3; で sizeof(i + 5) の場合、足し算をして 8 を求めることはない、
int と int の足し算なので評価の結果の型は分かっているから、sizeof(int) を
返します。

sizeof(printf("abc")) の場合、printf を呼び出して標準出力に abc のを送り込み
結果の文字数 3 を返すことはしない。printf が int を返すことが分かっているので
sizeof(int) と同じということです。

sys
記事: 27
登録日時: 14年前

Re: 配列型に対する&演算子について

#9

投稿記事 by sys » 14年前

かずまさん、ISLeさん宛

なるほど。理解出来たかもしれません。
確認になってしまいますが、
  • (1) array 自体の型は「char[2][3]」
    (2) array が式として書かれた場合評価され、その型は「char(*)[3]」となる。
    (3) typeid(array).name は式として評価されない為、「char[2][3]」
ここまでの解釈はあっていますか?

かずまさんへの質問です。
かずま さんが書きました: &*array を評価する場合、まず、array は上記の評価結果となる。
*array の評価結果は、値が array[0] すなわち "ab"。型は char [3]。
&*array の評価結果は、値が array[0] のアドレス。
型は、array[0] へのポインタで、char (*)[3]
これだと、*演算子と&演算子は評価してしまっているので、JIS X3010 6.5.3.2 の定義に反していませんか?
ただ単に説明の問題になってやぶさかですが、、、
&*array は、「*演算子も&演算子も評価せず、両演算子とも取り除いた場合と同じ結果になる。」の定義から両演算子は評価せず array と同じ型と値になる。という説明はどうでしょうか?

ISLeさんへの質問です。
ISLe さんが書きました: オペランド式は評価されませんが、オペランド式の型にもとづいた結果が返るわけなので、それを求めるために型変換は行われます。
ただしいくつかの例外があって、配列名が単独で現れたときのポインタへの変換などは行われません。
(追記)
型変換(だけ)が行われるというのはおかしな表現ですね。
型変換の規則にもとづいてオペランド式の型が求められる、というのが正しいですかね。
(1)typeid(&array).name は式は評価されないけど、型を求める為の計算(型を求める目的の為だけに評価する)がされているので「char(*)[2][3]」になる、ということでしょうか?
(2)sizeof(&array) がポインタのサイズを返すのはなぜなのでしょうか?
これもtypeidと一緒で式は評価されないけど、サイズを求める為の計算(サイズを求める目的の為だけに式を評価)がされている、ということでしょうか?

かずま

Re: 配列型に対する&演算子について

#10

投稿記事 by かずま » 14年前

sys さんが書きました: (3) typeid(array).name は式として評価されない為、「char[2][3]」
ここまでの解釈はあっていますか?
typeid(array).name という式が評価されないのではありません。
この式の中の array が typeid のオペランドですから、評価されないのです。
sys さんが書きました: これだと、*演算子と&演算子は評価してしまっているので、JIS X3010 6.5.3.2 の定義に反していませんか?
ただ単に説明の問題になってやぶさかですが、、、
&*array は、「*演算子も&演算子も評価せず、両演算子とも取り除いた場合と同じ結果になる。」の定義から両演算子は評価せず array と同じ型と値になる。という説明はどうでしょうか?
その説明でよいでしょう。
sys さんが書きました: (1)typeid(&array).name は式は評価されないけど、型を求める為の計算(型を求める目的の為だけに評価する)がされているので「char(*)[2][3]」になる、ということでしょうか?
array は & のオペランドなので、ポインタに変換されない。
& は、変換されなかった元の配列 char [2][3] へのポインタを返すから、
&array の型は char (*)[2][3]。
typeid(&array) は、その型の情報を内部にもつ typeinfo オブジェクトへの参照を
返す。
sys さんが書きました: (2)sizeof(&array) がポインタのサイズを返すのはなぜなのでしょうか?
&array がポインタだからです。
array は、ポインタに変換されず配列のままなので、&演算子は、配列の先頭要素へ
のポインタでななく、その配列全体へのポインタを返します。
sizeof はそのポインタのサイズを返します。
sys さんが書きました: これもtypeidと一緒で式は評価されないけど、サイズを求める為の計算(サイズを求める目的の為だけに式を評価)がされている、ということでしょうか?
式の最終結果の型だけを、コンパイル時に求めて、そのサイズを返しているという
ことです。

sys
記事: 27
登録日時: 14年前

Re: 配列型に対する&演算子について

#11

投稿記事 by sys » 14年前

かずまさん宛

typeid().name() について、よくわかりました。
私の理解が乏しすぎました…。
丁寧な解説、ありがとうございます。

sizeof(&array) についてわからない部分があります。
  • JIS X3010 6.5.3.4 sizeof演算子
    オペランドの型が可変長配列型である場合、オペランドを評価する。そうでない場合、オペランドは評価せず、その結果は、整数定数とする。
と定義。

sizeof(&array)を考えると、
(1)式を評価しない「array」の型は char[2][3]である。
(2)式を評価しないということは「&array」も評価されない。
(3)式を評価しない「array」の型はchar[2][3]、しかしサイズはポインタに… 
かずま さんが書きました: array は、ポインタに変換されず配列のままなので、&演算子は、配列の先頭要素へ
のポインタでななく、その配列全体へのポインタを返します。
これだと「&array」は評価されていることになりませんか?
かずま さんが書きました:
sys さんが書きました: sizeof演算子のオペランドは評価せず、はどういう意味なのでしょうかね、、、。
計算しない、実行しない、という意味です。
かずま さんが書きました: &演算子は、配列の先頭要素へ
のポインタでななく、その配列全体へのポインタを返します。
上記の「配列全体へのポインタを返します。」は評価しない(計算しない、実行しない)と矛盾になりませんか?

[追記]
この「式を評価しない」について定義はあるのかを調べてみました。
  • JIS X 3010 3.1 アクセス(access)
    <実行時の動作>オブジェクトの値を読み取る、又は変更すること。
    参考1)これらの二つの動作のうちのいずれか一方だけを意味する場合は、"読み取る"又は"変更する"という用語を使う。
    2)"変更する"は、格納する新しい値が、格納前の値と同じである場合も含む。
    3) 評価されない式は、オブジェクトをアクセスしない。
唯一私が見つけることが出来たのがこの定義だけなのですが、この(3)の
  • 評価されない式は、オブジェクトをアクセス(オブジェクトの値を読み取る、又は変更する)しない。
の定義から、加減演算子などと違って、&演算子はオブジェクトの値自体は読み取るや、変更はしてないので式の評価には当たらない。と考えました。

ただ、ここに出てくる「オブジェクトの値」が何を示しているのかにもよるのですが、、、。
新ANSI C言語辞典によれば「値」は、
  • 「オブジェクト(変数など)に記憶されているデータや式の評価結果」
とのことなので、「オブジェクトの値」の「値」が新ANSI C言語辞典と同じ意味を示しているのであれば、
「array」の値は「配列の先頭要素のアドレス」ということで、&演算子はアドレス自体を読み取るや変更はしてないので&演算子が適用になる。
というのはどうでしょうか?

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: 配列型に対する&演算子について

#12

投稿記事 by ISLe » 14年前

sys さんが書きました:
  • (1) array 自体の型は「char[2][3]」
    (2) array が式として書かれた場合評価され、その型は「char(*)[3]」となる。
    (3) typeid(array).name は式として評価されない為、「char[2][3]」
ここまでの解釈はあっていますか?
arrayがどのように評価されるかは、arrayがどこに記述されているかで異なるので(2)は誤りです。
特にsizeofやtypeidのオペランドに演算子をともなわない配列名が現れたときは例外扱いになりますから。
式「が」評価されない、で、式「として」評価するという表現はおかしい気がします。
sys さんが書きました:&*array は、「*演算子も&演算子も評価せず、両演算子とも取り除いた場合と同じ結果になる。」の定義から両演算子は評価せず array と同じ型と値になる。という説明はどうでしょうか?
「両演算子とも取り除いた場合と同じ結果になる」というのは、評価した結果得られた値を比較したとき等しくなければならない、ということです。
翻訳(コンパイル)する前に、&*を取り除いて考えるということではないですよ。
sys さんが書きました:(1)typeid(&array).name は式は評価されないけど、型を求める為の計算(型を求める目的の為だけに評価する)がされているので「char(*)[2][3]」になる、ということでしょうか?
(2)sizeof(&array) がポインタのサイズを返すのはなぜなのでしょうか?
これもtypeidと一緒で式は評価されないけど、サイズを求める為の計算(サイズを求める目的の為だけに式を評価)がされている、ということでしょうか?
例えばアドレス演算子の場合、JIS X3010:2003には「単項&演算子は、そのオペランドのアドレスを返す。オペランドが型“~型”をもっている場合、結果は、型“~型へのポインタ”をもつ。」とあります。
評価した結果がどんな型になるかきちんと定義されています。
評価した結果がどんな型になるかきちんと定義されているので、評価しなくても結果がどんな型になるか分かります。
定義によれば評価した結果の型はオペランドの型によってのみ決定します。
typeidもsizeofも型が分かれば結果を得ることができます(不完全型のオブジェクトを除く)。

sys
記事: 27
登録日時: 14年前

Re: 配列型に対する&演算子について

#13

投稿記事 by sys » 14年前

ISLe さんが書きました: arrayがどのように評価されるかは、arrayがどこに記述されているかで異なるので(2)は誤りです。
特にsizeofやtypeidのオペランドに演算子をともなわない配列名が現れたときは例外扱いになりますから。
基本的なことを見落としてました…。何回も話に出していた 6.3.2.1 の3つの例外をのぞく配列型の暗黙の型変換ですね…。
式が評価されない、される、そもそも式ってなんだ?アクセス?などなどいろいろ考えていたらこんがらがってしまいました。
ISLe さんが書きました: 評価した結果がどんな型になるかきちんと定義されています。
評価した結果がどんな型になるかきちんと定義されているので、評価しなくても結果がどんな型になるか分かります。
なるほど、わかりました。
上記から考えて、やっとsizeof(&array)の疑問が理解出来ました。
ありがとうございます。
しかし、「式が評価されない、と、式として評価する」で大きな違いになるように
もはや私の読解力(国語力?)不足を感じます、、、。

コメントしてくださった皆さん、ありがとうございました。
No11でのかずまさんへの質問も、上記から考えて解決できるので、
ひとまずは解決にチェックさせて頂きます。
もしもご指摘、補足等ありましたら、コメント頂けると幸いです。


少々話が逸れるのですが、定義の「式の評価」の解釈を探しているときに
C MAGAZINE の連載(?)を載せているサイトが見つかり、
「不定」と「未定義」の違いは?とか、私的にすごく面白い内容が載っていました。

現在はC MAGAZINE は休刊とのことなのですが、こういったC言語の定義や解釈、雑学(?)について記載された書籍や雑誌で、お薦めってありますか?
今回で実感したのですが、やはりJISやISOだけで定義を理解するのは限界を感じました。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: 配列型に対する&演算子について

#14

投稿記事 by ISLe » 14年前

sys さんが書きました:現在はC MAGAZINE は休刊とのことなのですが、こういったC言語の定義や解釈、雑学(?)について記載された書籍や雑誌で、お薦めってありますか?
今回で実感したのですが、やはりJISやISOだけで定義を理解するのは限界を感じました。
Cマガ以降、そういった内容を扱う雑誌は無くなってしまいましたね。
書籍や雑誌は知らないのですが、そのサイト(フィンローダ氏の初級C言語Q&Aですよね)のインデックスページにcomp.lang.c FAQのリンクがあります。
"C FAQ"とか"C++ FAQ"で検索するとその手のサイトがヒットしますよ。

sys
記事: 27
登録日時: 14年前

Re: 配列型に対する&演算子について

#15

投稿記事 by sys » 14年前

ISLe さんが書きました: 書籍や雑誌は知らないのですが、そのサイト(フィンローダ氏の初級C言語Q&Aですよね)のインデックスページにcomp.lang.c FAQのリンクがあります。
"C FAQ"とか"C++ FAQ"で検索するとその手のサイトがヒットしますよ。
はい、そのサイトです。他にも疑問に思っていたことも載っていて感動しました。

やっぱり内容的に書籍での需要はなそうですよね…。検索で地道に情報収集したいと思います。
ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る