strict aliasing ruleについて
解決済
回答 3
投稿
- 評価
- クリップ 0
- VIEW 2,148
x64のWindows上でCプログラミングをしている途中に、malloc
で確保してunsigned long
(Windowsでは32ビット)の配列として値を詰め込んだ領域を、unsigned long long
の配列として読み替えたくなる場面が出てきました。そこからいろいろ気になっての質問なのですが、
質問1:strict aliasing ruleについて調べてみたところ、互換性のない型で「書き込んだ」場合に未定義な動作となるというように読めるのですが、unsigned long
で書き込んだものをunsigned long long
として読み取るだけでは未定義の動作とはならない、という解釈で間違いはないでしょうか(もちろん、最後に出る端数についてはunsigned long long
アクセスしない前提です)。
質問2:質問1とは逆に、unsigned long long
の配列として書き込んだ値をunsigned long
の配列として読み書きしたいとします(もちろん、この2つのサイズが違う環境での話です)。スカラー値であれば、共用体を介してアクセスすることでstrict aliasing ruleをクリアできます。ただ、共用体の配列としてアクセスするのはどうにも不便となってしまいます。このような場合に、「コンパイラ独自の記法によってstrict aliasing ruleを使った最適化を抑える」という手段以外に、未定義の動作を避けつつ正しく行うには、「配列丸ごとコピーする」以外の手段はないものでしょうか。
(注:むろん、このような操作が「処理系定義」となるのは承知ですが、今回はそれについては考えなくて大丈夫です。また、符号なし整数について詰め物ビットはなく、よってトラップ表現が発生する可能性もないものとします)
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
質問1:strict aliasing ruleについて調べてみたところ、互換性のない型で「書き込んだ」場合に未定義な動作となるというように読めるのですが、unsigned longで書き込んだものをunsigned long longとして読み取るだけでは未定義の動作とはならない、という解釈で間違いはないでしょうか(もちろん、最後に出る端数についてはunsigned long longアクセスしない前提です)。
誤っています。strict aliasing rule定義に関する「アクセス」とは、読み取り(read)/変更(modify)の両方を意味します。C99(JIS X 3010:2003)での定義を引用します:
3.1. アクセス(access) <実行時の動作>オブジェクトの値を読み取る,又は変更すること。
参考1. これらの二つの動作のうちのいずれか一方だけを意味する場合は,“読み取る”又は“変更する”という用語を使う。
2. “変更する”は,格納する新しい値が,格納前の値と同じである場合も含む。
3. 評価されない式は,オブジェクトをアクセスしない。6.5 式
オブジェクトに格納された値に対するアクセスは,次のうちのいずれか一つの型をもつ左辺値によらなければならない。
- (省略)
質問2:(略)このような場合に、「コンパイラ独自の記法によってstrict aliasing ruleを使った最適化を抑える」という手段以外に、未定義の動作を避けつつ正しく行うには、「配列丸ごとコピーする」以外の手段はないものでしょうか。
必要な要素のみmemcpy
すれば良いと思います。
memcpy
関数でのアクセスはunsigined char
型ポインタとして解釈されるため、strict aliasing ruleによる問題が生じません。C99 TC3(ISO/IEC 9899:TC3)より引用します(JIS X 3010には未反映?):
7.21 String handling
<string.h>
For all functions in this subclause, each character shall be interpreted as if it had the typeunsigned char
(and therefore every possible object representation is valid and has a different value).
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
こんにちは。
実際にやったことはないので、外してたらごめんなさい。
yohhoyさんの日記に解説がありました。
これを見る限り、コンパイラは「非互換な型へのポインタ同士が同じメモリを指すことはない」と仮定して最適化を行ってよいということのようです。
つまり、unsigned long* p0;
とunsigned long long* p1;
は同じメモリを指すことはないという仮定ですね。本来そのようにプログラムしなければいけないのに同じメモリを指していると、最適化の副作用がでるかもしれないが、それはコンパイラのバグではなくプログラマのミスということのようです。
unsigned long* p0 = (unsigned long*)malloc(sizeof(unsigned long)*2);
unsigned long long* p1=(unsigned long long*)p0; // このようなコードを書くべきでない
*p0=1;
*p1=2;
printf("%lu\n", *p0); // 0や2などではなく、1が出力されても文句は言えない
*p1
に書き込むから想定外の結果になりそうですね。書き込まなければどうなのでしょう?
*p0=0;
*(p0+1)=0;
*p0=1;
printf("%llu\n", *p1); // 0や未初期化な値が出力されても文句は言えないかも?
質問2については、最適化を抑制するためにvolatileを付ければよいように感じます。
unsigned long* volatile p0 = (unsigned long*)malloc(sizeof(unsigned long)*2);
unsigned long long* volatile p1=(unsigned long long*)p0;
*p0=1;
*p1=2;
printf("%lu\n", *p0); // 0や2が出力される筈(処理系依存)
う~ん、しかし、これが本当なら怖いですね。今までにもやらかしているような気もします。
C++ならC型キャストやreinterprit_cast<>を使ってないなら問題は出ないはず?
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
メモリ領域に対する操作なら実用上は問題ないように思います。というか、同じようなことは私もこれまで何度となくやっていたような気が……。
unsigned long
のポインタもunsigned long long
のポインタも、同じアドレスを指している限り、同じアドレスへの操作となるはずです。最適化において「ある変数が別のポインタ変数によって書き換えられる可能性を考慮するかどうか」が問題となるのであって、ご質問のように両方ともポインタによる間接アクセスでは該当しないような気がします。そのような手法はテクニックとしてはごく一般的ですし。
と、書いてみたものの、命令スケジューリングで前後して意図した通りにならない可能性は否定できないですね。コンパイラーがどこまで頑張るか(空気読むか)にもよるでしょうけど。
union
を使うのが無難でしょうか。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.37%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/05/28 21:26