質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

structure

このタグは、プログラム言語におけるデータ型structure(構造体)に関するタグです。

Q&A

解決済

3回答

1743閲覧

構造体型・共用体のポインタ型を利用したエイリアシングの実現法<C言語>

cadet_study

総合スコア13

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

structure

このタグは、プログラム言語におけるデータ型structure(構造体)に関するタグです。

2グッド

3クリップ

投稿2018/08/28 13:54

厳密な別名規則(strict aliasing rules)で、構造体・共用体をもちいてエイリアシングを実現する方法とは

C言語を独学で勉強しているものです。今回初めて質問させていただきます。
ポインタについて調べていると、strict aliasing rulesというものを発見し、オブジェクトのエイリアス(別名)になるための規則があることを初めて知りました。
その規則は、以下のものであることを学びました。(C99)

オブジェクトに格納された値に対するアクセスは,次のうちのいずれか1つの型を持つ左辺値によらなければならない。

  1. オブジェクトの有効型と適合する型
  2. オブジェクトの有効型と適合する型の修飾版
  3. オブジェクトの有効型に対応する符号付き型または符号無し型
  4. オブジェクトの有効型の修飾版に対応する符号付き型または符号無し型

メンバの中に上に列挙した型の1つを含む集成体型または共用体型(再帰的に包含されている部分集成体または含まれる共用体のメンバを含む)
0. 文字型

発生している問題・エラーメッセージ

このうち、構造体・共用体の規則以外は理解できたのですが、4つ目の

"メンバの中に上に列挙した型の1つを含む集成体型または共用体型(再帰的に包含されている部分集成体または含まれる共用体のメンバを含む)"

がどのような意味を持つのか、そしてどのような形でそのケースのエイリアシングが実現されるのかが分からないです。

該当のソースコード

(ポインタのアライメントの未定義については今回は考慮せず、エイリアシングのみを焦点にしています。)

例えばコードが、

#include<stdio.h> #include<stdlib.h> typedef struct s { double a; float b; int c; short d; }st_alias; typedef union t { double e; float f; int g; short h; }uni_alias; int main(void){ st_alias * ali_s; uni_alias * ali_u; int test_1 = 10; short test_2 = 22; double test_3 = 123.4; float test_4 = 222.2f; system("pause"); return 0; }

のような状態であるとき、"ali_s"と"ali_u"のポインタは、変換におけるアラインメントが正しければ上記の規則によって、main関数内の4つのオブジェクトのエイリアスになることができ、ポインタを経由してアクセス(変更・読み取り)ができるということなのでしょうか?そして、それはメンバアクセス演算子(-> .)によって実現するのでしょうか?

ご教授して下さるととてもありがたいです。どうか宜しくお願いします。

試したこと

visual studio 2017 C++ でポインタにオブジェクトのアドレスを代入して、それを経由したアクセスをしたのですが、期待した結果ではありませんでした。(アラインメントによるアドレスの変化はなかったことは確認済みで、それが原因ではないと思っています。)
また、visual studio C++ が strict aliasing rules をサポートしているのかについて調査をしてみたのですが、そのようなものは見つかりませんでした。
もしかしたらそもそもサポートしていないのではないのかと思いました。
(inline関数の外部結合の定義形式も、C99のものではなくC++の形式をサポートしている状況もあったため、そのようなこともあるのではないのかと…)

補足情報(FW/ツールのバージョンなど)

visual studio C++ 2017を利用しています。(.cの形式で使用しています。)
また、strict aliasing rulesの日本語形式のものは、
https://www.jpcert.or.jp/sc-rules/c-exp39-c.htmlから引用させていただきました。

yumetodo, yohhoy👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

strict aliasing rulesというものを発見し、オブジェクトのエイリアス(別名)になるための規則があることを初めて知りました。

C言語仕様の"Strict Aliasing Rules"は、C言語プログラマのための規則ではなく、C言語コンパイラ製作者のための規則という側面が強いものです。

"Strict Aliasing Rules"の本題は、Cコンパイラは「変数型が異なる場合は、互いにエイリアス(別名)となっている可能性を排除して良く」、「エイリアス不在を前提とした生成コード最適化をして良い」というものです。"Strict(厳密な)"とは、コンパイラ視点からは「型に互換性がある場合は、互いにエイリアス(別名)の可能性を考慮すべき」、プログラマ視点からは「互換性のない型でエイリアスを作ってはいけない」というニュアンスから来ています。

オブジェクトに格納された値に対するアクセスは,次のうちのいずれか1つの型を持つ左辺値によらなければならない。
(略)

上記5つのルールは、"Strict Aliasing Rules"の例外事項を規定しています。例えば5番目「文字型(a character type)」は、基本ルールに従うとint型とchar型は互換性のない型なので互いにエイリアスであってはいけないが、例外規定により「エイリアスになり得ると考慮すべき(コンパイラ視点)」「エイリアスを作っても良い(プログラマ視点)」という意味です。


本題に入る前に、"Strict Aliasing Rules"の基本ルールを理解する必要があります。

c

1void f(int* a, double *b) { 2 // int型とdouble型は異なる型のため、int型(*a)への代入は... 3 *a = 0; 4 // double型(*b)変数のエイリアスとなっている可能性を考慮する必要がない。 5 *b = 3.14; 6 // つまり下記処理で改めてメモリ位置*aから値を読み込む必要はなく、 7 // 最適化によって "前掲処理で代入した直値0" をそのまま使って良い。 8 printf("%d", *a); 9 // コンパイラは↑をprintf("%d", 0);やprintf("0");に最適化しても良い。 10}

このうち、構造体・共用体の規則以外は理解できたのですが、4つ目の

"メンバの中に上に列挙した型の1つを含む集成体型または共用体型(再帰的に包含されている部分集成体または含まれる共用体のメンバを含む)"

がどのような意味を持つのか、そしてどのような形でそのケースのエイリアシングが実現されるのかが分からないです。

この例外規則の解釈は、下記の通りです。プログラマ視点では当然のことを言っているように見えるかもしれまん。

c

1// int型を含む集成体(struct)型は... 2struct S { 3 double b; 4 int i; 5}; 6 7void g(struct S* s, int* a) { 8 // int型と互換性のある型(=int型自身を含む)のエイリアスになっている可能性があるため... 9 *a = 0; 10 // 下記の処理によりメモリ位置*aが書き換わる可能性がある 11 struct S t = { 3.14, 42 }; 12 *s = t; 13 // つまり下記処理では改めてメモリ位置*aから値を読み込まなければならない。 14 printf("%d", *a); 15}

visual studio C++ が strict aliasing rules をサポートしているのかについて調査をしてみたのですが、そのようなものは見つかりませんでした。
もしかしたらそもそもサポートしていないのではないのかと思いました。

コンパイラが"Strict Aliasing Rules"をサポートするとは、前掲のように「型情報に基づいた積極的コード生成最適化を行う」という意味です。最新のVisualC++事情までは把握していませんが、私が知る限りはVisual C++はこのような積極的な最適化を行いません。なお、GCCやClangは積極的な最適化をサポートします。

善意的に解釈すればプログラマに優しく、悪意を持って解釈すれば最適化性能が劣るコンパイラということになります。しかし、"Strict Aliasing Rules"を厳密に守っているプログラムはほとんど存在せず(たぶん)、積極的な最適化はしばしばトラブルの元になります。C言語仕様で規定する以上「Cコンパイラが正しくプログラムが誤っている」のですが、現実的には"Strict Aliasing Rules"に基づいた最適化を無効化して運用されることが多いようです。GCC、Clangでは-fno-strict-aliasingオプションが用いられます。

投稿2018/08/28 16:23

編集2018/08/29 02:54
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yohhoy

2018/08/28 16:35

質問中のali_s, ali_u付近コードは誤記or記述途中でしょうか?そうでなければ、yumetodoさん指摘の通りです...
cadet_study

2018/08/29 01:27

おはようございます。回答して下さりありがとうございます。 ということは、オブジェクトが構造体・共用体のメンバとして構成されている場合、そのオブジェクトのポインタを利用することでエイリアシングが考慮されるだけはでなく、その構造体・共用体のポインタでアクセスしたとしてもエイリアシングが考慮されるよ,という意味なのでしょうか?(その解釈がもし正しいならali_s, ali_uの記述は自分の質問する前の時点での解釈間違えです。)
yohhoy

2018/08/29 02:23 編集

念のため確認しておくと、Aliasing Ruleは 左辺値(lvalue)=あるメモリ位置 に対するアクセス(読み/書き)について述べています。C言語の場合、あるメモリ位置を指す方法は「変数名そのもの」「ポインタ型の値に対する参照はがし操作(*p)の結果」が該当します。 > オブジェクトが構造体・共用体のメンバとして構成されている場合、そのオブジェクトのポインタを利用することでエイリアシングが考慮されるだけはでなく、その構造体・共用体のポインタでアクセスしたとしてもエイリアシングが考慮される こちらは質問意図を正確に汲み取れませんでした。"そのオブジェクトのポインタ"が指す先(A)と、"その構造体・共用体のポインタ"が指す先(B)が、互いにエイリアスか否かという点が問題になります。経路が(A)/(B)経由なのかという区別は、問題の本質ではありません。 回答中にあげた関数g()の例でいえば、"Strict Aliasing Rule"に従って *s (struct S) と *a (int) は互いにエイリアスとなる可能性があります。つまり *s を書き換えると(*s = t) *a が書き換わっている可能性があり、また *a を書き換えると *s の内容 が書き換わっている可能性があるという意味です。
cadet_study

2018/08/29 13:40

つまり、最適化を行う際、そのオブジェクトと互換性を持つ型や、構造体・共用体のメンバとして互換性のある型を含んでいる場合、それらのポインタなどを利用したアクセスは、もしかしたら同じメモリ位置に対するものなのかもしれない,ということをコンパイラが仮定し、その部分の最適化を抑制しなければならず、逆にそうでない場合はそのような仮定を踏まえずに自由に最適化を行い、規則を守っていない場合の結果はその最適化によっておかしくなる可能性がある,ということですか?
yohhoy

2018/08/29 13:50

はい。まさに記載の通りです。
cadet_study

2018/08/29 13:55

なるほど…だからvisual studioではそのような最適化をあまり行わないため規則を破ったとしても適切な動作っぽくなったのですか…
cadet_study

2018/08/29 14:02

もしよろしければなんですけど、私が引用したURLに違反コードの例があり、その中で、 2つ目の " ip = &t.i; return *ip;" の別名規則に違反する理由と、 3つ目の "return ((union a_union *) &d)->i;" が未定義動作となる理由が何故だかよく分かりません。 その理由についてのご教授をしてくださるとありがたいです。
yohhoy

2018/08/30 03:37 編集

Aliasing Rulesとunionを介したtype-punningは、(似通っていますが)別者と捉えたほうが良いかもしれません。前者は回答中の関数g()のように、「型情報を用いてどんな最適化を行えるか」を規定するものであり、後者は「意図的な型情報の読み替え」の規定です。 C99以前ではunionによるtype-punning自体があいまい(処理系定義)だったため、 http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm C99 TC3仕様にて改訂された経緯もあります。このとき、union型オブジェクトに含まれるメンバ(named member)の注釈として、type-punning動作が明記されました。 コメントで挙げられたJPCERTのNG例は、その翻訳元CERT https://wiki.sei.cmu.edu/confluence/display/c/EXP39-C.+Do+not+access+a+variable+through+a+pointer+of+an+incompatible+type では削除されているようですね。このあたりの言語仕様はC17現在も解釈が微妙な部分が多いようで、100%の断言が難しい気がしています...
cadet_study

2018/08/30 09:01

そうなんです,翻訳元の方にはないんですよね…  2つ目の違反コードは、関数 f() 内に int を含む "a_union t;" と、int へのポインタである "int *ip;"があり、それらを用いた左辺値によるアクセスに伴う最適化は、同じメモリ位置を指している可能性があるため、その点においては抑制されるのではないのかと考えました。 3つ目の違反コードは、構造体・共用体のメンバアクセスはそのメンバのオフセットをもと行う処理系が多い,ということをyumetodoさんからご教授していただいたため、結果として &d は全く関係のないメモリにアクセスすることになり、aliasing rules 以前に、そのアクセス自体が未定義につながるのではないのかと思いました。(オーバーランですか?)
cadet_study

2018/08/30 09:09

2つ目は type punning 関係ですかね… union における type punning は union 型を "直接" 介して行われるということを他のサイトで知りました。 もしかすると、最後に読み込んだデータが double型のため、x.y などの直接にアクセスする場合は type punnig が適用され、未定義にはならないが、x -> y や *i などの間接参照の場合は、たとえ最後に読み込んだデータと今読み込もうとしたデータが異なるとしても、type punning は適用されないため、その場合は定義されない,つまり未定義になるのではないのかとも思いました…
cadet_study

2018/08/30 09:15

"may" なので絶対ではない感じなのですが… 解釈が間違えていましたらすみません。
yohhoy

2018/08/30 09:47 編集

type-punningへの言及は、脚注(footnote)記載であり標準の一部ではない(non-normative)という扱いですから、"may"という表現はある意味で正確かもしれません。 > 3つ目の違反コードは、構造体・共用体のメンバアクセスはそのメンバのオフセットをもと行う処理系が多い,~ 結果として &d は全く関係のないメモリにアクセスすることになり~(オーバーランですか?) これは解釈が妥当でない気がします。少なくとも共用体のメンバは、いずれもオフセット0です(だからこそメモリ領域を"共用"できる)。またunion { int i; double d; }のsizeof適用結果は、sizeof(double)と同値と強く仮定できますから、メモリオーバーラン的な問題は生じないと思います。
cadet_study

2018/08/30 15:45

そうですよね… 改めてその違反コードを確認してみたのですが、 "return ((union a_union *) &d)->i;" は double 型として割り当てられているはずのメモリ域を、あたかも共用体として割り当てられているようにアクセスしている状況になっていると思いました。(それ自体が怪しい気がします…) 加えて、仮に共用体としてdouble として割り当てられたメモリ域の使用が許可されたとしても、最後に書かれたメモリの状態は double で、かつ union 型を直接経由しているわけではないので、メンバの読み取りにおける type punning は適用されない可能性があり、適用されない場合,それを int としてアクセスするため、違う型のメモリ域にアクセスしたことで aliasing rules に反するので、もしかするとその処理がとばされる,無視されるなどの期待しない動作が起こる,という意味なのだろうか、と新たに考えました。(return なので無視はできないと思うのですが…)
cadet_study

2018/08/31 08:00

質問が多くて本当にすみません。
guest

0

こんにちは。

strict aliasing rules を知らなかったので、ググって見たらyohhoyさんの記事が出て来ました。
(翻訳)C/C++のStrict Aliasingを理解する または - どうして#$@##@^%コンパイラは僕がしたい事をさせてくれないの!
分かりやすいです。

要するに「変なことしたらコンパイラが最適化してしまって、コードをまるっと削除するかも知れないよ」ってことみたいです。(もっと他のことも起こるかも知れません。鼻から悪魔系です。)
「オブジェクトに格納された値に対するアクセスは,次のうちのいずれか1つの型を持つ左辺値によらなければならない。」は、このルールに従えば、その「変なこと」に該当しないということですね。

yohhoyさんの記事の「壊れたバージョン」のプログラムのuint16_t *ptr=(uint16_t*)&acopy;*ptracopyと互換性がないため、ptracopyの一部をポイントしていないとコンパイラは仮定してよいわけです。(どうみてもポイントしてますが、ルール上それが許されることになります。)
ということは、ptrが指す先はどこかあらぬメモリとして解釈できるわけです。あらぬメモリへの操作は最適化によりまるっと削除しても「正しいプログラム」の動作には影響しないと判断して良いということです。だから、コンパイラは削除してもよいので、削除するコンパイラも存在するという流れです。

古いコンパイラでは再現しました。警告もでます。しかし、これより新しいものではことごとく再現せず、警告もでませんでした。削除しないコンパイラも少なくないということですね。

構造体については、構造体の中にある、int型等をint型ポインタは指すことができるので、その構造体とint型ポインタの指す先がエイリアス(同じ領域)かも知れないとコンパイラは仮定しなければならないということでしょう。
逆に云うと、short型を一切含まない構造体と、short型ポインタはエイリアスにならない(同じ領域ではない)とコンパイラは仮定して最適化して良いということです。

投稿2018/08/28 15:58

編集2018/08/28 16:00
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cadet_study

2018/08/29 01:41

おはようございます。回答して下さりありがとうございます。実は、yohhoyさんのstrict aliasing rulesについての記事は、質問する前に拝見させていただいていました。 ということは、int型・short型などのオブジェクトが構造体・共用体のメンバとして構成されている場合、その該当するオブジェクトへのポインタを用いてアクセスすると、勿論エイリアシングが考慮されるほか、その構造体・共用体のポインタを用いて、メンバ内のオブジェクトにアクセスする場合もエイリアシングが考慮されるよ,という意味ですか?
yohhoy

2018/08/29 02:47 編集

GCC 4.4系で再現/4.5以降で再現しないと思ったら、https://www.gnu.org/software/gcc/gcc-4.5/changes.html "The infrastructure for optimizing based on restrict qualified pointers has been rewritten [...]" コンパイラ内部のポインタエイリアス解析機構が大きく変更された影響のようですね。 該当コードのように"局所解析で本当はエイリアスだと分かるケース”は何とか救えますが、 https://godbolt.org/z/rJps2t のように大局解析が必要なケースでは、Strict Aliasing Rules通りに振舞うようです。(戻り値42を直接返しています。 -O1などでコンパイラの最適化レベルを落とすと、改めてメモリからロードする命令になるが、-fstrict-aliasing最適化は-O2以上で有効になるためです。)
Chironian

2018/08/29 07:50

cadet_studyさん その通りです。yohhoyさんのサンプルから思いついてやってみました。 https://wandbox.org/permlink/qRHFOuOvNXMfsBRo int型へのポインタをshort型へのポインタへキャストすると嵌りそうですね。 (最適化を外すとソースの見た目通りに動きます。)
cadet_study

2018/08/29 13:51

例え同じメモリ位置を指していたとしても、互換性のない型・構造体・共用体のメンバとして含まれていない場合は、規則によってまさか同じメモリ位置を指していないだろうと仮定されてしまい、最適化によってかえって予期しない結果になる可能性があるということですか…
cadet_study

2018/08/29 14:09

よろしければなんですが、私が引用したURLの中に別名規則に関する違反コードの例があり、その中で、 2つ目の、" ip = &t.i; return *ip;" の別名規則に違反する理由と、 3つ目の、"return ((union a_union *) &d)->i;" が未定義動作となる理由がよく分からないです。 それについてのご教授もいただけるとありがたいです。
Chironian

2018/08/29 16:00

2つ目については、http://d.hatena.ne.jp/gununu/20150201/1422769062 のunionに関する解説が分かりやすそうです。要するに union を使って type punning する時にポインタを経由すると危険な時代があったということっぽいです。(ポインタを経由しなければOKだそうです。) 3つ目は良く分からないです。ポインタが指す先のメンバ変数もポインタ経由なので、別名か別名でないのかを決めて最適化する必要があるのでしょう。で、互換性のある型のみ別名になり得ると判断するのが、strict aliasing ruleですね。unionには互換性のない型が含まれる場合もあるので、その時は別名にはなり得ないと判断するという取り決めがあるのではないでしょうか? 正直、難しすぎてついていけないです。 type punning 自体は基本的に処理系依存しますから、標準規格的には常に「未定義」になりそうな印象もあります。だから、コンパイラの仕様を調べて地道にやるしかないのかも知れません。 標準規格上 未定義でもコンパイラの仕様として定めることに何ら問題はないです。type punningはある意味必要な処理なので、きちんと定めているコンパイラも少なくないだろうと思います。
cadet_study

2018/08/30 09:28

gcc は union をポインタで経由することは未定義なのですか…もしかしたらこの2つの違反コードはそのことについて言及していたのかもしれませんね… 2つ目の違反コードについてなのですが、関数 f() 内には int をメンバに含む "a_union t;" があり、int へのポインタである "int *ip;" がある状態で、それらを用いた左辺値によるアクセスに伴う最適化は抑制されるはずだと思い、別の原因があるのではないのかと思いました。 3つ目の違反コードは、aliasing rules 以前の問題のような気がしまして、&d によるメンバへのアクセスは、そのメンバのオフセットを利用する処理系が多い,ということをyumetodoさんからご教授していただき、結果として全く関係のないメモリにアクセスすることが未定義につながるのではないのか,と思いました…
cadet_study

2018/08/30 09:35

union のメンバにおいての type punning は、union 型を "直接" 介して行われるということをあるサイトから学びました。 もしかすると、x.y のようにunion型を経る場合は、type punning が適用されて未定義にはならないが、 x -> y や *i のようにunion型を経ないアクセスの場合、たとえ最後に書き込んだメンバと今読み込もうとしているデータ型が違くても、type punning は適用されないため、その結果は定義されず、未定義になるのでは,とも思いました。
cadet_study

2018/08/30 09:38

"may" という表現なので、違うかもしれません。 間違えていたら、すみません。
Chironian

2018/08/30 12:07

> x.y のようにunion型を経る場合は、type punning が適用されて未定義にはならない 例えば、intやdoubleの内部表現をC言語の標準規格は規定していません。 それらの内部表現はコンパイラが定義します。そのコンパイラの定義がないとtype punningの結果読み出せる値を導くことができません。つまり、各型の内部表現を規定していない以上、標準規格的にはtype punningの振る舞いを定義することはできないです(=未定義)。 従って、type punningする場合は、コンパイラの仕様まできちんと確認しましょうということですね。(至極当然ですね。特にエンディアンや浮動小数点は気にします。これらは標準規格をいくら見ても記載されていません。私の知る限りコンパイラの仕様書には書かれています。) strict aliasing rules は最適化により字面通りでない動作を起こさないことを標準規格的に保証する記述方法を規定しています。その記述方法から外れた時、どのように動作するべきか定義していません(=未定義)。ですので、その時、どのように振る舞うのかはコンパイラの自由です。(当然ですが、字面通りに動作させるという選択を行っても標準規格に準拠します。)そして、type punnngしない限り、strict aliasing rulesから外れた記述にはならないと思います。従って、type punning するなら、strict aliasing rules関連のコンパイラの仕様もちゃんと確認しておいた方がよさそうです。(逆にtype punningしないなら気にする必要なさそうですね。) 結論としては、type puningする時はstrict aliasing rulesから外れた時のコンパイラの仕様の確認も忘れないようにということと思います。
cadet_study

2018/08/30 15:16

すみません,私が調べたところ、type punning は2つの捉え方があるらしく、(他のサイトでも区別しているところを複数見つけました) 1つは、strict aliasing rules つまり元の型を別の型として使用すること(type punning)は基本的にできない… →別のポインタ型へのキャストなど?(gununuさんの記事より) → undefined behavior もう1つは、unionオブジェクトの中身に対するアクセスに使われたメンバが同オブジェクトへの値の格納で直近に使われたメンバと同じでないならば、値のオブジェクト表現の適切な箇所は6.2.6で言及される通りに新しい型のオブジェクト表現として再解釈される。(このプロセスは“type punning”と呼ばれることがある。) →その type punning が行われる条件は、union型を通して読み込まれなければいけないこと  (yohhoyさんの記事より) → unspecified behavior (C++ではこの場合も undefined behavior) となっているらしいです。
cadet_study

2018/08/30 15:26

おそらくなんですけど… 前者のパラグラフでのご教授は、私の二つ目のtype punningの意味の方で、読み取った際に、どのような表現になるかは、直前に書き込まれたデータ表現が異なるためその振る舞いは定義することはできなくて、(規定されていないため) 後者のパラグラフでのご教授は、私の最初のtype punnig の意味の方で、 aliasing rules を破り、キャストなどを行った際に、どうなるかは分からないため(未定義)、コンパイラの仕様を確認した方が良い,という解釈で正しいのでしょうか…
Chironian

2018/08/30 19:51

その通りです!!
cadet_study

2018/08/31 07:59

ありがとうございます! 質問が多くて本当にすみません,最後に一つだけこの質問についてご教授させて下さらないないでしょうか… (gununuさんの記事にあるコードについてです。)
cadet_study

2018/08/31 08:00

下さらないですね,すみません間違えました。
cadet_study

2018/08/31 08:43

u a; //共用体 func2(&a.i, &a.f); //実引数が共用体のメンバのアドレス void func2(int* x, float* y) { *x = 0; *y = 0; ...} のコードについてなのですが、ある関数をコンパイルする際、その関数における最適化はその関数内の情報のみ,つまり仮引数やその関数内で宣言された変数による左辺値の情報のみ(今回だと double,int)で判断して行われるため、実引数のポインタによるアクセスが別名規則を守っていたとしても、unionの場合はその最適化がかえってなんらかの不都合を起こすかもしれない,という意味なのでしょうか?
cadet_study

2018/08/31 08:53

実引数に type punning したオブジェクトのアドレスを指定し、異なる型のポインタに入れた場合のアクセスについても考えたのですが、これは aliasing rules に完全に違反し、未定義になるのでこの場合はプログラム実行時におかしくなるのではと考えたのですが…  (関数原型が呼び出し前にあったとしても、type punned したアドレスのためコンパイラの引数チェックでは通ってしまうので、実行時に初めてその未定義動作が発覚するのではと思いました。)
cadet_study

2018/08/31 09:03

実引数も考慮して最適化するなんてものはないのではと…(それぞれの呼び出しにおける実引数を考慮するならきりがないと思いますし、それをしないためにも仮引数の型の宣言があるのではと思いました。) 間違いでしたらすみません。
guest

0

のような状態であるとき、"ali_s"と"ali_u"のポインタは、変換におけるアラインメントが正しければ上記の規則によって、main関数内の4つのオブジェクトのエイリアスになることができ

アラインメントとか以前に何の関係もない赤の他人です。strict aliasing rules以前の問題。まずunionがなにか調べてください。


その構造体・unionに関してのstrict aliasing rulesってのは、私はC++規格書リーディング初心者なのであってるかわかりませんが、

(翻訳)C/C++のStrict Aliasingを理解する または - どうして#$@##@^%コンパイラは僕がしたい事をさせてくれないの! - yohhoyの日記
アグリゲート(structまたはclass)もしくはunion型は、その内部に含む型のaliasとできます。(例:関数にintへのポインタが渡された場合、内部にintを含むstructやunionへのポインタ、もしくはintを含む別のstructやunionを含む型、もしくは...と際限なく、int*は別のポインタが指すstructやunionに含まれるintを指すことができます。)

を解釈するとおそらく次のようなことを指します。

cpp

1struct Hoge { 2 int fuga; 3}; 4int main() 5{ 6 Hoge hoge; 7 hoge.fuga = 2; 8}

というコードがあったとき、hogefugaのaliasだと思われます。これはよく考えればそうおかしいことではないです。なぜならば、構造体というのはつまるところポインタでアクセスしているからです。メンバーアクセスに使う演算子.は、多くの処理系では右辺の識別名からコンパイル時に構造体型変数のアドレス内でのオフセットを認識し、ポインタ演算でその要素にアクセスするコードを出力します(最適化は考えない)。

投稿2018/08/28 14:41

編集2018/08/28 14:51
yumetodo

総合スコア5852

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yumetodo

2018/08/28 17:37 編集

ええい、やっぱり頓珍漢というか中途半端だったくさいな。そもそもこんなもん意識してプログラム書くことってそうそうないし。 引用した翻訳記事のyohhoyさんご本人が回答されているのでそちらを
cadet_study

2018/08/29 01:53

おはようございます。回答して下さりありがとうございます。 ということは、あるオブジェクトが構造体・共用体のメンバとして構成されている場合、エイリアシングがそのオブジェクトへのポインタを用いた場合に考慮されるだけではなく、オブジェクトを包含している構造体・共用体へのポインタを用いた場合にも考慮される,という意味なのでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問