アプリ版:「スタンプのみでお礼する」機能のリリースについて

一般に…
int *x = (int*)0xdeadbeef
こんな風にポインタのアドレスを直接指定するコードが散見されますが、C99 によると、整数値からポインタへの変換は処理系定義とのことなので、確実性がないように思います。
https://www.jpcert.or.jp/m/sc-rules/c-int36-c.html
こちらにそのことが詳しく書かれています
(「適切に境界調整されないかもしれないし、被参照型のエントリを指していないかもしれないし、あるいはトラップ表現になるかもしれない。」とのことですが、これの意味はちょっとよく分かってないです)。

そこで、適合コードによると、
volatile uintptr_t iptr = 0xdeadbeef;
int *x = (int*)iptr;
とすれば、「コンパイラが整数値を適切なポインタ値に変換する役に立つかもしれない」とのことです。

質問は2点あります。
① iptr を volatile 宣言する理由が分かりません。不要ではないでしょうか。何か問題が起きますか。
② uintptr_t から int* への変換は規定がない(?)ので、int *x = (int*)(void*)iptr とすべきではないでしょうか。

以上です。
お分かりになる方、ご回答よろしくお願いします。

A 回答 (8件)

アドレス値を即値で指定する時点で, 何をどうしようと「処理系依存」で「確実性がない」のはどうしようもない.

    • good
    • 2
この回答へのお礼

その通りですが、
(int*)iptr は処理系定義の動作であり、
(int*)(void*)iptr は処理系定義の動作でない
ですか?

お礼日時:2020/05/14 17:59

仮想記憶とかあると、アドレス直接指定は無意味になりますが、


絶対アドレスを指定できるなら特に問題は無いかと。
ただ、0xdeadbeef;の様な奇数番地は、int には使えないかもしれません。
アライメントも考えた番地にしましょう。

volatile 宣言は、コンパイラの最適化対策です。
コンパイラのソースコード中で変更していないデータは、
値が変わることが無いので、無視・または飛ばされることがあります。
volatile付けることで、
割り込みでのデーター変化や、外部IOなどからのデータ変更がある、
常に監視しておかなければいけないデータ領域であることを、
コンパイラに教えます。
    • good
    • 0
この回答へのお礼

アライメントの問題、失礼しました。
気をつけます。

volatile int *x
であれば、0xdeadbeef の指す先が外的要因で変化することを知らせていることになると思うのですが、
iptr の方を volatile しているのは、なぜなのでしょうか?

お礼日時:2020/05/14 18:03

>iptr の方を volatile しているのは、なぜなのでしょうか?


int *x = (int*)iptr;
が実行されないかもしれません。
>volatile int *xであれば、
int *x = (int*)iptr;
された後なので、おおもとのデータが変わっても、x のデータは変わりません。
最適化しても問題ないと思います。

ん・・・ここは
コメントはツリーに出来ないのかな?
まいいか。
    • good
    • 0

ん、失礼


違ってるかもしれず。

書いてから・・・あれって
int x = *(int *)iptr;
と勘違いしていた。

ちょっとよくわかりません。
    • good
    • 0

リンク先の適合コードというのがどの程度妥当なのか疑問ですね。


アドレスを即値で指定する時点で、処理系の如何に関わらずデバイス依存性の強いコードなので処理系依存でも構わないでしょう。
    • good
    • 0
この回答へのお礼

そうですね。
ただの学術的興味です。

お礼日時:2020/05/15 10:58

例えば


volatile uintptr_t iptr = 0xdeadbeef;
int *x = (int*)(void*)iptr;
が「処理系定義の動作」かどうかというと, 結論的には「処理系定義」です. 規格上保証されているのは
void * の値を uintptr_t に変換して void * に戻すと元と同じ
であって, 今の例とは異なるのでこの条件には当てはまりません. で, 当てはまる文面を探すと
整数を任意のポインタ型に変換できる (may be conerted)
があって, 結果として (void *) の有無にかかわらずこの場面では「処理系定義」でしかありません.

volatile があるのは #2 で指摘されているように「最適化対策」でしょう (「それは本来の volatile の用途じゃない」と突っ込まれそうだが). volatile がないと
uintptr_t iptr = 0xdeadbeef;
int *x = (int *)0xdeadbeef;
という最適化が可能なので.

あと, この 0xdeadbeef という値はおそらく
アラインメントを「考慮」した (& 英語として「読める」) 値
のはず>#2.

というか, そもそもとして uintptr_t の存在を前提にしていいのか?
    • good
    • 1
この回答へのお礼

理解できた気がしました。
uintptr_t型の整数 0xdeadbeef が、ポインタ値としての 0xdeadbeef 番地であるとは限らない、その点が処理系定義ということですね。
規格上の保証は、
int a;
uintptr iptr = (uintptr_t)(void*)&a;
int *x = (int*)(void*)iptr;
assert(&a == x);
ということですかね。
整数を任意のポインタ型に変換できる、ということは、どう書こうが処理系定義だから、
int *x = (int*)0xdeadbeef
これで構わないということでしょうか。
掲題のページで「コンパイラが整数値を適切なポインタ値に変換する役に立つかもしれない」とあるのは、整数定数 0xdeadbeef の型のサイズが、uintptr_t の型のサイズより小さい場合、うまくいかないかもしれないよ、ということを言いたいような気がしました。

volatile がないときに
uintptr_t iptr = 0xdeadbeef;
int *x = (int *)0xdeadbeef;
という最適化がされるのであれば、x に代入されるアドレスは
uintptr_t iptr = 0xdeadbeef;
int *x = (int *)iptr;
としたときと同じになる場合ではないですか?
であれば、その最適化には問題がないと思います。

アライメントを考慮したのであれば、intが4バイトの環境では、4の倍数になるのでは?
どんな環境の仮定かは分かりませんが、0xdeadbeef = 11 * 257 * 1321517 なので、アライメントを考慮してないように思います。

とりあえず、uintptr_t の存在は仮定します。

お礼日時:2020/05/15 11:30

まず結論を簡潔に書く.



どうせなにをどうやっても規格上動作は保証できないんだから
int *x = (int *)0xdeadbeef;
で十分. 何をどうこねたところで話は変わらない以上, まわりくどく面倒な書き方をする必要はない.

で 1つずついくと
・0xdeadbeef の型のサイズが uintptr_t より小さかったらどうなるか: たぶん 0 拡張されたうえでポインタ型に変換されるだろうけど, もとより「規格では何も決めていない」のだからどうなるかなどわかりようもない. ちなみに sizeof (uintptr_t) と sizeof(void *) の大小関係も決まってない.
・最適化のあたり: volatile の意味は理解できていますか... と書いておくけど, これもはっきりいってここでは無関係だしな~.
・アラインメント: ここは表現の微妙な違いを味わってほしいところ. #2 では
アライメントも考えた
となっているのに対し #6 では
アラインメントを「考慮」した
となっている.

あ, 「適合」のところは
気のせいなので忘れてあげる
べきだと思います>#5.
    • good
    • 0
この回答へのお礼

概ね理解できました。
ありがとうございました。

お礼日時:2020/05/18 10:46

アドレスを直接指定している場合、


コンパイラが勝手に整数値を適切なポインタ値に変換してくれたら
困るんじゃないの?
アラインメントエラーでコアダンプになったほうが数倍ましというか。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!