「もっとえぐいトリックはないものか?」で次のような関数を出しました。
unsigned _mark;void mark(int arg)
{
_mark = *(unsigned *)(&arg - 1);
}
これは、変数_markに、呼ばれた時点での戻り番地を代入する関数です。環境に依存しますが、多くの場合、戻り番地は第1引数のすぐ下に置かれています。よって、&arg - 1 が戻り番地の格納場所のアドレスです。
これで確かに戻り番地を取れるのですが、具合が悪い点があります。迂闊にも見落としていたのですが、markが戻った直後に、引数をスタックから取りはらう処理が実行されます*1。つまり、変数_markの指しているアドレスに飛ぶと、とりあえずスタックをはらうので、スタックトップがずれてしまう可能性があります。
これは「可能性」で、gccの場合ではスタックのズレが起きないときもあります。アセンブリをざっと眺めた感じでは、gccでは引数を積んだり降ろしたりをpush/popを使わずにmovを使うようで、関数呼び出しの直後にpop相当コードが入るとも限らないのです。(次の関数呼び出しで以前の引数を上書きすることがある。)
コンパイラに影響されにくいようにするには、引数を使わずにローカル変数を基準にしたほうがいいでしょう。
unsigned _mark;void mark()
{
int dummy;
_mark = *(unsigned *)(&dummy + 2); // 2は環境に依存
}
とはいっても、ジョーク以外で、こんなミョウチクリンな関数を使う機会なんてどうせないでしょうが、、、
[追記]ジョークの例 → ラベルとgotoを関数で実現(おバカです)[/追記]