移植性がなく、実際に動作することが保証されていないトリックの 1 つは、ローカルのアドレスをポインターとして単純に出力することです。
void print_stack_pointer() {
void* p = NULL;
printf("%p", (void*)&p);
}
これにより、基本的に p
のアドレスが出力されます これは、現在のスタック ポインターの適切な近似です
それを行う移植可能な方法はありません。
GNU C では、これは、gcc が「SP」を ESP または RSP の短縮形として認識する x86 を含む、SP という名前のレジスタを持つターゲット ISA で機能する可能性があります。
// broken with clang, but usually works with GCC
register void *sp asm ("sp");
printf("%p", sp);
ローカル レジスタ変数のこの使用法は、GCC で非推奨になりました:
<ブロック引用>この機能でサポートされている唯一の使用法は、拡張 asm を呼び出すときに入力オペランドと出力オペランドのレジスタを指定することです
レジスター変数を定義しても、レジスターは予約されません。 Extended asm 呼び出し時以外は、指定レジスタの内容は保証されません。このため、次の使用は明示的にサポートされていません。 機能しているように見えても、それは単なる偶然です 、周囲のコードの(一見)無関係な変更、または gcc の将来のバージョンの最適化における小さな変更により、意図したとおりに動作しなくなる可能性があります。 ...
また、clang where sp
で実際に壊れています 他の初期化されていない変数と同様に扱われます。
特にGCCを使用したduedl0rの回答に加えて __builtin_frame_address(0)
を使用できます これは GCC 固有のものです (ただし、x86 ではありません)
これは Clang でも動作するはずです (ただし、いくつかのバグがあります)。
(JaredPar が回答したように) ローカルのアドレスを取得することも解決策です。
私の知る限り、C 標準は理論上コール スタックを必要としないことに注意してください。
他のテクニックを夢見ることができます。また、スタックを分割することもできます (少なくとも最近の GCC では)。その場合、スタック ポインターの概念自体があまり意味を持ちません (スタックが連続しておらず、それぞれいくつかの呼び出しフレームの多くのセグメントで構成されている可能性があるため)。 .